From 336df2023c200637dba10512540095ba5185408c Mon Sep 17 00:00:00 2001 From: laxmanhalaki Date: Mon, 17 Nov 2025 16:52:14 +0530 Subject: [PATCH] setup instruction added --- GENERATE_VAPID_KEYS.md | 124 ++++++++++ README.md | 350 +++++++++++++++++++++++++++-- env.example | 48 ++-- setup-env.sh | 280 +++++++++++++++++++++++ src/config/sso.ts | 3 +- src/middlewares/cors.middleware.ts | 69 +++--- src/realtime/socket.ts | 40 +++- 7 files changed, 830 insertions(+), 84 deletions(-) create mode 100644 GENERATE_VAPID_KEYS.md create mode 100644 setup-env.sh diff --git a/GENERATE_VAPID_KEYS.md b/GENERATE_VAPID_KEYS.md new file mode 100644 index 0000000..53afb85 --- /dev/null +++ b/GENERATE_VAPID_KEYS.md @@ -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) + diff --git a/README.md b/README.md index b79cc36..413e191 100644 --- a/README.md +++ b/README.md @@ -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=` | 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= + VAPID_PRIVATE_KEY= + VAPID_CONTACT=mailto:admin@royalenfield.com + ``` + +3. **Add to Frontend `.env`:** + ``` + VITE_PUBLIC_VAPID_KEY= + ``` + +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 diff --git a/env.example b/env.example index e09cda0..08291ad 100644 --- a/env.example +++ b/env.example @@ -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 -# 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 \ No newline at end of file diff --git a/setup-env.sh b/setup-env.sh new file mode 100644 index 0000000..b7ed73d --- /dev/null +++ b/setup-env.sh @@ -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 + +# 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: " + echo " Private Key: " + echo " ================================================" + echo "" + echo "3. Add the keys to your .env file:" + echo " VAPID_PUBLIC_KEY=" + echo " VAPID_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=" + 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 + diff --git a/src/config/sso.ts b/src/config/sso.ts index 9690a51..a79e23d 100644 --- a/src/config/sso.ts +++ b/src/config/sso.ts @@ -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 || '', diff --git a/src/middlewares/cors.middleware.ts b/src/middlewares/cors.middleware.ts index 0619498..f0648c0 100644 --- a/src/middlewares/cors.middleware.ts +++ b/src/middlewares/cors.middleware.ts @@ -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, }); \ No newline at end of file diff --git a/src/realtime/socket.ts b/src/realtime/socket.ts index 0b21d55..8b4174a 100644 --- a/src/realtime/socket.ts +++ b/src/realtime/socket.ts @@ -6,18 +6,40 @@ let io: Server | null = null; const onlineUsersPerRequest = new Map>(); 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 },