setup instruction added

This commit is contained in:
laxmanhalaki 2025-11-17 16:52:14 +05:30
parent 43b0a493f4
commit 336df2023c
7 changed files with 830 additions and 84 deletions

124
GENERATE_VAPID_KEYS.md Normal file
View File

@ -0,0 +1,124 @@
# VAPID Key Generation Guide
## What are VAPID Keys?
VAPID (Voluntary Application Server Identification) keys are cryptographic keys used for web push notifications. They identify your application server to push services and ensure secure communication.
## Generating VAPID Keys
### Method 1: Using npx (Recommended - No Installation Required)
The easiest way to generate VAPID keys is using `npx`, which doesn't require any global installation:
```bash
npx web-push generate-vapid-keys
```
This command will output something like:
```
=======================================
Public Key:
BEl62iUYgUivxIkvpY5kXK3t3b9i5X8YzA1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6Q7R8S9T0U1V2W3X4Y5Z6
Private Key:
aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890AbCdEfGhIjKlMnOpQrStUvWxYz
=======================================
```
### Method 2: Using Node.js Script
If you prefer to generate keys programmatically, you can create a simple script:
```javascript
// generate-vapid-keys.js
const webpush = require('web-push');
const vapidKeys = webpush.generateVAPIDKeys();
console.log('=======================================');
console.log('Public Key:');
console.log(vapidKeys.publicKey);
console.log('');
console.log('Private Key:');
console.log(vapidKeys.privateKey);
console.log('=======================================');
```
Then run:
```bash
node generate-vapid-keys.js
```
## Configuration
### Backend Configuration
Add the generated keys to your backend `.env` file:
```env
# Notification Service Worker credentials (Web Push / VAPID)
VAPID_PUBLIC_KEY=BEl62iUYgUivxIkvpY5kXK3t3b9i5X8YzA1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6Q7R8S9T0U1V2W3X4Y5Z6
VAPID_PRIVATE_KEY=aBcDeFgHiJkLmNoPqRsTuVwXyZ1234567890AbCdEfGhIjKlMnOpQrStUvWxYz
VAPID_CONTACT=mailto:admin@royalenfield.com
```
**Important Notes:**
- The `VAPID_CONTACT` should be a valid `mailto:` URL
- Keep your `VAPID_PRIVATE_KEY` secure and **never commit it to version control**
- The private key should only be stored on your backend server
### Frontend Configuration
Add the **SAME** `VAPID_PUBLIC_KEY` to your frontend `.env` file:
```env
# Push Notifications (Web Push / VAPID)
VITE_PUBLIC_VAPID_KEY=BEl62iUYgUivxIkvpY5kXK3t3b9i5X8YzA1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6Q7R8S9T0U1V2W3X4Y5Z6
```
**Important:**
- Only the **public key** goes in the frontend
- The **private key** stays on the backend only
- Both frontend and backend must use the **same public key** from the same key pair
## Security Best Practices
1. **Never commit private keys to version control**
- Add `.env` to `.gitignore`
- Use environment variables in production
2. **Use different keys for different environments**
- Development, staging, and production should have separate VAPID key pairs
3. **Keep private keys secure**
- Store them in secure environment variable management systems
- Use secrets management tools in production (AWS Secrets Manager, Azure Key Vault, etc.)
4. **Rotate keys if compromised**
- If a private key is ever exposed, generate new keys immediately
- Update both frontend and backend configurations
## Troubleshooting
### Issue: Push notifications not working
1. **Verify keys match**: Ensure the public key in frontend matches the public key in backend
2. **Check VAPID_CONTACT**: Must be a valid `mailto:` URL
3. **Verify HTTPS**: Web push requires HTTPS (except for localhost)
4. **Check browser console**: Look for errors in the browser console
5. **Verify service worker**: Ensure service worker is properly registered
### Issue: "Invalid VAPID key" error
- Ensure you're using the public key (not private key) in the frontend
- Verify the key format is correct (no extra spaces or line breaks)
- Make sure both frontend and backend are using keys from the same key pair
## Additional Resources
- [Web Push Protocol](https://web.dev/push-notifications-overview/)
- [VAPID Specification](https://tools.ietf.org/html/rfc8292)
- [web-push npm package](https://www.npmjs.com/package/web-push)

350
README.md
View File

@ -50,9 +50,21 @@ A comprehensive backend API for the Royal Enfield Workflow Management System bui
```
3. **Setup environment**
**Option A: Automated Setup (Recommended - Unix/Linux/Mac)**
```bash
chmod +x setup-env.sh
./setup-env.sh
# Follow the interactive prompts
# The script will generate secure secrets automatically
```
**Option B: Manual Setup (Windows or Custom Configuration)**
```bash
cp env.example .env
# Edit .env with your configuration
# Generate JWT secrets manually:
# openssl rand -base64 32 | tr -d "=+/" | cut -c1-32
```
4. **Setup database**
@ -74,8 +86,14 @@ The API will be available at `http://localhost:5000`
### Docker Setup
```bash
# Copy environment file
# Setup environment (use automated script or manual)
# Option 1: Automated (if running on Unix/Linux/Mac host)
chmod +x setup-env.sh
./setup-env.sh
# Option 2: Manual
cp env.example .env
# Edit .env with your configuration
# Start services
docker-compose up --build -d
@ -84,25 +102,291 @@ docker-compose up --build -d
docker-compose logs -f
```
**Note:** Ensure your `.env` file is properly configured before starting Docker containers.
## API Endpoints
### Authentication
- `POST /api/v1/auth/sso-callback` - SSO callback from frontend
- `GET /api/v1/auth/me` - Get current user profile
- `POST /api/v1/auth/refresh` - Refresh access token
- `POST /api/v1/auth/logout` - Logout user
- `GET /api/v1/auth/validate` - Validate token
### Base URL
All API endpoints are prefixed with `/api/v1` unless otherwise specified.
### Health Check
- `GET /health` - API health status
- `GET /api/v1/health` - Detailed health check
### Authentication & Authorization
- `GET /health` - API health status (no auth required)
- `GET /api/v1/config` - Get public system configuration (no auth required)
### Authentication Endpoints (`/api/v1/auth`)
| Method | Endpoint | Description | Auth Required |
|--------|----------|-------------|---------------|
| `POST` | `/token-exchange` | Token exchange for localhost development | No |
| `POST` | `/sso-callback` | SSO callback from frontend | No |
| `POST` | `/refresh` | Refresh access token | No |
| `GET` | `/me` | Get current user profile | Yes |
| `GET` | `/validate` | Validate authentication token | Yes |
| `POST` | `/logout` | Logout user and clear cookies | Optional |
### Workflow Management (`/api/v1/workflows`)
| Method | Endpoint | Description | Auth Required |
|--------|----------|-------------|---------------|
| `GET` | `/` | List all workflows | Yes |
| `GET` | `/my` | List workflows created by current user | Yes |
| `GET` | `/open-for-me` | List workflows open for current user | Yes |
| `GET` | `/closed-by-me` | List workflows closed by current user | Yes |
| `POST` | `/` | Create new workflow | Yes |
| `POST` | `/multipart` | Create workflow with file uploads | Yes |
| `GET` | `/:id` | Get workflow by ID | Yes |
| `GET` | `/:id/details` | Get detailed workflow information | Yes |
| `PUT` | `/:id` | Update workflow | Yes |
| `PUT` | `/:id/multipart` | Update workflow with file uploads | Yes |
| `PATCH` | `/:id/submit` | Submit workflow for approval | Yes |
### Approval Management (`/api/v1/workflows`)
| Method | Endpoint | Description | Auth Required |
|--------|----------|-------------|---------------|
| `GET` | `/:id/approvals` | Get all approval levels for workflow | Yes |
| `GET` | `/:id/approvals/current` | Get current approval level | Yes |
| `PATCH` | `/:id/approvals/:levelId/approve` | Approve at specific level | Yes (Approver) |
| `PATCH` | `/:id/approvals/:levelId/reject` | Reject at specific level | Yes (Approver) |
| `POST` | `/:id/approvals/:levelId/skip` | Skip approver at level | Yes (Initiator/Approver) |
| `POST` | `/:id/approvers/at-level` | Add approver at specific level | Yes (Initiator/Approver) |
### Participants Management (`/api/v1/workflows`)
| Method | Endpoint | Description | Auth Required |
|--------|----------|-------------|---------------|
| `POST` | `/:id/participants/approver` | Add approver to workflow | Yes |
| `POST` | `/:id/participants/spectator` | Add spectator to workflow | Yes |
### Work Notes (`/api/v1/workflows`)
| Method | Endpoint | Description | Auth Required |
|--------|----------|-------------|---------------|
| `GET` | `/:id/work-notes` | Get all work notes for workflow | Yes |
| `POST` | `/:id/work-notes` | Create work note with attachments | Yes |
| `GET` | `/work-notes/attachments/:attachmentId/preview` | Preview work note attachment | Yes |
| `GET` | `/work-notes/attachments/:attachmentId/download` | Download work note attachment | Yes |
### Document Management (`/api/v1/workflows` & `/api/v1/documents`)
| Method | Endpoint | Description | Auth Required |
|--------|----------|-------------|---------------|
| `GET` | `/workflows/documents/:documentId/preview` | Preview workflow document | Yes |
| `GET` | `/workflows/documents/:documentId/download` | Download workflow document | Yes |
| `POST` | `/documents` | Upload document (multipart/form-data) | Yes |
### Activities & Notifications (`/api/v1/workflows`)
| Method | Endpoint | Description | Auth Required |
|--------|----------|-------------|---------------|
| `GET` | `/:id/activity` | Get activity log for workflow | Yes |
| `POST` | `/notifications/subscribe` | Subscribe to push notifications | Yes |
| `POST` | `/notifications/test` | Send test push notification | Yes |
### User Management (`/api/v1/users`)
| Method | Endpoint | Description | Auth Required |
|--------|----------|-------------|---------------|
| `GET` | `/search?q=<email or name>` | Search users by email or name | Yes |
| `POST` | `/ensure` | Ensure user exists (create if not) | Yes |
### Dashboard & Analytics (`/api/v1/dashboard`)
| Method | Endpoint | Description | Auth Required |
|--------|----------|-------------|---------------|
| `GET` | `/kpis` | Get KPI summary (all KPI cards) | Yes |
| `GET` | `/stats/requests` | Get detailed request statistics | Yes |
| `GET` | `/stats/tat-efficiency` | Get TAT efficiency metrics | Yes |
| `GET` | `/stats/approver-load` | Get approver load statistics | Yes |
| `GET` | `/stats/engagement` | Get engagement & quality metrics | Yes |
| `GET` | `/stats/ai-insights` | Get AI & closure insights | Yes |
| `GET` | `/stats/ai-remark-utilization` | Get AI remark utilization with trends | Yes |
| `GET` | `/stats/approver-performance` | Get approver performance metrics | Yes |
| `GET` | `/stats/by-department` | Get department-wise summary | Yes |
| `GET` | `/stats/priority-distribution` | Get priority distribution | Yes |
| `GET` | `/activity/recent` | Get recent activity feed | Yes |
| `GET` | `/requests/critical` | Get high priority/critical requests | Yes |
| `GET` | `/deadlines/upcoming` | Get upcoming deadlines | Yes |
| `GET` | `/reports/lifecycle` | Get Request Lifecycle Report | Yes |
| `GET` | `/reports/activity-log` | Get enhanced User Activity Log Report | Yes |
| `GET` | `/reports/workflow-aging` | Get Workflow Aging Report | Yes |
### Notifications (`/api/v1/notifications`)
| Method | Endpoint | Description | Auth Required |
|--------|----------|-------------|---------------|
| `GET` | `/?page=&limit=&unreadOnly=` | Get user's notifications (paginated) | Yes |
| `GET` | `/unread-count` | Get unread notification count | Yes |
| `PATCH` | `/:notificationId/read` | Mark notification as read | Yes |
| `POST` | `/mark-all-read` | Mark all notifications as read | Yes |
| `DELETE` | `/:notificationId` | Delete notification | Yes |
### TAT (Turnaround Time) Monitoring (`/api/v1/tat`)
| Method | Endpoint | Description | Auth Required |
|--------|----------|-------------|---------------|
| `GET` | `/alerts/request/:requestId` | Get all TAT alerts for a request | Yes |
| `GET` | `/alerts/level/:levelId` | Get TAT alerts for a specific level | Yes |
| `GET` | `/compliance/summary?startDate=&endDate=` | Get TAT compliance summary | Yes |
| `GET` | `/breaches` | Get TAT breach report | Yes |
| `GET` | `/performance/:approverId` | Get TAT performance for approver | Yes |
### AI & Conclusion Management (`/api/v1/conclusions` & `/api/v1/ai`)
| Method | Endpoint | Description | Auth Required |
|--------|----------|-------------|---------------|
| `POST` | `/conclusions/:requestId/generate` | Generate AI-powered conclusion remark | Yes (Initiator) |
| `PUT` | `/conclusions/:requestId` | Update conclusion remark | Yes (Initiator) |
| `POST` | `/conclusions/:requestId/finalize` | Finalize conclusion and close request | Yes (Initiator) |
| `GET` | `/conclusions/:requestId` | Get conclusion for a request | Yes |
| `GET` | `/ai/status` | Get AI service status | Yes (Admin) |
| `POST` | `/ai/reinitialize` | Reinitialize AI service | Yes (Admin) |
### Admin Management (`/api/v1/admin`)
**All admin routes require authentication and admin role.**
#### Holiday Management
| Method | Endpoint | Description |
|--------|----------|-------------|
| `GET` | `/holidays?year=` | Get all holidays (optional year filter) |
| `GET` | `/holidays/calendar/:year` | Get holiday calendar for specific year |
| `POST` | `/holidays` | Create new holiday |
| `PUT` | `/holidays/:holidayId` | Update holiday |
| `DELETE` | `/holidays/:holidayId` | Delete (deactivate) holiday |
| `POST` | `/holidays/bulk-import` | Bulk import holidays from CSV/JSON |
#### Configuration Management
| Method | Endpoint | Description |
|--------|----------|-------------|
| `GET` | `/configurations?category=` | Get all admin configurations |
| `PUT` | `/configurations/:configKey` | Update configuration value |
| `POST` | `/configurations/:configKey/reset` | Reset configuration to default |
#### User Role Management (RBAC)
| Method | Endpoint | Description |
|--------|----------|-------------|
| `POST` | `/users/assign-role` | Assign role to user by email |
| `PUT` | `/users/:userId/role` | Update user's role |
| `GET` | `/users/by-role?role=` | Get users filtered by role |
| `GET` | `/users/role-statistics` | Get count of users in each role |
### Debug Endpoints (`/api/v1/debug`)
**Note:** Debug endpoints are for development/testing purposes.
| Method | Endpoint | Description | Auth Required |
|--------|----------|-------------|---------------|
| `GET` | `/tat-jobs/:requestId` | Check scheduled TAT jobs for request | No |
| `GET` | `/tat-jobs` | Check all queued TAT jobs | No |
| `POST` | `/tat-calculate` | Calculate TAT times (debug) | No |
| `GET` | `/queue-status` | Check queue and worker status | No |
| `POST` | `/trigger-test-tat` | Manually trigger test TAT job | No |
## Environment Variables
See `env.example` for all required environment variables.
### Quick Setup Options
1. **Automated Setup (Recommended)**
```bash
chmod +x setup-env.sh
./setup-env.sh
```
The script will:
- Guide you through all required configuration
- Automatically generate secure JWT and session secrets
- Provide VAPID key generation instructions for web push notifications
- Create a properly formatted `.env` file
2. **Manual Setup**
- Copy `env.example` to `.env`
- Fill in all required values
- Generate secrets using:
```bash
openssl rand -base64 32 | tr -d "=+/" | cut -c1-32
```
### Required Environment Variables
#### Application Configuration
- `NODE_ENV` - Environment (development/production)
- `PORT` - Server port (default: 5000)
- `BASE_URL` - Backend deployed URL
- `FRONTEND_URL` - Frontend URL for CORS
#### Database Configuration
- `DB_HOST` - PostgreSQL host (default: localhost)
- `DB_PORT` - PostgreSQL port (default: 5432)
- `DB_NAME` - Database name (default: re_workflow_db)
- `DB_USER` - Database user
- `DB_PASSWORD` - Database password
#### Authentication & Security
- `JWT_SECRET` - JWT signing secret (min 32 characters, auto-generated by setup script)
- `JWT_EXPIRY` - JWT expiration time (default: 24h)
- `REFRESH_TOKEN_SECRET` - Refresh token secret (auto-generated by setup script)
- `REFRESH_TOKEN_EXPIRY` - Refresh token expiration (default: 7d)
- `SESSION_SECRET` - Session secret (min 32 characters, auto-generated by setup script)
#### SSO Configuration (Okta)
- `OKTA_DOMAIN` - Okta domain URL
- `OKTA_CLIENT_ID` - Okta application client ID
- `OKTA_CLIENT_SECRET` - Okta application client secret
- `OKTA_API_TOKEN` - Okta API token (optional, for user management)
#### Web Push Notifications (VAPID)
- `VAPID_PUBLIC_KEY` - VAPID public key for web push
- `VAPID_PRIVATE_KEY` - VAPID private key for web push
- `VAPID_CONTACT` - Contact email (format: mailto:admin@example.com)
**To generate VAPID keys:**
```bash
npx web-push generate-vapid-keys
```
The setup script provides detailed instructions, or run:
```bash
./setup-env.sh
# Select option 2 for VAPID key generation instructions
```
#### Redis Configuration (TAT Queue)
- `REDIS_URL` - Redis connection URL (default: redis://localhost:6379)
#### Optional Services
**Email Service (SMTP)**
- `SMTP_HOST` - SMTP server host
- `SMTP_PORT` - SMTP port (default: 587)
- `SMTP_SECURE` - Use TLS (default: false)
- `SMTP_USER` - SMTP username
- `SMTP_PASSWORD` - SMTP password
- `EMAIL_FROM` - Sender email address
**Cloud Storage (GCP)**
- `GCP_PROJECT_ID` - Google Cloud Project ID
- `GCP_BUCKET_NAME` - GCS bucket name
- `GCP_KEY_FILE` - Path to GCP service account key file
**AI Service (Claude)**
- `CLAUDE_MODEL` - Claude model name (default: claude-sonnet-4-20250514)
### Environment Variables Reference
See `env.example` for the complete list with default values and descriptions.
## Development
### Prerequisites
- Backend API server running
- PostgreSQL database configured and accessible
- Environment variables set up (use `./setup-env.sh` or manually)
- Redis server (optional, for TAT queue)
### Available Scripts
```bash
# Run in development mode
npm run dev
@ -120,6 +404,18 @@ npm run type-check
npm run build
```
### Setup Checklist
- [ ] Node.js 22.x LTS installed
- [ ] PostgreSQL 16.x installed and running
- [ ] Environment variables configured (`.env` file created)
- [ ] Database created: `createdb re_workflow_db` (or your DB_NAME)
- [ ] Database schema applied (if applicable)
- [ ] Redis installed and running (for TAT monitoring)
- [ ] VAPID keys generated (for web push notifications)
- [ ] Optional: SMTP configured (for email notifications)
- [ ] Optional: GCP credentials configured (for cloud storage)
## Project Structure
```
@ -153,11 +449,39 @@ The database schema includes all tables from the ERD:
## Authentication Flow
1. Frontend handles SSO authentication
1. Frontend handles SSO authentication (Okta/Auth0)
2. Frontend sends user data to `/api/v1/auth/sso-callback`
3. Backend creates/updates user record
4. Backend generates JWT tokens
4. Backend generates JWT access and refresh tokens
5. Frontend uses tokens for subsequent API calls
6. Tokens are automatically refreshed when expired
## Web Push Notifications
The backend supports web push notifications via VAPID (Voluntary Application Server Identification).
### Setup Instructions
1. **Generate VAPID Keys:**
```bash
npx web-push generate-vapid-keys
```
2. **Add to Backend `.env`:**
```
VAPID_PUBLIC_KEY=<your-public-key>
VAPID_PRIVATE_KEY=<your-private-key>
VAPID_CONTACT=mailto:admin@royalenfield.com
```
3. **Add to Frontend `.env`:**
```
VITE_PUBLIC_VAPID_KEY=<same-public-key-as-backend>
```
4. **Important:** The VAPID public key must be the same in both backend and frontend `.env` files.
The `setup-env.sh` script provides detailed VAPID key generation instructions (select option 2).
## Contributing

View File

@ -2,14 +2,15 @@
NODE_ENV=development
PORT=5000
API_VERSION=v1
BASE_URL=http://localhost:5000
BASE_URL={{CURRENT_BACKEND_DEPLOYED_URL}}
FRONTEND_URL={{FrontEND_BASE_URL}}
# Database
DB_HOST=localhost
DB_HOST={{DB_HOST}}
DB_PORT=5432
DB_NAME=re_workflow_db
DB_USER=postgres
DB_PASSWORD=postgres
DB_USER={{DB_USER}}
DB_PASSWORD={{DB_PASWORD}}
DB_SSL=false
DB_POOL_MIN=2
DB_POOL_MAX=10
@ -21,12 +22,6 @@ JWT_EXPIRY=24h
REFRESH_TOKEN_SECRET=your_refresh_token_secret_here
REFRESH_TOKEN_EXPIRY=7d
# Okta/Auth0 Configuration (for backend token exchange in localhost)
OKTA_DOMAIN=https://dev-830839.oktapreview.com
OKTA_CLIENT_ID=0oa2j8slwj5S4bG5k0h8
OKTA_CLIENT_SECRET=your_okta_client_secret_here
OKTA_API_TOKEN=your_okta_api_token_here # For Okta User Management API (user search)
# Session
SESSION_SECRET=your_session_secret_here_min_32_chars
@ -43,20 +38,15 @@ SMTP_USER=notifications@royalenfield.com
SMTP_PASSWORD=your_smtp_password
EMAIL_FROM=RE Workflow System <notifications@royalenfield.com>
# AI Service (for conclusion generation)
AI_API_KEY=your_ai_api_key
AI_MODEL=gpt-4
AI_MAX_TOKENS=500
# AI Service (for conclusion generation) mandatory for claude
CLAUDE_MODEL=claude-sonnet-4-20250514
# Logging
LOG_LEVEL=info
LOG_FILE_PATH=./logs
# CORS - Comma-separated list of allowed origins
# Local: http://localhost:3000
# Production: https://your-frontend-domain.com
# Multiple: http://localhost:3000,http://localhost:5173,https://your-frontend-domain.com
CORS_ORIGIN=http://localhost:3000
# CORS
CORS_ORIGIN="*"
# Rate Limiting
RATE_LIMIT_WINDOW_MS=900000
@ -70,15 +60,17 @@ ALLOWED_FILE_TYPES=pdf,doc,docx,xls,xlsx,ppt,pptx,jpg,jpeg,png,gif
TAT_CHECK_INTERVAL_MINUTES=30
TAT_REMINDER_THRESHOLD_1=50
TAT_REMINDER_THRESHOLD_2=80
OKTA_API_TOKEN={{api token given fto access octa users}}
OKTA_DOMAIN={{okta_domain_given for the envirnment}}
OKTA_CLIENT_ID={{okta_client_id}}
OKTA_CLIENT_SECRET={{okta_client_secret}}
# Redis (for TAT Queue)
REDIS_URL=redis://localhost:6379
# Notificaton Service Worker credentials
VAPID_PUBLIC_KEY={{vapid_public_key}} note: same key need to add on front end for web push
VAPID_PRIVATE_KEY={{vapid_private_key}}
VAPID_CONTACT=mailto:you@example.com
# TAT Test Mode (for development/testing)
# When enabled, 1 TAT hour = 1 minute (for faster testing)
# Example: 48-hour TAT becomes 48 minutes
TAT_TEST_MODE=false
#Redis
REDIS_URL={{REDIS_URL_FOR DELAY JoBS create redis setup and add url here}}
TAT_TEST_MODE=false (on true it will consider 1 hour==1min)
# Working Hours Configuration (optional)
WORK_START_HOUR=9
WORK_END_HOUR=18

280
setup-env.sh Normal file
View File

@ -0,0 +1,280 @@
#!/bin/bash
# Environment Setup Script for Royal Enfield Workflow Backend
echo "=================================================="
echo "Royal Enfield - Backend Environment Setup"
echo "=================================================="
echo ""
# Function to generate random secret
generate_secret() {
openssl rand -base64 32 | tr -d "=+/" | cut -c1-32
}
# Function to create .env file
create_env_file() {
local env_type=$1
local file_name=$2
echo ""
echo "=================================================="
echo "Creating ${env_type} Environment File"
echo "=================================================="
echo ""
# Application Configuration
read -p "Enter NODE_ENV (development/production) [default: development]: " NODE_ENV
NODE_ENV=${NODE_ENV:-development}
read -p "Enter PORT [default: 5000]: " PORT
PORT=${PORT:-5000}
read -p "Enter BASE_URL (backend deployed URL): " BASE_URL
read -p "Enter FRONTEND_URL (frontend URL for CORS): " FRONTEND_URL
# Database Configuration
echo ""
echo "--- Database Configuration ---"
read -p "Enter DB_HOST [default: localhost]: " DB_HOST
DB_HOST=${DB_HOST:-localhost}
read -p "Enter DB_PORT [default: 5432]: " DB_PORT
DB_PORT=${DB_PORT:-5432}
read -p "Enter DB_NAME [default: re_workflow_db]: " DB_NAME
DB_NAME=${DB_NAME:-re_workflow_db}
read -p "Enter DB_USER: " DB_USER
read -p "Enter DB_PASSWORD: " DB_PASSWORD
# JWT Secrets
echo ""
echo "--- JWT Configuration ---"
read -p "Generate JWT_SECRET automatically? (y/n) [default: y]: " GEN_JWT
GEN_JWT=${GEN_JWT:-y}
if [ "$GEN_JWT" = "y" ]; then
JWT_SECRET=$(generate_secret)
echo "✅ Generated JWT_SECRET"
else
read -p "Enter JWT_SECRET (min 32 chars): " JWT_SECRET
fi
read -p "Generate REFRESH_TOKEN_SECRET automatically? (y/n) [default: y]: " GEN_REFRESH
GEN_REFRESH=${GEN_REFRESH:-y}
if [ "$GEN_REFRESH" = "y" ]; then
REFRESH_TOKEN_SECRET=$(generate_secret)
echo "✅ Generated REFRESH_TOKEN_SECRET"
else
read -p "Enter REFRESH_TOKEN_SECRET: " REFRESH_TOKEN_SECRET
fi
# Session Secret
read -p "Generate SESSION_SECRET automatically? (y/n) [default: y]: " GEN_SESSION
GEN_SESSION=${GEN_SESSION:-y}
if [ "$GEN_SESSION" = "y" ]; then
SESSION_SECRET=$(generate_secret)
echo "✅ Generated SESSION_SECRET"
else
read -p "Enter SESSION_SECRET (min 32 chars): " SESSION_SECRET
fi
# Okta Configuration
echo ""
echo "--- Okta SSO Configuration ---"
read -p "Enter OKTA_DOMAIN: " OKTA_DOMAIN
read -p "Enter OKTA_CLIENT_ID: " OKTA_CLIENT_ID
read -p "Enter OKTA_CLIENT_SECRET: " OKTA_CLIENT_SECRET
read -p "Enter OKTA_API_TOKEN (optional): " OKTA_API_TOKEN
# VAPID Keys for Web Push
echo ""
echo "--- Web Push (VAPID) Configuration ---"
echo "Note: VAPID keys are required for push notifications."
echo "Run 'npx web-push generate-vapid-keys' to generate them, or enter manually."
read -p "Enter VAPID_PUBLIC_KEY (or press Enter to skip): " VAPID_PUBLIC_KEY
read -p "Enter VAPID_PRIVATE_KEY (or press Enter to skip): " VAPID_PRIVATE_KEY
read -p "Enter VAPID_CONTACT email [default: mailto:admin@example.com]: " VAPID_CONTACT
VAPID_CONTACT=${VAPID_CONTACT:-mailto:admin@example.com}
# Redis Configuration
echo ""
echo "--- Redis Configuration (for TAT Queue) ---"
read -p "Enter REDIS_URL [default: redis://localhost:6379]: " REDIS_URL
REDIS_URL=${REDIS_URL:-redis://localhost:6379}
# Optional Services
echo ""
echo "--- Optional Services ---"
read -p "Enter SMTP_HOST (or press Enter to skip): " SMTP_HOST
read -p "Enter SMTP_USER (or press Enter to skip): " SMTP_USER
read -p "Enter SMTP_PASSWORD (or press Enter to skip): " SMTP_PASSWORD
read -p "Enter GCP_PROJECT_ID (or press Enter to skip): " GCP_PROJECT_ID
read -p "Enter GCP_BUCKET_NAME (or press Enter to skip): " GCP_BUCKET_NAME
read -p "Enter CLAUDE_MODEL [default: claude-sonnet-4-20250514]: " CLAUDE_MODEL
CLAUDE_MODEL=${CLAUDE_MODEL:-claude-sonnet-4-20250514}
# Create .env file
cat > "$file_name" << EOF
# Application
NODE_ENV=${NODE_ENV}
PORT=${PORT}
API_VERSION=v1
BASE_URL=${BASE_URL}
FRONTEND_URL=${FRONTEND_URL}
# Database
DB_HOST=${DB_HOST}
DB_PORT=${DB_PORT}
DB_NAME=${DB_NAME}
DB_USER=${DB_USER}
DB_PASSWORD=${DB_PASSWORD}
DB_SSL=false
DB_POOL_MIN=2
DB_POOL_MAX=10
# SSO Configuration (Frontend-handled)
# Backend only needs JWT secrets for token validation
JWT_SECRET=${JWT_SECRET}
JWT_EXPIRY=24h
REFRESH_TOKEN_SECRET=${REFRESH_TOKEN_SECRET}
REFRESH_TOKEN_EXPIRY=7d
# Session
SESSION_SECRET=${SESSION_SECRET}
# Cloud Storage (GCP)
GCP_PROJECT_ID=${GCP_PROJECT_ID}
GCP_BUCKET_NAME=${GCP_BUCKET_NAME}
GCP_KEY_FILE=./config/gcp-key.json
# Email Service (Optional)
SMTP_HOST=${SMTP_HOST}
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=${SMTP_USER}
SMTP_PASSWORD=${SMTP_PASSWORD}
EMAIL_FROM=RE Workflow System <notifications@royalenfield.com>
# AI Service (for conclusion generation) mandatory for claude
CLAUDE_MODEL=${CLAUDE_MODEL}
# Logging
LOG_LEVEL=info
LOG_FILE_PATH=./logs
# Rate Limiting
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX_REQUESTS=100
# File Upload
MAX_FILE_SIZE_MB=10
ALLOWED_FILE_TYPES=pdf,doc,docx,xls,xlsx,ppt,pptx,jpg,jpeg,png,gif
# TAT Monitoring
TAT_CHECK_INTERVAL_MINUTES=30
TAT_REMINDER_THRESHOLD_1=50
TAT_REMINDER_THRESHOLD_2=80
OKTA_API_TOKEN=${OKTA_API_TOKEN}
OKTA_DOMAIN=${OKTA_DOMAIN}
OKTA_CLIENT_ID=${OKTA_CLIENT_ID}
OKTA_CLIENT_SECRET=${OKTA_CLIENT_SECRET}
# Notification Service Worker credentials (Web Push / VAPID)
VAPID_PUBLIC_KEY=${VAPID_PUBLIC_KEY}
VAPID_PRIVATE_KEY=${VAPID_PRIVATE_KEY}
VAPID_CONTACT=${VAPID_CONTACT}
# Redis (for TAT Queue)
REDIS_URL=${REDIS_URL}
TAT_TEST_MODE=false
EOF
echo ""
echo "✅ Created ${file_name}"
}
# Function to show VAPID key generation instructions
show_vapid_instructions() {
echo ""
echo "=================================================="
echo "VAPID Key Generation Instructions"
echo "=================================================="
echo ""
echo "VAPID (Voluntary Application Server Identification) keys are required"
echo "for web push notifications. You need to generate a key pair:"
echo ""
echo "1. Generate VAPID keys using npx (no installation needed):"
echo " npx web-push generate-vapid-keys"
echo ""
echo " This will output:"
echo " ================================================"
echo " Public Key: <your-public-key>"
echo " Private Key: <your-private-key>"
echo " ================================================"
echo ""
echo "3. Add the keys to your .env file:"
echo " VAPID_PUBLIC_KEY=<your-public-key>"
echo " VAPID_PRIVATE_KEY=<your-private-key>"
echo " VAPID_CONTACT=mailto:your-email@example.com"
echo ""
echo "4. IMPORTANT: Add the SAME VAPID_PUBLIC_KEY to your frontend .env file:"
echo " VITE_PUBLIC_VAPID_KEY=<your-public-key>"
echo ""
echo "5. The VAPID_CONTACT should be a valid mailto: URL"
echo " Example: mailto:admin@royalenfield.com"
echo ""
echo "Note: Keep your VAPID_PRIVATE_KEY secure and never commit it to version control!"
echo ""
}
# Main execution
echo "This script will help you create environment configuration files for your backend."
echo ""
echo "Options:"
echo "1. Create .env file (interactive)"
echo "2. Show VAPID key generation instructions"
echo "3. Exit"
echo ""
read -p "Select an option (1-3): " OPTION
case $OPTION in
1)
create_env_file "Development" ".env"
echo ""
echo "=================================================="
echo "Setup Complete!"
echo "=================================================="
echo ""
echo "Next Steps:"
echo ""
echo "1. Generate VAPID keys for web push notifications:"
echo " npx web-push generate-vapid-keys"
echo " Then add them to your .env file"
echo ""
echo "2. Set up your database:"
echo " - Ensure PostgreSQL is running"
echo " - Run migrations if needed"
echo ""
echo "3. Set up Redis (for TAT queue):"
echo " - Install and start Redis"
echo " - Update REDIS_URL in .env"
echo ""
echo "4. Start the backend:"
echo " npm run dev"
echo ""
;;
2)
show_vapid_instructions
;;
3)
echo "Exiting..."
exit 0
;;
*)
echo "Invalid option. Exiting..."
exit 1
;;
esac

View File

@ -5,7 +5,8 @@ const ssoConfig: SSOConfig = {
jwtExpiry: process.env.JWT_EXPIRY || '24h',
refreshTokenExpiry: process.env.REFRESH_TOKEN_EXPIRY || '7d',
sessionSecret: process.env.SESSION_SECRET || '',
allowedOrigins: process.env.CORS_ORIGIN?.split(',') || ['http://localhost:3000'],
// Use only FRONTEND_URL from environment - no fallbacks
allowedOrigins: process.env.FRONTEND_URL?.split(',').map(s => s.trim()).filter(Boolean) || [],
// Okta/Auth0 configuration for token exchange
oktaDomain: process.env.OKTA_DOMAIN || 'https://dev-830839.oktapreview.com',
oktaClientId: process.env.OKTA_CLIENT_ID || '',

View File

@ -1,44 +1,47 @@
import cors from 'cors';
// Get allowed origins from environment variable or default to localhost
const getOrigins = (): string[] => {
const corsOrigin = process.env.CORS_ORIGIN;
if (!corsOrigin) {
return ['http://localhost:3000'];
// Configure allowed origins - uses only FRONTEND_URL from environment
const getAllowedOrigins = (): string[] | boolean => {
const frontendUrl = process.env.FRONTEND_URL;
const isProduction = process.env.NODE_ENV === 'production';
// FRONTEND_URL is required - no fallbacks
if (!frontendUrl) {
console.error('❌ ERROR: FRONTEND_URL environment variable is not set!');
console.error(' CORS will block all origins. This will prevent frontend connections.');
console.error(' To fix: Set FRONTEND_URL environment variable with your frontend URL(s)');
console.error(' Example: FRONTEND_URL=https://your-frontend-domain.com');
console.error(' Multiple origins: FRONTEND_URL=https://app1.com,https://app2.com');
console.error(' Development: FRONTEND_URL=http://localhost:3000');
// Return empty array to block all origins if not configured
return [];
}
// Handle both comma-separated string and single origin
if (corsOrigin.includes(',')) {
return corsOrigin.split(',').map(origin => origin.trim());
// If FRONTEND_URL is set to '*', allow all origins
if (frontendUrl === '*') {
if (isProduction) {
console.warn('⚠️ WARNING: FRONTEND_URL is set to allow ALL origins (*) in production. This is not recommended for security.');
}
return true; // This allows any origin
}
return [corsOrigin.trim()];
// Parse comma-separated URLs or use single URL
const origins = frontendUrl.split(',').map(url => url.trim()).filter(Boolean);
if (origins.length === 0) {
console.error('❌ ERROR: FRONTEND_URL is set but contains no valid URLs!');
return [];
}
console.log(`✅ CORS: Allowing origins from FRONTEND_URL: ${origins.join(', ')}`);
return origins;
};
export const corsMiddleware = cors({
origin: (origin, callback) => {
const allowedOrigins = getOrigins();
// In development, be more permissive
if (process.env.NODE_ENV !== 'production') {
// Allow localhost on any port
if (!origin || origin.includes('localhost') || origin.includes('127.0.0.1')) {
return callback(null, true);
}
}
// Allow requests with no origin (like mobile apps or curl requests)
if (!origin) {
return callback(null, true);
}
if (origin && allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true,
origin: getAllowedOrigins(),
credentials: true, // Allow cookies and authorization headers
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With', 'Accept'],
exposedHeaders: ['X-Total-Count', 'X-Page-Count'],
optionsSuccessStatus: 200, // Some legacy browsers (IE11, various SmartTVs) choke on 204
optionsSuccessStatus: 200,
});

View File

@ -6,18 +6,40 @@ let io: Server | null = null;
const onlineUsersPerRequest = new Map<string, Set<string>>();
export function initSocket(httpServer: any) {
const defaultOrigins = [
'http://localhost:3000',
'http://127.0.0.1:3000',
'http://localhost:5173',
'http://127.0.0.1:5173'
];
const configured = (process.env.FRONTEND_ORIGIN || '').split(',').map(s => s.trim()).filter(Boolean);
const origins = configured.length ? configured : defaultOrigins;
// Use only FRONTEND_URL from environment - no fallbacks
const frontendUrl = process.env.FRONTEND_URL;
const isProduction = process.env.NODE_ENV === 'production';
let origins: string[];
// FRONTEND_URL is required - no fallbacks
if (!frontendUrl) {
console.error('❌ ERROR: FRONTEND_URL environment variable is not set for Socket.io!');
console.error(' Socket.io will block all origins. This will prevent WebSocket connections.');
console.error(' To fix: Set FRONTEND_URL environment variable with your frontend URL(s)');
console.error(' Example: FRONTEND_URL=https://your-frontend-domain.com');
console.error(' Multiple origins: FRONTEND_URL=https://app1.com,https://app2.com');
console.error(' Development: FRONTEND_URL=http://localhost:3000');
origins = []; // Block all origins if not configured
} else if (frontendUrl === '*') {
if (isProduction) {
console.warn('⚠️ WARNING: Socket.io FRONTEND_URL is set to allow ALL origins (*) in production. This is not recommended for security.');
}
origins = ['*'] as any; // Allow all origins
} else {
origins = frontendUrl.split(',').map(s => s.trim()).filter(Boolean);
if (origins.length === 0) {
console.error('❌ ERROR: FRONTEND_URL is set but contains no valid URLs for Socket.io!');
origins = [];
} else {
console.log(`✅ Socket.io: Allowing origins from FRONTEND_URL: ${origins.join(', ')}`);
}
}
io = new Server(httpServer, {
cors: {
origin: origins,
origin: origins.length === 1 && origins[0] === '*' ? true : origins,
methods: ['GET', 'POST'],
credentials: true
},