Re_Backend/.cursor/project_setup.md
2025-10-29 19:31:40 +05:30

1773 lines
48 KiB
Markdown

# RE Workflow Management System - Backend Setup Guide
**Version:** 1.0
**Date:** October 16, 2025
**Technology Stack:** Node.js 22 LTS + TypeScript 5.7 + Express.js 4.21 + PostgreSQL 16
---
## Table of Contents
1. [Backend Architecture Overview](#backend-architecture-overview)
2. [Technology Stack](#technology-stack)
3. [Project Folder Structure](#project-folder-structure)
4. [Database Schema Design](#database-schema-design)
5. [API Endpoints Structure](#api-endpoints-structure)
6. [Authentication & Security](#authentication-security)
7. [Configuration Management](#configuration-management)
8. [Deployment Architecture](#deployment-architecture)
9. [Development Setup Instructions](#development-setup-instructions)
---
## 1. Backend Architecture Overview
### High-Level Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ APPLICATION LAYER (Node.js) │
│ - Express.js REST API Server │
│ - Business Logic & Controllers │
│ - Service Layer (Workflow, Notification, Document) │
│ - AI Integration (Conclusion Generation) │
│ - Authentication Middleware (JWT + SSO) │
│ - Validation Layer (Zod) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ DATA LAYER (PostgreSQL) │
│ - Relational Database │
│ - Stored Procedures & Functions │
│ - Triggers for Activity Logging │
│ - Indexes for Performance │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ STORAGE LAYER │
│ - Cloud Storage (GCP Cloud Storage) │
│ - Document Repository │
│ - Backup & Recovery System │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ EXTERNAL SERVICES │
│ - SSO Authentication Provider │
│ - Email Service (SMTP) │
│ - AI Service (OpenAI) │
└─────────────────────────────────────────────────────────────┘
```
---
## 2. Technology Stack
| Component | Technology | Version | Purpose |
|-----------|-----------|---------|---------|
| **Backend Runtime** | Node.js | 22.x LTS | Server-side JavaScript runtime |
| **Backend Language** | TypeScript | 5.7.x | Type-safe backend development |
| **Backend Framework** | Express.js | 4.21.x | RESTful API framework |
| **Database** | PostgreSQL | 16.x | Primary relational database |
| **ORM** | Sequelize + TypeScript | 6.37.x | Database ORM with TypeScript support |
| **Authentication** | Passport.js | 0.7.x | SSO integration middleware |
| **Validation** | Zod | 3.24.x | TypeScript-first schema validation |
| **File Upload** | Multer | 1.4.x | Multipart file handling |
| **Logging** | Winston | 3.17.x | Application logging |
| **API Documentation** | Swagger/OpenAPI | 3.1 | API documentation |
| **Testing** | Jest + Supertest + ts-jest | 29.x | Unit & integration testing |
| **Process Manager** | PM2 | 5.4.x | Production process management |
| **Code Quality** | ESLint + Prettier + TypeScript ESLint | 9.x/3.x | Code linting & formatting |
---
## 3. Project Folder Structure
### Backend Repository (`re-workflow-backend`)
```
Re_Backend/
├── src/ # Source code
│ ├── app.ts
│ ├── server.ts
│ ├── types/ # TypeScript type definitions
│ │ ├── index.ts
│ │ ├── express.d.ts
│ │ ├── user.types.ts
│ │ ├── workflow.types.ts
│ │ ├── approval.types.ts
│ │ ├── document.types.ts
│ │ ├── notification.types.ts
│ │ └── common.types.ts
│ │
│ ├── config/ # Configuration files
│ │ ├── database.ts
│ │ ├── jwt.ts
│ │ ├── sso.ts
│ │ ├── storage.ts
│ │ ├── email.ts
│ │ └── constants.ts
│ │
│ ├── controllers/ # Request handlers
│ │ ├── auth.controller.ts
│ │ ├── workflow.controller.ts
│ │ ├── approval.controller.ts
│ │ ├── document.controller.ts
│ │ ├── notification.controller.ts
│ │ ├── workNote.controller.ts
│ │ ├── participant.controller.ts
│ │ ├── dashboard.controller.ts
│ │ └── user.controller.ts
│ │
│ ├── services/ # Business logic layer
│ │ ├── auth.service.ts
│ │ ├── workflow.service.ts
│ │ ├── approval.service.ts
│ │ ├── tat.service.ts
│ │ ├── notification.service.ts
│ │ ├── document.service.ts
│ │ ├── workNote.service.ts
│ │ ├── participant.service.ts
│ │ ├── activity.service.ts
│ │ ├── ai.service.ts
│ │ ├── email.service.ts
│ │ └── storage.service.ts
│ │
│ ├── models/ # Sequelize models (ORM)
│ │ ├── index.ts
│ │ ├── User.ts
│ │ ├── WorkflowRequest.ts
│ │ ├── ApprovalLevel.ts
│ │ ├── Approver.ts
│ │ ├── Participant.ts
│ │ ├── Spectator.ts
│ │ ├── Document.ts
│ │ ├── WorkNote.ts
│ │ ├── Activity.ts
│ │ ├── Notification.ts
│ │ ├── TATTracking.ts
│ │ └── ConclusionRemark.ts
│ │
│ ├── routes/ # API route definitions
│ │ ├── index.ts
│ │ ├── auth.routes.ts
│ │ ├── workflow.routes.ts
│ │ ├── approval.routes.ts
│ │ ├── document.routes.ts
│ │ ├── notification.routes.ts
│ │ ├── workNote.routes.ts
│ │ ├── participant.routes.ts
│ │ ├── dashboard.routes.ts
│ │ └── user.routes.ts
│ │
│ ├── middlewares/ # Express middlewares
│ │ ├── auth.middleware.ts
│ │ ├── sso.middleware.ts
│ │ ├── validate.middleware.ts
│ │ ├── upload.middleware.ts
│ │ ├── rateLimiter.middleware.ts
│ │ ├── errorHandler.middleware.ts
│ │ ├── logger.middleware.ts
│ │ └── cors.middleware.ts
│ │
│ ├── validators/ # Request validation schemas (Zod)
│ │ ├── auth.validator.ts
│ │ ├── workflow.validator.ts
│ │ ├── approval.validator.ts
│ │ ├── document.validator.ts
│ │ ├── workNote.validator.ts
│ │ └── participant.validator.ts
│ │
│ ├── utils/ # Utility functions
│ │ ├── logger.ts
│ │ ├── responseHandler.ts
│ │ ├── errorHandler.ts
│ │ ├── dateCalculator.ts
│ │ ├── fileValidator.ts
│ │ ├── emailTemplate.ts
│ │ └── helpers.ts
│ │
│ ├── jobs/ # Background jobs & schedulers
│ │ ├── tatMonitor.job.ts
│ │ ├── reminderSender.job.ts
│ │ ├── dataCleanup.job.ts
│ │ └── reportGenerator.job.ts
│ │
│ └── seeders/ # Database seed data
│ ├── admin.seeder.ts
│ └── testData.seeder.ts
├── dist/ # Compiled JavaScript output
├── tests/ # Test suites
│ ├── unit/
│ │ ├── services/
│ │ ├── controllers/
│ │ └── utils/
│ ├── integration/
│ │ ├── api/
│ │ └── database/
│ └── setup.js
├── database/ # Database Scripts & Migrations
│ ├── migrations/
│ ├── seeders/
│ ├── schema/
│ │ └── schema.sql
│ └── README.md
├── logs/ # Application logs
│ ├── error.log
│ ├── combined.log
│ └── access.log
├── uploads/ # Temporary upload directory
│ └── .gitkeep
├── docs/ # API Documentation
│ ├── swagger/
│ └── postman/
├── scripts/ # Deployment & Utility Scripts
│ ├── deploy.sh
│ ├── backup.sh
│ ├── setup.sh
│ └── seed-db.sh
├── .env.example
├── .env.development
├── .env.production
├── .eslintrc.json
├── .prettierrc
├── .gitignore
├── .dockerignore
├── Dockerfile
├── docker-compose.yml
├── jest.config.js
├── tsconfig.json
├── nodemon.json
├── package.json
├── package-lock.json
└── README.md
```
---
## 4. Key Backend Files
### 4.1 TypeScript Configuration
#### `tsconfig.json`
```json
{
"compilerOptions": {
"target": "ES2021",
"module": "commonjs",
"lib": ["ES2021"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"moduleResolution": "node",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"types": ["node", "jest"],
"typeRoots": ["./node_modules/@types", "./src/types"],
"baseUrl": "./src",
"paths": {
"@/*": ["./*"],
"@controllers/*": ["controllers/*"],
"@services/*": ["services/*"],
"@models/*": ["models/*"],
"@middlewares/*": ["middlewares/*"],
"@utils/*": ["utils/*"],
"@types/*": ["types/*"],
"@config/*": ["config/*"]
}
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "tests"]
}
```
### 4.2 Express Application
#### `src/app.ts`
```typescript
import express, { Application, Request, Response } from 'express';
import helmet from 'helmet';
import cors from 'cors';
import morgan from 'morgan';
import routes from './routes';
import errorHandler from './middlewares/errorHandler.middleware';
import logger from './utils/logger';
const app: Application = express();
// Security middleware
app.use(helmet());
app.use(cors());
// Body parsing middleware
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
// Logging middleware
app.use(morgan('combined', { stream: logger.stream }));
// API routes
app.use('/api/v1', routes);
// Health check endpoint
app.get('/health', (req: Request, res: Response) => {
res.status(200).json({ status: 'OK', timestamp: new Date() });
});
// Error handling middleware (must be last)
app.use(errorHandler);
export default app;
```
### 4.3 Server Entry Point
#### `src/server.ts`
```typescript
import app from './app';
import { sequelize } from './models';
import logger from './utils/logger';
const PORT: number = parseInt(process.env.PORT || '5000', 10);
// Database connection and server start
const startServer = async (): Promise<void> => {
try {
// Test database connection
await sequelize.authenticate();
logger.info('Database connection established successfully');
// Sync models (in development only)
if (process.env.NODE_ENV === 'development') {
await sequelize.sync({ alter: true });
logger.info('Database models synchronized');
}
// Start server
app.listen(PORT, () => {
logger.info(`Server running on port ${PORT}`);
logger.info(`Environment: ${process.env.NODE_ENV}`);
});
} catch (error) {
logger.error('Unable to start server:', error);
process.exit(1);
}
};
// Graceful shutdown
process.on('SIGTERM', async () => {
logger.info('SIGTERM signal received: closing HTTP server');
await sequelize.close();
process.exit(0);
});
startServer();
```
### 4.4 User Model Implementation
#### `src/models/User.ts`
```typescript
import { DataTypes, Model, Optional } from 'sequelize';
import { sequelize } from './index';
interface UserAttributes {
userId: string;
employeeId: string;
email: string;
firstName: string;
lastName: string;
displayName: string;
department?: string;
designation?: string;
phone?: string;
reportingManagerId?: string;
isActive: boolean;
isAdmin: boolean;
lastLogin?: Date;
createdAt: Date;
updatedAt: Date;
}
interface UserCreationAttributes extends Optional<UserAttributes, 'userId' | 'department' | 'designation' | 'phone' | 'reportingManagerId' | 'lastLogin' | 'createdAt' | 'updatedAt'> {}
class User extends Model<UserAttributes, UserCreationAttributes> implements UserAttributes {
public userId!: string;
public employeeId!: string;
public email!: string;
public firstName!: string;
public lastName!: string;
public displayName!: string;
public department?: string;
public designation?: string;
public phone?: string;
public reportingManagerId?: string;
public isActive!: boolean;
public isAdmin!: boolean;
public lastLogin?: Date;
public createdAt!: Date;
public updatedAt!: Date;
// Associations
public reportingManager?: User;
public subordinates?: User[];
}
User.init(
{
userId: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
employeeId: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
comment: 'HR System Employee ID'
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true
}
},
firstName: {
type: DataTypes.STRING,
allowNull: false
},
lastName: {
type: DataTypes.STRING,
allowNull: false
},
displayName: {
type: DataTypes.STRING,
allowNull: false,
comment: 'Full Name for display'
},
department: {
type: DataTypes.STRING,
allowNull: true
},
designation: {
type: DataTypes.STRING,
allowNull: true
},
phone: {
type: DataTypes.STRING,
allowNull: true
},
reportingManagerId: {
type: DataTypes.UUID,
allowNull: true,
references: {
model: 'users',
key: 'userId'
},
comment: 'Self-reference to reporting manager'
},
isActive: {
type: DataTypes.BOOLEAN,
defaultValue: true,
comment: 'Account status'
},
isAdmin: {
type: DataTypes.BOOLEAN,
defaultValue: false,
comment: 'Super user flag'
},
lastLogin: {
type: DataTypes.DATE,
allowNull: true
},
createdAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW
},
updatedAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW
}
},
{
sequelize,
modelName: 'User',
tableName: 'users',
timestamps: true,
indexes: [
{
unique: true,
fields: ['employeeId']
},
{
unique: true,
fields: ['email']
},
{
fields: ['reportingManagerId']
},
{
fields: ['department']
},
{
fields: ['isActive']
}
]
}
);
// Self-referencing association for reporting manager
User.belongsTo(User, {
as: 'reportingManager',
foreignKey: 'reportingManagerId',
targetKey: 'userId'
});
User.hasMany(User, {
as: 'subordinates',
foreignKey: 'reportingManagerId',
sourceKey: 'userId'
});
export { User };
```
#### Authentication Middleware
```typescript
// src/middlewares/auth.middleware.ts
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import { User } from '@models/User';
import { ssoConfig } from '@config/sso';
interface JwtPayload {
userId: string;
employeeId: string;
email: string;
role: string;
iat: number;
exp: number;
}
export const authenticateToken = async (
req: Request,
res: Response,
next: NextFunction
): Promise<void> => {
try {
const authHeader = req.headers.authorization;
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
if (!token) {
res.status(401).json({
success: false,
message: 'Access token is required'
});
return;
}
// Verify JWT token
const decoded = jwt.verify(token, ssoConfig.jwtSecret) as JwtPayload;
// Fetch user from database to ensure they still exist and are active
const user = await User.findByPk(decoded.userId);
if (!user || !user.isActive) {
res.status(401).json({
success: false,
message: 'User not found or inactive'
});
return;
}
// Attach user info to request object
req.user = {
userId: user.userId,
email: user.email,
employeeId: user.employeeId,
role: user.isAdmin ? 'admin' : 'user'
};
next();
} catch (error) {
if (error.name === 'TokenExpiredError') {
res.status(401).json({
success: false,
message: 'Token has expired'
});
} else if (error.name === 'JsonWebTokenError') {
res.status(401).json({
success: false,
message: 'Invalid token'
});
} else {
res.status(500).json({
success: false,
message: 'Authentication error',
error: error.message
});
}
}
};
export const requireAdmin = (
req: Request,
res: Response,
next: NextFunction
): void => {
if (req.user?.role !== 'admin') {
res.status(403).json({
success: false,
message: 'Admin access required'
});
return;
}
next();
};
```
#### Authentication Routes
```typescript
// src/routes/auth.routes.ts
import { Router } from 'express';
import { AuthController } from '@controllers/auth.controller';
import { authenticateToken } from '@middlewares/auth.middleware';
import { validateSSOCallback } from '@validators/auth.validator';
const router = Router();
const authController = new AuthController();
// SSO callback endpoint (no authentication required)
router.post('/sso-callback', authController.handleSSOCallback);
// Protected routes (require authentication)
router.get('/me', authenticateToken, authController.getCurrentUser);
router.post('/refresh', authController.refreshToken);
router.post('/logout', authenticateToken, authController.logout);
export default router;
```
### 4.5 Type Definitions
#### `src/types/express.d.ts`
```typescript
import { JwtPayload } from 'jsonwebtoken';
declare global {
namespace Express {
interface Request {
user?: {
userId: string;
email: string;
employeeId: string;
role?: string;
};
file?: Express.Multer.File;
files?: Express.Multer.File[];
}
}
}
```
#### `src/types/common.types.ts`
```typescript
export enum Priority {
STANDARD = 'STANDARD',
EXPRESS = 'EXPRESS'
}
export enum WorkflowStatus {
DRAFT = 'DRAFT',
PENDING = 'PENDING',
IN_PROGRESS = 'IN_PROGRESS',
APPROVED = 'APPROVED',
REJECTED = 'REJECTED',
CLOSED = 'CLOSED'
}
export enum ApprovalStatus {
PENDING = 'PENDING',
IN_PROGRESS = 'IN_PROGRESS',
APPROVED = 'APPROVED',
REJECTED = 'REJECTED',
SKIPPED = 'SKIPPED'
}
export enum ParticipantType {
SPECTATOR = 'SPECTATOR',
INITIATOR = 'INITIATOR',
APPROVER = 'APPROVER',
CONSULTATION = 'CONSULTATION'
}
export enum TATStatus {
ON_TRACK = 'ON_TRACK',
APPROACHING = 'APPROACHING',
BREACHED = 'BREACHED'
}
export interface ApiResponse<T = any> {
success: boolean;
message: string;
data?: T;
error?: string;
timestamp: Date;
}
export interface PaginationParams {
page: number;
limit: number;
sortBy?: string;
sortOrder?: 'ASC' | 'DESC';
}
export interface PaginatedResponse<T> {
data: T[];
pagination: {
page: number;
limit: number;
total: number;
totalPages: number;
};
}
```
---
## 5. Database Schema Design
[See Full Database Schema Section from Original Document]
The database schema includes:
- `users` - User information from SSO
- `workflow_requests` - Main workflow requests
- `approval_levels` - Approval hierarchy
- `participants` - Spectators and participants
- `documents` - Document metadata
- `work_notes` - Communication within workflow
- `activities` - Activity log
- `notifications` - User notifications
- `tat_tracking` - TAT monitoring
- `conclusion_remarks` - AI-generated conclusions
- `audit_logs` - System audit trail
- `system_settings` - Application configuration
---
## 6. API Endpoints Structure
### Base URL: `http://api.re-workflow.com/api/v1`
### 6.1 Authentication APIs
```
POST /auth/sso-callback # Frontend sends SSO user data
POST /auth/logout # Logout and invalidate session
GET /auth/me # Get current user profile
POST /auth/refresh # Refresh JWT token
GET /auth/validate # Validate JWT token
```
### 6.2 Workflow Management APIs
```
GET /workflows
POST /workflows
GET /workflows/:id
PUT /workflows/:id
DELETE /workflows/:id
PATCH /workflows/:id/submit
PATCH /workflows/:id/close
GET /workflows/my-requests
GET /workflows/open
GET /workflows/closed
GET /workflows/assigned-to-me
```
### 6.3 Approval APIs
```
GET /workflows/:id/approvals
POST /workflows/:id/approvals
PATCH /workflows/:id/approvals/:levelId/approve
PATCH /workflows/:id/approvals/:levelId/reject
GET /workflows/:id/approvals/current
```
### 6.4 Participant APIs
```
GET /workflows/:id/participants
POST /workflows/:id/participants
DELETE /workflows/:id/participants/:participantId
GET /workflows/:id/spectators
```
### 6.5 Document APIs
```
GET /workflows/:id/documents
POST /workflows/:id/documents
GET /documents/:documentId
GET /documents/:documentId/download
DELETE /documents/:documentId
GET /documents/:documentId/preview
```
### 6.6 Work Notes APIs
```
GET /workflows/:id/work-notes
POST /workflows/:id/work-notes
PUT /work-notes/:noteId
DELETE /work-notes/:noteId
POST /work-notes/:noteId/reactions
POST /work-notes/:noteId/attachments
```
### 6.7 Activity Log APIs
```
GET /workflows/:id/activities
GET /workflows/:id/activities/:type
```
### 6.8 Notification APIs
```
GET /notifications
GET /notifications/unread
PATCH /notifications/:id/read
PATCH /notifications/mark-all-read
DELETE /notifications/:id
```
### 6.9 TAT Tracking APIs
```
GET /workflows/:id/tat
GET /tat/breached
GET /tat/approaching
```
### 6.10 Dashboard APIs
```
GET /dashboard/stats
GET /dashboard/recent
GET /dashboard/pending-actions
```
### 6.11 User Management APIs
```
GET /users
GET /users/search
GET /users/:id
GET /users/:id/requests
```
### 6.12 Conclusion APIs
```
POST /workflows/:id/conclusion/generate
PUT /workflows/:id/conclusion
GET /workflows/:id/conclusion
```
---
## 7. Authentication & Security
### 7.1 SSO Integration Flow (Frontend-Initiated)
The authentication flow is handled entirely on the frontend side. The backend receives user information from the frontend after successful SSO authentication and either creates a new user record or updates an existing one.
#### SSO Flow Architecture
```
┌─────────────────┐ SSO Auth ┌─────────────────┐ User Data ┌─────────────────┐
│ Frontend │ ────────────► │ SSO Provider │ ──────────────► │ Frontend │
│ Application │ │ (RE SSO) │ │ Application │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
│ │
│ User Info + JWT Token │
│ ──────────────────────────────────────────────────────────────────────► │
│ │
▼ ▼
┌─────────────────┐ POST /api/v1/auth/sso-callback ┌─────────────────┐
│ Backend API │ ◄─────────────────────────────────── │ Frontend │
│ Server │ │ Application │
└─────────────────┘ └─────────────────┘
│ Create/Update User Record
┌─────────────────┐
│ PostgreSQL │
│ Database │
└─────────────────┘
```
#### SSO Configuration
```typescript
// src/config/sso.ts
export interface SSOUserData {
employeeId: string;
email: string;
firstName: string;
lastName: string;
displayName: string;
department?: string;
designation?: string;
phone?: string;
reportingManagerId?: string;
}
export interface SSOConfig {
jwtSecret: string;
jwtExpiry: string;
refreshTokenExpiry: string;
sessionSecret: string;
allowedOrigins: string[];
}
const ssoConfig: SSOConfig = {
jwtSecret: process.env.JWT_SECRET || '',
jwtExpiry: '24h',
refreshTokenExpiry: '7d',
sessionSecret: process.env.SESSION_SECRET || '',
allowedOrigins: process.env.CORS_ORIGIN?.split(',') || ['http://localhost:3000']
};
export default ssoConfig;
```
### 7.2 Authentication Service Implementation
#### SSO Callback Handler
```typescript
// src/services/auth.service.ts
import { User } from '@models/User';
import { SSOUserData } from '@config/sso';
import jwt from 'jsonwebtoken';
import { ssoConfig } from '@config/sso';
export class AuthService {
/**
* Handle SSO callback from frontend
* Creates new user or updates existing user based on employeeId
*/
async handleSSOCallback(userData: SSOUserData): Promise<{
user: User;
accessToken: string;
refreshToken: string;
}> {
try {
// Check if user exists by employeeId
let user = await User.findOne({
where: { employeeId: userData.employeeId }
});
if (user) {
// Update existing user
user = await user.update({
email: userData.email,
firstName: userData.firstName,
lastName: userData.lastName,
displayName: userData.displayName,
department: userData.department,
designation: userData.designation,
phone: userData.phone,
reportingManagerId: userData.reportingManagerId,
lastLogin: new Date(),
isActive: true
});
} else {
// Create new user
user = await User.create({
employeeId: userData.employeeId,
email: userData.email,
firstName: userData.firstName,
lastName: userData.lastName,
displayName: userData.displayName,
department: userData.department,
designation: userData.designation,
phone: userData.phone,
reportingManagerId: userData.reportingManagerId,
isActive: true,
isAdmin: false,
lastLogin: new Date()
});
}
// Generate JWT tokens
const accessToken = this.generateAccessToken(user);
const refreshToken = this.generateRefreshToken(user);
return {
user,
accessToken,
refreshToken
};
} catch (error) {
throw new Error(`SSO callback failed: ${error.message}`);
}
}
/**
* Generate JWT access token
*/
private generateAccessToken(user: User): string {
const payload = {
userId: user.userId,
employeeId: user.employeeId,
email: user.email,
role: user.isAdmin ? 'admin' : 'user'
};
return jwt.sign(payload, ssoConfig.jwtSecret, {
expiresIn: ssoConfig.jwtExpiry
});
}
/**
* Generate JWT refresh token
*/
private generateRefreshToken(user: User): string {
const payload = {
userId: user.userId,
type: 'refresh'
};
return jwt.sign(payload, ssoConfig.jwtSecret, {
expiresIn: ssoConfig.refreshTokenExpiry
});
}
/**
* Validate JWT token
*/
async validateToken(token: string): Promise<any> {
try {
return jwt.verify(token, ssoConfig.jwtSecret);
} catch (error) {
throw new Error('Invalid token');
}
}
/**
* Refresh access token using refresh token
*/
async refreshAccessToken(refreshToken: string): Promise<string> {
try {
const decoded = jwt.verify(refreshToken, ssoConfig.jwtSecret) as any;
if (decoded.type !== 'refresh') {
throw new Error('Invalid refresh token');
}
const user = await User.findByPk(decoded.userId);
if (!user || !user.isActive) {
throw new Error('User not found or inactive');
}
return this.generateAccessToken(user);
} catch (error) {
throw new Error('Token refresh failed');
}
}
}
```
#### SSO Callback Controller
```typescript
// src/controllers/auth.controller.ts
import { Request, Response } from 'express';
import { AuthService } from '@services/auth.service';
import { SSOUserData } from '@config/sso';
import { validateSSOCallback } from '@validators/auth.validator';
export class AuthController {
private authService: AuthService;
constructor() {
this.authService = new AuthService();
}
/**
* Handle SSO callback from frontend
* POST /api/v1/auth/sso-callback
*/
async handleSSOCallback(req: Request, res: Response): Promise<void> {
try {
// Validate request body
const validatedData = validateSSOCallback(req.body);
const result = await this.authService.handleSSOCallback(validatedData);
res.status(200).json({
success: true,
message: 'Authentication successful',
data: {
user: {
userId: result.user.userId,
employeeId: result.user.employeeId,
email: result.user.email,
firstName: result.user.firstName,
lastName: result.user.lastName,
displayName: result.user.displayName,
department: result.user.department,
designation: result.user.designation,
isAdmin: result.user.isAdmin
},
accessToken: result.accessToken,
refreshToken: result.refreshToken
}
});
} catch (error) {
res.status(400).json({
success: false,
message: 'Authentication failed',
error: error.message
});
}
}
/**
* Get current user profile
* GET /api/v1/auth/me
*/
async getCurrentUser(req: Request, res: Response): Promise<void> {
try {
const user = req.user;
res.status(200).json({
success: true,
data: {
userId: user.userId,
employeeId: user.employeeId,
email: user.email,
firstName: user.firstName,
lastName: user.lastName,
displayName: user.displayName,
department: user.department,
designation: user.designation,
isAdmin: user.isAdmin,
lastLogin: user.lastLogin
}
});
} catch (error) {
res.status(500).json({
success: false,
message: 'Failed to get user profile',
error: error.message
});
}
}
/**
* Refresh access token
* POST /api/v1/auth/refresh
*/
async refreshToken(req: Request, res: Response): Promise<void> {
try {
const { refreshToken } = req.body;
if (!refreshToken) {
res.status(400).json({
success: false,
message: 'Refresh token is required'
});
return;
}
const newAccessToken = await this.authService.refreshAccessToken(refreshToken);
res.status(200).json({
success: true,
data: {
accessToken: newAccessToken
}
});
} catch (error) {
res.status(401).json({
success: false,
message: 'Token refresh failed',
error: error.message
});
}
}
/**
* Logout user
* POST /api/v1/auth/logout
*/
async logout(req: Request, res: Response): Promise<void> {
try {
// In a more sophisticated implementation, you might want to:
// 1. Add the token to a blacklist
// 2. Update user's last logout time
// 3. Clear any server-side sessions
res.status(200).json({
success: true,
message: 'Logout successful'
});
} catch (error) {
res.status(500).json({
success: false,
message: 'Logout failed',
error: error.message
});
}
}
}
```
#### SSO Validation Schema
```typescript
// src/validators/auth.validator.ts
import { z } from 'zod';
export const ssoCallbackSchema = z.object({
employeeId: z.string().min(1, 'Employee ID is required'),
email: z.string().email('Valid email is required'),
firstName: z.string().min(1, 'First name is required'),
lastName: z.string().min(1, 'Last name is required'),
displayName: z.string().min(1, 'Display name is required'),
department: z.string().optional(),
designation: z.string().optional(),
phone: z.string().optional(),
reportingManagerId: z.string().uuid().optional()
});
export const validateSSOCallback = (data: any) => {
return ssoCallbackSchema.parse(data);
};
```
### 7.3 Security Measures
| Security Layer | Implementation |
|----------------|----------------|
| **Transport Security** | HTTPS/TLS 1.3 |
| **Authentication** | Frontend SSO + JWT tokens |
| **Authorization** | Role-based access control (RBAC) |
| **Token Storage** | HTTP-only cookies + Local storage |
| **CSRF Protection** | CSRF tokens on state-changing operations |
| **XSS Protection** | Input sanitization + Content Security Policy |
| **SQL Injection** | Parameterized queries (Sequelize ORM) |
| **Rate Limiting** | Express rate limiter (100 req/15min) |
| **File Upload Security** | Type validation, size limits, virus scanning |
| **Audit Logging** | All actions logged with user context |
| **Data Encryption** | At-rest (database) and in-transit (TLS) |
| **User Data Validation** | Zod schema validation for SSO user data |
| **Session Management** | JWT with refresh token rotation |
---
## 8. Configuration Management
### 8.1 Environment Variables
```bash
# backend/.env.example
# Application
NODE_ENV=development
PORT=5000
API_VERSION=v1
BASE_URL=http://localhost:5000
# Database
DB_HOST=localhost
DB_PORT=5432
DB_NAME=re_workflow_db
DB_USER=postgres
DB_PASSWORD=postgres
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=your_jwt_secret_key_here_min_32_chars
JWT_EXPIRY=24h
REFRESH_TOKEN_SECRET=your_refresh_token_secret_here
REFRESH_TOKEN_EXPIRY=7d
# Session
SESSION_SECRET=your_session_secret_here_min_32_chars
# Cloud Storage (GCP)
GCP_PROJECT_ID=re-workflow-project
GCP_BUCKET_NAME=re-workflow-documents
GCP_KEY_FILE=./config/gcp-key.json
# Email Service (Optional)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_SECURE=false
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
# Logging
LOG_LEVEL=info
LOG_FILE_PATH=./logs
# CORS
CORS_ORIGIN=http://localhost:3000
# 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
```
---
## 9. Deployment Architecture
### 9.1 Docker Compose for Local Development
```yaml
# docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:16-alpine
container_name: re_workflow_db
environment:
POSTGRES_USER: ${DB_USER:-workflow_user}
POSTGRES_PASSWORD: ${DB_PASSWORD:-secure_password}
POSTGRES_DB: ${DB_NAME:-re_workflow_db}
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
- ./database/schema:/docker-entrypoint-initdb.d
networks:
- re_workflow_network
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-workflow_user}"]
interval: 10s
timeout: 5s
retries: 5
backend:
build:
context: .
dockerfile: Dockerfile
container_name: re_workflow_backend
environment:
NODE_ENV: development
DB_HOST: postgres
DB_PORT: 5432
DB_USER: ${DB_USER:-workflow_user}
DB_PASSWORD: ${DB_PASSWORD:-secure_password}
DB_NAME: ${DB_NAME:-re_workflow_db}
PORT: 5000
ports:
- "5000:5000"
depends_on:
postgres:
condition: service_healthy
volumes:
- ./logs:/app/logs
- ./uploads:/app/uploads
networks:
- re_workflow_network
restart: unless-stopped
volumes:
postgres_data:
networks:
re_workflow_network:
driver: bridge
```
### 9.2 Dockerfile
```dockerfile
# Dockerfile
FROM node:22-alpine AS builder
WORKDIR /app
# Copy package files
COPY package*.json ./
COPY tsconfig.json ./
# Install all dependencies (including devDependencies for build)
RUN npm ci
# Copy source code
COPY src ./src
# Build TypeScript to JavaScript
RUN npm run build
# =====================================
# Production Image
# =====================================
FROM node:22-alpine
WORKDIR /app
# Install PM2 globally
RUN npm install -g pm2
# Copy package files
COPY package*.json ./
# Install only production dependencies
RUN npm ci --only=production
# Copy compiled JavaScript from builder
COPY --from=builder /app/dist ./dist
# Create logs and uploads directories
RUN mkdir -p logs uploads
# Expose port
EXPOSE 5000
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD node -e "require('http').get('http://localhost:5000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
# Start with PM2
CMD ["pm2-runtime", "start", "dist/server.js", "--name", "re-workflow-api"]
```
### 9.3 CI/CD Pipeline (GitHub Actions)
```yaml
# .github/workflows/backend-deploy.yml
name: Backend CI/CD
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test-and-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run tests
run: npm test
- name: Build TypeScript
run: npm run build
- name: Build Docker image
run: docker build -t gcr.io/re-project/re-workflow-backend:${{ github.sha }} .
- name: Push to GCR
run: docker push gcr.io/re-project/re-workflow-backend:${{ github.sha }}
```
---
## 10. Development Setup Instructions
### 10.1 Prerequisites
- **Node.js:** v22.x LTS
- **PostgreSQL:** v16.x
- **npm:** v10.x or higher
- **TypeScript:** v5.7.x (installed globally or as dev dependency)
- **Git:** Latest version
- **Docker & Docker Compose** (Optional)
- **GCP Account** (for cloud storage in production)
### 10.2 Local Development Setup
#### Step 1: Clone Repository
```bash
git clone https://github.com/royalenfield/re-workflow-backend.git
cd re-workflow-backend
```
#### Step 2: Setup Database
```bash
# Install PostgreSQL (if not installed)
# For Ubuntu/Debian:
# sudo apt-get install postgresql-16
# For macOS:
# brew install postgresql@16
# Create database
createdb re_workflow_db
# Or using psql:
psql -U postgres
CREATE DATABASE re_workflow_db;
\q
# Run schema
psql -U postgres -d re_workflow_db -f database/schema/schema.sql
```
#### Step 3: Configure Environment
```bash
# Copy environment file
cp .env.example .env
# Edit .env with your configurations
nano .env # or use your preferred editor
# Required variables:
# - Database credentials
# - JWT secrets
# - SSO configuration
# - GCP storage keys
```
#### Step 4: Install Dependencies & Run
```bash
# Install dependencies
npm install
# Run TypeScript type checking
npm run type-check
# Run migrations (if using Sequelize CLI)
npx sequelize-cli db:migrate
# Seed data (optional)
npx sequelize-cli db:seed:all
# Start development server (with hot reload)
npm run dev
# Backend will run on: http://localhost:5000
# API Documentation: http://localhost:5000/api-docs
```
### 10.3 Docker Setup
```bash
# From backend repository root
cd re-workflow-backend
# Copy environment file
cp .env.example .env
# Edit .env if needed
nano .env
# Build and start services (backend + PostgreSQL)
docker-compose up --build -d
# Check logs
docker-compose logs -f
# Access backend: http://localhost:5000
# Stop services
docker-compose down
# Stop and remove volumes (clean state)
docker-compose down -v
```
### 10.4 Running Tests
```bash
# From backend repository
cd re-workflow-backend
npm test # Run all tests (Jest + ts-jest)
npm run test:unit # Unit tests only
npm run test:integration # Integration tests only
npm run test:watch # Watch mode for development
npm run test:coverage # With coverage report
# Coverage report will be in: coverage/
# View HTML report: open coverage/lcov-report/index.html
```
### 10.5 Code Quality Checks
```bash
# From backend repository
cd re-workflow-backend
npm run lint # ESLint check (TypeScript rules)
npm run lint:fix # Auto-fix issues
npm run format # Prettier formatting
npm run type-check # TypeScript type checking only (no compilation)
# Run all quality checks together
npm run lint && npm run type-check && npm test
```
---
## 11. Package.json
```json
{
"name": "re-workflow-backend",
"version": "1.0.0",
"description": "Royal Enfield Workflow Management System - Backend API (TypeScript)",
"main": "dist/server.js",
"scripts": {
"start": "node dist/server.js",
"dev": "nodemon --exec ts-node src/server.ts",
"build": "tsc",
"build:watch": "tsc --watch",
"start:prod": "NODE_ENV=production node dist/server.js",
"test": "jest --coverage",
"test:unit": "jest --testPathPattern=tests/unit",
"test:integration": "jest --testPathPattern=tests/integration",
"test:watch": "jest --watch",
"lint": "eslint src/**/*.ts",
"lint:fix": "eslint src/**/*.ts --fix",
"format": "prettier --write \"src/**/*.ts\"",
"type-check": "tsc --noEmit",
"db:migrate": "sequelize-cli db:migrate",
"db:migrate:undo": "sequelize-cli db:migrate:undo",
"db:seed": "sequelize-cli db:seed:all",
"clean": "rm -rf dist"
},
"dependencies": {
"express": "^4.21.2",
"pg": "^8.13.1",
"pg-hstore": "^2.3.4",
"sequelize": "^6.37.5",
"bcryptjs": "^2.4.3",
"jsonwebtoken": "^9.0.2",
"passport": "^0.7.0",
"passport-jwt": "^4.0.1",
"zod": "^3.24.1",
"multer": "^1.4.5-lts.1",
"winston": "^3.17.0",
"cors": "^2.8.5",
"helmet": "^8.0.0",
"dotenv": "^16.4.7",
"express-rate-limit": "^7.5.0",
"morgan": "^1.10.0",
"@google-cloud/storage": "^7.14.0",
"axios": "^1.7.9",
"node-cron": "^3.0.3"
},
"devDependencies": {
"@types/express": "^5.0.0",
"@types/node": "^22.10.5",
"@types/bcryptjs": "^2.4.6",
"@types/jsonwebtoken": "^9.0.7",
"@types/passport": "^1.0.16",
"@types/passport-jwt": "^4.0.1",
"@types/multer": "^1.4.12",
"@types/cors": "^2.8.17",
"@types/morgan": "^1.9.9",
"@types/jest": "^29.5.14",
"@types/supertest": "^6.0.2",
"typescript": "^5.7.2",
"ts-node": "^10.9.2",
"ts-node-dev": "^2.0.0",
"nodemon": "^3.1.9",
"jest": "^29.7.0",
"ts-jest": "^29.2.5",
"supertest": "^7.0.0",
"eslint": "^9.17.0",
"@typescript-eslint/eslint-plugin": "^8.19.1",
"@typescript-eslint/parser": "^8.19.1",
"prettier": "^3.4.2",
"sequelize-cli": "^6.6.2",
"sequelize-typescript": "^2.1.7"
},
"engines": {
"node": ">=22.0.0",
"npm": ">=10.0.0"
}
}
```
---
## Summary
This backend documentation provides:
**Complete Backend Architecture** - Server structure, layers, and components
**Technology Stack** - Node.js 22 LTS + TypeScript 5.7 + PostgreSQL 16
**Folder Structure** - Detailed organization with TypeScript conventions
**Type Definitions** - Comprehensive type safety with interfaces and enums
**Database Schema** - Full PostgreSQL schema with tables, indexes, triggers
**API Specification** - RESTful endpoints for all features
**Frontend SSO Integration** - Frontend-handled authentication with backend user management
**User Management** - Create/update users based on SSO data from frontend
**JWT Authentication** - Token-based authentication with refresh token support
**Security Implementation** - JWT validation, RBAC, audit trails
**Configuration Management** - Environment variables and settings
**Deployment Architecture** - Docker, docker-compose, CI/CD pipelines
**Development Setup** - Step-by-step installation and configuration
**Testing Strategy** - Unit and integration testing with ts-jest
**Code Quality** - ESLint, Prettier, TypeScript best practices
**Technology Stack:** Node.js 22 LTS + TypeScript 5.7 + Express.js 4.21 + PostgreSQL 16
**Authentication Flow:** Frontend SSO → Backend User Creation/Update → JWT Token Generation
**Repository:** `re-workflow-backend` (Independent Repository)
**Status:** ✅ Ready for Implementation