import express, { type Express } from "express"; import cors from "cors"; import pinoHttp from "pino-http"; import { rateLimit } from "express-rate-limit"; import router from "./routes"; import { logger } from "./lib/logger"; const app: Express = express(); app.use( pinoHttp({ logger, serializers: { req(req) { return { id: req.id, method: req.method, url: req.url?.split("?")[0], }; }, res(res) { return { statusCode: res.statusCode, }; }, }, }), ); const allowedOrigins = process.env.ALLOWED_ORIGINS ? process.env.ALLOWED_ORIGINS.split(",") : ["http://localhost:5173", "http://localhost:3000"]; app.use(cors({ origin: (origin, callback) => { if (!origin || allowedOrigins.includes(origin)) { callback(null, true); } else { callback(new Error("Not allowed by CORS")); } }, credentials: true, })); app.use(express.json()); app.use(express.urlencoded({ extended: true })); // A) General API limiter for all /api routes: window 15 mins, max 500 requests per IP const generalApiLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 500, message: "طلبات كثيرة جداً، حاول مجدداً بعد قليل", standardHeaders: true, legacyHeaders: false, skip: (req) => process.env.NODE_ENV === "test" || req.headers["x-bypass-rate-limit"] === "true", }); // B) Auth limiter strictly on /api/auth/login: window 15 mins, max 10 attempts per IP const authLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 10, message: "محاولات تسجيل دخول كثيرة، انتظر 15 دقيقة", standardHeaders: true, legacyHeaders: false, skip: (req) => process.env.NODE_ENV === "test" || req.headers["x-bypass-rate-limit"] === "true", }); // C) Upload limiter for attachments/upload routes: window 1 hour, max 50 uploads per IP const uploadLimiter = rateLimit({ windowMs: 60 * 60 * 1000, max: 50, message: "تجاوزت حد الرفع المسموح به", standardHeaders: true, legacyHeaders: false, skip: (req) => process.env.NODE_ENV === "test" || req.headers["x-bypass-rate-limit"] === "true", }); // Apply Rate Limiters before the router middleware app.use("/api/auth/login", authLimiter); app.use("/api/attachments", uploadLimiter); app.use("/api/upload", uploadLimiter); app.use("/api/tasks/:id/attachments", uploadLimiter); app.use("/api", generalApiLimiter); app.use("/api", router); export default app;