185 lines
5.2 KiB
TypeScript
185 lines
5.2 KiB
TypeScript
import express from 'express';
|
|
import helmet from 'helmet';
|
|
import morgan from 'morgan';
|
|
import dotenv from 'dotenv';
|
|
import cookieParser from 'cookie-parser';
|
|
import { UserService } from './services/user.service';
|
|
import { SSOUserData } from './types/auth.types';
|
|
import { sequelize } from './config/database';
|
|
import { corsMiddleware } from './middlewares/cors.middleware';
|
|
import routes from './routes/index';
|
|
import { ensureUploadDir, UPLOAD_DIR } from './config/storage';
|
|
import path from 'path';
|
|
|
|
// Load environment variables
|
|
dotenv.config();
|
|
|
|
const app: express.Application = express();
|
|
const userService = new UserService();
|
|
|
|
// Initialize database connection
|
|
const initializeDatabase = async () => {
|
|
try {
|
|
await sequelize.authenticate();
|
|
} catch (error) {
|
|
console.error('❌ Database connection failed:', error);
|
|
}
|
|
};
|
|
|
|
// Initialize database
|
|
initializeDatabase();
|
|
|
|
// CORS middleware - MUST be before other middleware
|
|
app.use(corsMiddleware);
|
|
|
|
// Security middleware - Configure Helmet to work with CORS
|
|
app.use(helmet({
|
|
crossOriginEmbedderPolicy: false,
|
|
crossOriginResourcePolicy: { policy: "cross-origin" },
|
|
contentSecurityPolicy: {
|
|
directives: {
|
|
defaultSrc: ["'self'"],
|
|
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
scriptSrc: ["'self'"],
|
|
imgSrc: ["'self'", "data:", "https:"],
|
|
},
|
|
},
|
|
}));
|
|
|
|
// Cookie parser middleware - MUST be before routes
|
|
app.use(cookieParser());
|
|
|
|
// Body parsing middleware
|
|
app.use(express.json({ limit: '10mb' }));
|
|
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
|
|
|
|
// Logging middleware
|
|
app.use(morgan('combined'));
|
|
|
|
// Health check endpoint
|
|
app.get('/health', (_req: express.Request, res: express.Response) => {
|
|
res.status(200).json({
|
|
status: 'OK',
|
|
timestamp: new Date(),
|
|
uptime: process.uptime(),
|
|
environment: process.env.NODE_ENV || 'development'
|
|
});
|
|
});
|
|
|
|
// Mount API routes
|
|
app.use('/api/v1', routes);
|
|
|
|
// Serve uploaded files statically
|
|
ensureUploadDir();
|
|
app.use('/uploads', express.static(UPLOAD_DIR));
|
|
|
|
// Root endpoint
|
|
app.get('/', (_req: express.Request, res: express.Response) => {
|
|
res.status(200).json({
|
|
message: 'Royal Enfield Workflow Management System API',
|
|
version: '1.0.0',
|
|
status: 'running',
|
|
timestamp: new Date()
|
|
});
|
|
});
|
|
|
|
// Legacy SSO Callback endpoint for user creation/update (kept for backward compatibility)
|
|
app.post('/api/v1/auth/sso-callback', async (req: express.Request, res: express.Response): Promise<void> => {
|
|
try {
|
|
const ssoData: SSOUserData = req.body;
|
|
|
|
// Validate required fields - email and oktaSub are required
|
|
if (!ssoData.email || !ssoData.oktaSub) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: 'Missing required fields: email and oktaSub are required',
|
|
timestamp: new Date()
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Create or update user
|
|
const user = await userService.createOrUpdateUser(ssoData);
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
message: 'User processed successfully',
|
|
data: {
|
|
user: {
|
|
userId: user.userId,
|
|
employeeId: user.employeeId || null,
|
|
oktaSub: user.oktaSub,
|
|
email: user.email,
|
|
firstName: user.firstName || null,
|
|
lastName: user.lastName || null,
|
|
displayName: user.displayName || null,
|
|
department: user.department || null,
|
|
designation: user.designation || null,
|
|
phone: user.phone || null,
|
|
location: user.location || null,
|
|
isAdmin: user.isAdmin,
|
|
lastLogin: user.lastLogin
|
|
},
|
|
isNewUser: user.createdAt.getTime() === user.updatedAt.getTime()
|
|
},
|
|
timestamp: new Date()
|
|
});
|
|
} catch (error) {
|
|
console.error('❌ SSO Callback failed:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: 'Internal server error',
|
|
timestamp: new Date()
|
|
});
|
|
}
|
|
});
|
|
|
|
// Get all users endpoint
|
|
app.get('/api/v1/users', async (_req: express.Request, res: express.Response): Promise<void> => {
|
|
try {
|
|
const users = await userService.getAllUsers();
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
message: 'Users retrieved successfully',
|
|
data: {
|
|
users: users.map(user => ({
|
|
userId: user.userId,
|
|
employeeId: user.employeeId || null,
|
|
oktaSub: user.oktaSub,
|
|
email: user.email,
|
|
firstName: user.firstName || null,
|
|
lastName: user.lastName || null,
|
|
displayName: user.displayName || null,
|
|
department: user.department || null,
|
|
designation: user.designation || null,
|
|
phone: user.phone || null,
|
|
location: user.location || null,
|
|
isAdmin: user.isAdmin,
|
|
lastLogin: user.lastLogin,
|
|
createdAt: user.createdAt
|
|
})),
|
|
total: users.length
|
|
},
|
|
timestamp: new Date()
|
|
});
|
|
} catch (error) {
|
|
console.error('❌ Get Users failed:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: 'Internal server error',
|
|
timestamp: new Date()
|
|
});
|
|
}
|
|
});
|
|
|
|
// Error handling middleware
|
|
app.use((req: express.Request, res: express.Response) => {
|
|
res.status(404).json({
|
|
success: false,
|
|
message: `Route ${req.originalUrl} not found`,
|
|
timestamp: new Date(),
|
|
});
|
|
});
|
|
|
|
export default app; |