File size: 4,256 Bytes
9a92a42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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);
  }
}