Move sms.ir template IDs out of env into settings so SuperAdmin can manage them, and record every outbound SMS status in notifications.
146 lines
5.9 KiB
JavaScript
146 lines
5.9 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);
|
|
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;
|