setup instruction added
This commit is contained in:
parent
43b0a493f4
commit
336df2023c
124
GENERATE_VAPID_KEYS.md
Normal file
124
GENERATE_VAPID_KEYS.md
Normal 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
350
README.md
@ -50,9 +50,21 @@ A comprehensive backend API for the Royal Enfield Workflow Management System bui
|
|||||||
```
|
```
|
||||||
|
|
||||||
3. **Setup environment**
|
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
|
```bash
|
||||||
cp env.example .env
|
cp env.example .env
|
||||||
# Edit .env with your configuration
|
# Edit .env with your configuration
|
||||||
|
# Generate JWT secrets manually:
|
||||||
|
# openssl rand -base64 32 | tr -d "=+/" | cut -c1-32
|
||||||
```
|
```
|
||||||
|
|
||||||
4. **Setup database**
|
4. **Setup database**
|
||||||
@ -74,8 +86,14 @@ The API will be available at `http://localhost:5000`
|
|||||||
### Docker Setup
|
### Docker Setup
|
||||||
|
|
||||||
```bash
|
```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
|
cp env.example .env
|
||||||
|
# Edit .env with your configuration
|
||||||
|
|
||||||
# Start services
|
# Start services
|
||||||
docker-compose up --build -d
|
docker-compose up --build -d
|
||||||
@ -84,25 +102,291 @@ docker-compose up --build -d
|
|||||||
docker-compose logs -f
|
docker-compose logs -f
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Note:** Ensure your `.env` file is properly configured before starting Docker containers.
|
||||||
|
|
||||||
## API Endpoints
|
## API Endpoints
|
||||||
|
|
||||||
### Authentication
|
### Base URL
|
||||||
- `POST /api/v1/auth/sso-callback` - SSO callback from frontend
|
All API endpoints are prefixed with `/api/v1` unless otherwise specified.
|
||||||
- `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
|
|
||||||
|
|
||||||
### Health Check
|
### Authentication & Authorization
|
||||||
- `GET /health` - API health status
|
- `GET /health` - API health status (no auth required)
|
||||||
- `GET /api/v1/health` - Detailed health check
|
- `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
|
## 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
|
## 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
|
```bash
|
||||||
# Run in development mode
|
# Run in development mode
|
||||||
npm run dev
|
npm run dev
|
||||||
@ -120,6 +404,18 @@ npm run type-check
|
|||||||
npm run build
|
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
|
## Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -153,11 +449,39 @@ The database schema includes all tables from the ERD:
|
|||||||
|
|
||||||
## Authentication Flow
|
## 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`
|
2. Frontend sends user data to `/api/v1/auth/sso-callback`
|
||||||
3. Backend creates/updates user record
|
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
|
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
|
## Contributing
|
||||||
|
|
||||||
|
|||||||
48
env.example
48
env.example
@ -2,14 +2,15 @@
|
|||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
PORT=5000
|
PORT=5000
|
||||||
API_VERSION=v1
|
API_VERSION=v1
|
||||||
BASE_URL=http://localhost:5000
|
BASE_URL={{CURRENT_BACKEND_DEPLOYED_URL}}
|
||||||
|
FRONTEND_URL={{FrontEND_BASE_URL}}
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
DB_HOST=localhost
|
DB_HOST={{DB_HOST}}
|
||||||
DB_PORT=5432
|
DB_PORT=5432
|
||||||
DB_NAME=re_workflow_db
|
DB_NAME=re_workflow_db
|
||||||
DB_USER=postgres
|
DB_USER={{DB_USER}}
|
||||||
DB_PASSWORD=postgres
|
DB_PASSWORD={{DB_PASWORD}}
|
||||||
DB_SSL=false
|
DB_SSL=false
|
||||||
DB_POOL_MIN=2
|
DB_POOL_MIN=2
|
||||||
DB_POOL_MAX=10
|
DB_POOL_MAX=10
|
||||||
@ -21,12 +22,6 @@ JWT_EXPIRY=24h
|
|||||||
REFRESH_TOKEN_SECRET=your_refresh_token_secret_here
|
REFRESH_TOKEN_SECRET=your_refresh_token_secret_here
|
||||||
REFRESH_TOKEN_EXPIRY=7d
|
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
|
||||||
SESSION_SECRET=your_session_secret_here_min_32_chars
|
SESSION_SECRET=your_session_secret_here_min_32_chars
|
||||||
|
|
||||||
@ -43,20 +38,15 @@ SMTP_USER=notifications@royalenfield.com
|
|||||||
SMTP_PASSWORD=your_smtp_password
|
SMTP_PASSWORD=your_smtp_password
|
||||||
EMAIL_FROM=RE Workflow System <notifications@royalenfield.com>
|
EMAIL_FROM=RE Workflow System <notifications@royalenfield.com>
|
||||||
|
|
||||||
# AI Service (for conclusion generation)
|
# AI Service (for conclusion generation) mandatory for claude
|
||||||
AI_API_KEY=your_ai_api_key
|
CLAUDE_MODEL=claude-sonnet-4-20250514
|
||||||
AI_MODEL=gpt-4
|
|
||||||
AI_MAX_TOKENS=500
|
|
||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
LOG_LEVEL=info
|
LOG_LEVEL=info
|
||||||
LOG_FILE_PATH=./logs
|
LOG_FILE_PATH=./logs
|
||||||
|
|
||||||
# CORS - Comma-separated list of allowed origins
|
# CORS
|
||||||
# Local: http://localhost:3000
|
CORS_ORIGIN="*"
|
||||||
# Production: https://your-frontend-domain.com
|
|
||||||
# Multiple: http://localhost:3000,http://localhost:5173,https://your-frontend-domain.com
|
|
||||||
CORS_ORIGIN=http://localhost:3000
|
|
||||||
|
|
||||||
# Rate Limiting
|
# Rate Limiting
|
||||||
RATE_LIMIT_WINDOW_MS=900000
|
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_CHECK_INTERVAL_MINUTES=30
|
||||||
TAT_REMINDER_THRESHOLD_1=50
|
TAT_REMINDER_THRESHOLD_1=50
|
||||||
TAT_REMINDER_THRESHOLD_2=80
|
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)
|
# Notificaton Service Worker credentials
|
||||||
REDIS_URL=redis://localhost:6379
|
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)
|
#Redis
|
||||||
# When enabled, 1 TAT hour = 1 minute (for faster testing)
|
REDIS_URL={{REDIS_URL_FOR DELAY JoBS create redis setup and add url here}}
|
||||||
# Example: 48-hour TAT becomes 48 minutes
|
TAT_TEST_MODE=false (on true it will consider 1 hour==1min)
|
||||||
TAT_TEST_MODE=false
|
|
||||||
|
|
||||||
# Working Hours Configuration (optional)
|
|
||||||
WORK_START_HOUR=9
|
|
||||||
WORK_END_HOUR=18
|
|
||||||
280
setup-env.sh
Normal file
280
setup-env.sh
Normal 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
|
||||||
|
|
||||||
@ -5,7 +5,8 @@ const ssoConfig: SSOConfig = {
|
|||||||
jwtExpiry: process.env.JWT_EXPIRY || '24h',
|
jwtExpiry: process.env.JWT_EXPIRY || '24h',
|
||||||
refreshTokenExpiry: process.env.REFRESH_TOKEN_EXPIRY || '7d',
|
refreshTokenExpiry: process.env.REFRESH_TOKEN_EXPIRY || '7d',
|
||||||
sessionSecret: process.env.SESSION_SECRET || '',
|
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
|
// Okta/Auth0 configuration for token exchange
|
||||||
oktaDomain: process.env.OKTA_DOMAIN || 'https://dev-830839.oktapreview.com',
|
oktaDomain: process.env.OKTA_DOMAIN || 'https://dev-830839.oktapreview.com',
|
||||||
oktaClientId: process.env.OKTA_CLIENT_ID || '',
|
oktaClientId: process.env.OKTA_CLIENT_ID || '',
|
||||||
|
|||||||
@ -1,44 +1,47 @@
|
|||||||
import cors from 'cors';
|
import cors from 'cors';
|
||||||
|
|
||||||
// Get allowed origins from environment variable or default to localhost
|
// Configure allowed origins - uses only FRONTEND_URL from environment
|
||||||
const getOrigins = (): string[] => {
|
const getAllowedOrigins = (): string[] | boolean => {
|
||||||
const corsOrigin = process.env.CORS_ORIGIN;
|
const frontendUrl = process.env.FRONTEND_URL;
|
||||||
if (!corsOrigin) {
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
return ['http://localhost:3000'];
|
|
||||||
|
// 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(',')) {
|
// If FRONTEND_URL is set to '*', allow all origins
|
||||||
return corsOrigin.split(',').map(origin => origin.trim());
|
if (frontendUrl === '*') {
|
||||||
|
if (isProduction) {
|
||||||
|
console.warn('⚠️ WARNING: FRONTEND_URL is set to allow ALL origins (*) in production. This is not recommended for security.');
|
||||||
}
|
}
|
||||||
return [corsOrigin.trim()];
|
return true; // This allows any origin
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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({
|
export const corsMiddleware = cors({
|
||||||
origin: (origin, callback) => {
|
origin: getAllowedOrigins(),
|
||||||
const allowedOrigins = getOrigins();
|
credentials: true, // Allow cookies and authorization headers
|
||||||
|
|
||||||
// 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,
|
|
||||||
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
|
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
|
||||||
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With', 'Accept'],
|
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With', 'Accept'],
|
||||||
exposedHeaders: ['X-Total-Count', 'X-Page-Count'],
|
exposedHeaders: ['X-Total-Count', 'X-Page-Count'],
|
||||||
optionsSuccessStatus: 200, // Some legacy browsers (IE11, various SmartTVs) choke on 204
|
optionsSuccessStatus: 200,
|
||||||
});
|
});
|
||||||
@ -6,18 +6,40 @@ let io: Server | null = null;
|
|||||||
const onlineUsersPerRequest = new Map<string, Set<string>>();
|
const onlineUsersPerRequest = new Map<string, Set<string>>();
|
||||||
|
|
||||||
export function initSocket(httpServer: any) {
|
export function initSocket(httpServer: any) {
|
||||||
const defaultOrigins = [
|
// Use only FRONTEND_URL from environment - no fallbacks
|
||||||
'http://localhost:3000',
|
const frontendUrl = process.env.FRONTEND_URL;
|
||||||
'http://127.0.0.1:3000',
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
'http://localhost:5173',
|
|
||||||
'http://127.0.0.1:5173'
|
let origins: string[];
|
||||||
];
|
|
||||||
const configured = (process.env.FRONTEND_ORIGIN || '').split(',').map(s => s.trim()).filter(Boolean);
|
// FRONTEND_URL is required - no fallbacks
|
||||||
const origins = configured.length ? configured : defaultOrigins;
|
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, {
|
io = new Server(httpServer, {
|
||||||
cors: {
|
cors: {
|
||||||
origin: origins,
|
origin: origins.length === 1 && origins[0] === '*' ? true : origins,
|
||||||
methods: ['GET', 'POST'],
|
methods: ['GET', 'POST'],
|
||||||
credentials: true
|
credentials: true
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user