require('dotenv').config(); const express = require('express'); const cors = require('cors'); const helmet = require('helmet'); const compression = require('compression'); const rateLimit = require('express-rate-limit'); const path = require('path'); // Import database const db = require('./src/database/models'); // Import routes (Modular Monolith Structure) const authRoutes = require('./src/modules/auth/auth.routes'); const onboardingRoutes = require('./src/modules/onboarding/onboarding.routes'); const selfServiceRoutes = require('./src/modules/self-service/self-service.routes'); const masterRoutes = require('./src/modules/master/master.routes'); const settlementRoutes = require('./src/modules/settlement/settlement.routes'); const collaborationRoutes = require('./src/modules/collaboration/collaboration.routes'); // Import common middleware & utils const errorHandler = require('./src/common/middleware/errorHandler'); const logger = require('./src/common/utils/logger'); // Initialize Express app const app = express(); // Security middleware app.use(helmet()); app.use(cors({ origin: process.env.FRONTEND_URL || 'http://localhost:5173', credentials: true })); // Rate limiting const limiter = rateLimit({ windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS) || 15 * 60 * 1000, // 15 minutes max: parseInt(process.env.RATE_LIMIT_MAX_REQUESTS) || 100, message: 'Too many requests from this IP, please try again later.' }); app.use('/api/', limiter); // Body parsing middleware app.use(express.json({ limit: '10mb' })); app.use(express.urlencoded({ extended: true, limit: '10mb' })); // Compression app.use(compression()); // Static files (uploaded documents) app.use('/uploads', express.static(path.join(__dirname, 'uploads'))); // Request logging app.use((req, res, next) => { logger.info(`${req.method} ${req.path}`, { ip: req.ip, userAgent: req.get('user-agent') }); next(); }); // Health check endpoint app.get('/health', (req, res) => { res.json({ status: 'OK', timestamp: new Date().toISOString(), uptime: process.uptime(), environment: process.env.NODE_ENV }); }); // API Routes (Modular) app.use('/api/auth', authRoutes); app.use('/api/onboarding', onboardingRoutes); app.use('/api/self-service', selfServiceRoutes); app.use('/api/master', masterRoutes); app.use('/api/settlement', settlementRoutes); app.use('/api/collaboration', collaborationRoutes); // Backward Compatibility Aliases app.use('/api/applications', onboardingRoutes); app.use('/api/resignations', require('./src/modules/self-service/resignation.routes')); app.use('/api/constitutional', (req, res, next) => { // Map /api/constitutional to /api/self-service/constitutional req.url = '/constitutional' + req.url; next(); }, selfServiceRoutes); app.use('/api/relocations', (req, res, next) => { // Map /api/relocations to /api/self-service/relocation req.url = '/relocation' + req.url; next(); }, selfServiceRoutes); app.use('/api/outlets', require('./src/modules/master/outlet.routes')); app.use('/api/finance', settlementRoutes); app.use('/api/worknotes', collaborationRoutes); // 404 handler app.use((req, res) => { res.status(404).json({ success: false, message: 'Route not found' }); }); // Global error handler app.use(errorHandler); // Database connection and server start const PORT = process.env.PORT || 5000; const startServer = async () => { try { // Test database connection await db.sequelize.authenticate(); logger.info('Database connection established successfully'); // Sync database (in development only) if (process.env.NODE_ENV === 'development') { await db.sequelize.sync({ alter: false }); logger.info('Database models synchronized'); } // Start server app.listen(PORT, () => { logger.info(`🚀 Server running on port ${PORT}`); logger.info(`📍 Environment: ${process.env.NODE_ENV}`); logger.info(`🔗 API Base URL: http://localhost:${PORT}/api`); }); } catch (error) { logger.error('Unable to start server:', error); process.exit(1); } }; // Handle unhandled promise rejections process.on('unhandledRejection', (err) => { logger.error('Unhandled Promise Rejection:', err); process.exit(1); }); // Handle uncaught exceptions process.on('uncaughtException', (err) => { logger.error('Uncaught Exception:', err); process.exit(1); }); // Graceful shutdown process.on('SIGTERM', async () => { logger.info('SIGTERM signal received: closing HTTP server'); await db.sequelize.close(); process.exit(0); }); startServer(); module.exports = app;