Files
gameno-api/app.js
T
kavehhn cd98adff84 feat: add pending student registration flow and pricing APIs.
Support public class lookup, user provisioning, installment pricing, and admin approval before final enrollment.
2026-08-16 08:42:25 +03:30

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;