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

48 KiB

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
  2. Technology Stack
  3. Project Folder Structure
  4. Database Schema Design
  5. API Endpoints Structure
  6. Authentication & Security
  7. Configuration Management
  8. Deployment Architecture
  9. 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

{
  "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

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

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

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

// 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

// 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

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

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

// 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

// 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

// 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

// 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

# 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

# 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

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)

# .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

git clone https://github.com/royalenfield/re-workflow-backend.git
cd re-workflow-backend

Step 2: Setup Database

# 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

# 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

# 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

# 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

# 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

# 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

{
  "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