Support public class lookup, user provisioning, installment pricing, and admin approval before final enrollment.
148 lines
6.0 KiB
JavaScript
148 lines
6.0 KiB
JavaScript
// /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 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);
|
|
const pendingStudentRoutes = require('./components/pendingStudents/pendingStudentRoutes');
|
|
app.use('/api/pending-students', pendingStudentRoutes);
|
|
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;
|