import express, { Request, Response } from "express"; import cookieParser from "cookie-parser"; import cors from "cors"; import connectDB from "./util/database.util"; import userRoutes from "./router/user.router"; import locationRoutes from "./router/locationHistory.router"; import analyticsRoutes from './router/analytics.router'; import activityRoutes from './router/activity.router'; import eventRoutes from './router/event.router'; import eventDayRoutes from './router/eventDay.router'; import formRoutes from './router/form.router'; import sachetRoutes from './router/sachet.router'; import { API_PREFIX, PORT } from "./config/env.config"; import { initializeRedis } from './util/redis.util'; import cluster from 'cluster'; import os from 'os'; const app = express(); // ============================================================================ // MIDDLEWARE // ============================================================================ app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(cookieParser()); const allowedOrigins = [ "http://localhost:5173", "https://jpglj93t-3000.inc1.devtunnels.ms", "https://jpglj93t-5173.inc1.devtunnels.ms" ]; app.use( cors({ origin: function (origin, callback) { if (!origin || allowedOrigins.includes(origin)) { callback(null, origin); } else { callback(new Error("Not allowed by CORS")); } }, credentials: true, }) ); // Trust proxy for load balancers app.set('trust proxy', 1); // ============================================================================ // HEALTH CHECK - Required for load balancers // ============================================================================ app.get(`${API_PREFIX}/health`, (req: Request, res: Response): Response => { return res.status(200).json({ message: "I eat apple everyday :)", success: true, instance: process.pid, uptime: process.uptime() }); }); // Readiness check for K8s/load balancers app.get(`${API_PREFIX}/ready`, async (req: Request, res: Response) => { try { // Check database connection const mongoose = await import('mongoose'); if (mongoose.connection.readyState !== 1) { return res.status(503).json({ ready: false, reason: 'Database not ready' }); } // Check Redis connection const redis = await import('./util/redis.util'); const redisClient = await redis.getRedisClient(); if (!redisClient.isReady) { return res.status(503).json({ ready: false, reason: 'Redis not ready' }); } return res.status(200).json({ ready: true, instance: process.pid }); } catch (error) { return res.status(503).json({ ready: false, error: String(error) }); } }); // ============================================================================ // API ROUTES // ============================================================================ app.use(`${API_PREFIX}/users`, userRoutes); app.use(`${API_PREFIX}/location`, locationRoutes); app.use(`${API_PREFIX}/events`, eventRoutes); app.use(`${API_PREFIX}/event-days`, eventDayRoutes); app.use(`${API_PREFIX}/analytics`, analyticsRoutes); app.use(`${API_PREFIX}/activity`, activityRoutes); app.use(`${API_PREFIX}/forms`, formRoutes); app.use(`${API_PREFIX}/sachet`, sachetRoutes); // ============================================================================ // CLUSTERING FOR MULTI-CORE USAGE // ============================================================================ if (cluster.isPrimary && process.env.NODE_ENV === 'production') { const numCPUs = os.cpus().length; console.log(`Primary ${process.pid} is running`); console.log(`Forking ${numCPUs} workers...`); for (let i = 0; i < numCPUs; i++) { cluster.fork(); } cluster.on('exit', (worker, code, signal) => { console.log(`Worker ${worker.process.pid} died. Forking new worker...`); cluster.fork(); }); } else { // Worker processes startServer(); } async function startServer() { try { await connectDB(); await initializeRedis(); app.listen(PORT, () => { console.log(`Worker ${process.pid} running on port ${PORT}`); }); } catch (error) { console.error('Failed to start server:', error); process.exit(1); } }