// /app.js — Main Express Application Entry Point 'use strict'; const express = require('express'); const cors = require('cors'); const helmet = require('helmet'); const rateLimit = require('express-rate-limit'); const mongoose = require('mongoose'); const config = require('./config/config'); const connectDB = require('./config/db'); const logger = require('./utils/logger'); const notFoundHandler = require('./middlewares/notFoundHandler'); const globalErrorHandler = require('./middlewares/globalErrorHandler'); // ── Route imports ───────────────────────────────────────────────────────────── const authRoutes = require('./components/auth/authRoutes'); const userRoutes = require('./components/users/userRoutes'); const professorRoutes = require('./components/professors/professorRoutes'); const courseRoutes = require('./components/courses/courseRoutes'); const classRoutes = require('./components/classes/classRoutes'); const sessionRoutes = require('./components/sessions/sessionRoutes'); const paymentRoutes = require('./components/payments/paymentRoutes'); const roleRoutes = require('./components/roles/roleRoutes'); const notificationRoutes = require('./components/notifications/notificationRoutes'); const certificateRoutes = require('./components/certificates/certificateRoutes'); const documentRoutes = require('./components/documents/documentRoutes'); const fileRoutes = require('./components/files/fileRoutes'); const dashboardRoutes = require('./components/dashboard/dashboardRoutes'); const activityLogRoutes = require('./components/activityLogs/activityLogRoutes'); const contactInquiryRoutes = require('./components/contactInquiries/contactInquiryRoutes'); const expenseRoutes = require('./components/expenses/expenseRoutes'); const financialReportRoutes = require('./components/financialReports/financialReportRoutes'); const activityLogger = require('./middlewares/activityLogger'); const app = express(); // ── Security & Middleware ───────────────────────────────────────────────────── app.use(helmet()); app.use(cors({ origin: config.CORS_ORIGIN, credentials: true })); app.use(express.json({ limit: '10mb' })); app.use(express.urlencoded({ extended: true, limit: '10mb' })); app.use(activityLogger); // Global rate limiter const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 300, standardHeaders: true, legacyHeaders: false, message: { status: 'error', message: 'Too many requests. Please try again later.' } }); app.use('/api', limiter); // ── Routes ──────────────────────────────────────────────────────────────────── app.get('/api/health', (req, res) => { const dbStates = { 0: 'disconnected', 1: 'connected', 2: 'connecting', 3: 'disconnecting', 99: 'uninitialized' }; const dbStateCode = mongoose.connection.readyState; const dbStatus = dbStates[dbStateCode] || 'unknown'; const isHealthy = dbStateCode === 1; res.status(isHealthy ? 200 : 503).json({ status: isHealthy ? 'healthy' : 'unhealthy', timestamp: new Date().toISOString(), uptime: process.uptime(), services: { database: dbStatus }, environment: config.NODE_ENV }); }); const seedDatabase = require('./seed'); const seedRoutes = require('./components/seed/seedRoutes'); const dataImportRoutes = require('./components/dataImport/dataImportRoutes'); const settingRoutes = require('./components/settings/settingRoutes'); app.use('/api/auth', authRoutes); app.use('/api/users', userRoutes); app.use('/api/professors', professorRoutes); app.use('/api/courses', courseRoutes); app.use('/api/classes', classRoutes); app.use('/api/sessions', sessionRoutes); app.use('/api/payments', paymentRoutes); app.use('/api/roles', roleRoutes); app.use('/api/notifications', notificationRoutes); app.use('/api/certificates', certificateRoutes); app.use('/api/documents', documentRoutes); app.use('/api/files', fileRoutes); app.use('/api/dashboard', dashboardRoutes); app.use('/api/activity-logs', activityLogRoutes); app.use('/api/contact-inquiries', contactInquiryRoutes); app.use('/api/expenses', expenseRoutes); app.use('/api/financial-reports', financialReportRoutes); const pendingStudentRoutes = require('./components/pendingStudents/pendingStudentRoutes'); const waitlistRoutes = require('./components/waitlist/waitlistRoutes'); app.use('/api/pending-students', pendingStudentRoutes); app.use('/api/waitlist', waitlistRoutes); const employeeTimingRoutes = require('./components/employeeTimings/employeeTimingRoutes'); app.use('/api/employee-timings', employeeTimingRoutes); app.use('/api/seed', seedRoutes); app.use('/api/data-import', dataImportRoutes); app.use('/api/settings', settingRoutes); // ── Error Handlers ──────────────────────────────────────────────────────────── app.use(notFoundHandler); app.use(globalErrorHandler); // ── Server startup ──────────────────────────────────────────────────────────── const PORT = config.PORT || 3000; const startServer = async () => { await connectDB(); if (config.NODE_ENV === 'production') { logger.info('Running production bootstrap (roles + SuperAdmin from env)'); await seedDatabase({ disconnectOnComplete: false }); } const { ensureAppBuckets } = require('./utils/s3Client'); await ensureAppBuckets(); const registerEventListeners = require('./events/eventListeners'); const { startPaymentReminderJob } = require('./jobs/paymentReminderJob'); const { startNotificationRetryJob } = require('./jobs/notificationRetryJob'); const { startTempBucketCleanupJob } = require('./jobs/tempBucketCleanupJob'); const { startClassReminderJob } = require('./jobs/classReminderJob'); registerEventListeners(); startPaymentReminderJob(); startNotificationRetryJob(); startTempBucketCleanupJob(); startClassReminderJob(); app.listen(PORT, () => { logger.info(`Gameno API listening on http://localhost:${PORT} [${config.NODE_ENV}]`); }); }; startServer().catch(err => { logger.error('Failed to start server:', err); process.exit(1); }); module.exports = app;