import express from "express"; import cors from "cors"; import routes from "./routes"; import { errorHandler } from "./middlewares/error.middleware"; import { apiLogger } from "./middlewares/logger.middleware"; import authRoutes from "./routes/auth"; import twilioWebhookRoutes from "./routes/twilio-webhooks"; import { authenticateJWT } from "./middlewares/auth.middleware"; import dotenv from "dotenv"; import { startBackupCron } from "./cron/backupCheck"; import path from "path"; dotenv.config(); const NODE_ENV = ( process.env.NODE_ENV || process.env.ENV || "development" ).toLowerCase(); const app = express(); app.use(express.json()); app.use(express.urlencoded({ extended: true })); // For form data app.use(apiLogger); // --- CORS handling (flexible for dev and strict for prod) --- /** * FRONTEND_URLS env value: comma-separated allowed origins * Example: FRONTEND_URLS=http://localhost:3000,http://192.168.1.8:3000 */ const rawFrontendUrls = process.env.FRONTEND_URLS || process.env.FRONTEND_URL || ""; const FRONTEND_URLS = rawFrontendUrls .split(",") .map((s) => s.trim()) .filter(Boolean); // helper to see if origin is allowed function isOriginAllowed(origin?: string | null) { if (!origin) return true; // allow non-browser clients (curl/postman) if (NODE_ENV !== "production") { // Dev mode: allow localhost and any private LAN range if ( origin.startsWith("http://localhost") || origin.startsWith("http://127.0.0.1") || /^https?:\/\/10\.\d+\.\d+\.\d+/.test(origin) || /^https?:\/\/172\.(1[6-9]|2\d|3[01])\.\d+\.\d+/.test(origin) || /^https?:\/\/192\.168\.\d+\.\d+/.test(origin) ) return true; if (FRONTEND_URLS.includes(origin)) return true; return false; } // production: strict whitelist — must match configured FRONTEND_URLS exactly return FRONTEND_URLS.includes(origin); } app.use( cors({ origin: (origin, cb) => { if (isOriginAllowed(origin)) return cb(null, true); cb(new Error(`CORS: Origin ${origin} not allowed`)); }, methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"], allowedHeaders: ["Content-Type", "Authorization"], credentials: true, }) ); // Serve static files from uploads directory app.use("/uploads", express.static(path.join(process.cwd(), "uploads"))); app.use("/api/auth", authRoutes); // Twilio webhooks are public — Twilio sends no JWT token app.use("/api/twilio", express.urlencoded({ extended: false }), twilioWebhookRoutes); // All other API routes require JWT app.use("/api", authenticateJWT, routes); app.use(errorHandler); //startig cron job startBackupCron(); export default app;