const rateLimit = require("express-rate-limit"); const logger = require("./logger"); const { RATE_LIMITS, HTTP_STATUS } = require("./constants"); const createRateLimiter = (config, limitType = "API") => { return rateLimit({ windowMs: config.windowMs, max: config.max, skipSuccessfulRequests: config.skipSuccessfulRequests || false, skipFailedRequests: config.skipFailedRequests || false, message: { success: false, message: config.message, }, standardHeaders: true, legacyHeaders: false, // Use X-Forwarded-For header from nginx/proxy - properly handle IPv6 keyGenerator: (req, res) => { const ip = req.headers["x-forwarded-for"]?.split(",")[0]?.trim() || req.headers["x-real-ip"] || req.ip || req.connection.remoteAddress; // Normalize IPv6 addresses to prevent bypass return ip.includes(":") ? ip.replace(/:/g, "-") : ip; }, handler: (req, res) => { const clientIp = req.headers["x-forwarded-for"]?.split(",")[0]?.trim() || req.ip; logger.warn(`${limitType} rate limit exceeded`, { ip: clientIp, path: req.path, email: req.body?.email, }); res.status(HTTP_STATUS.TOO_MANY_REQUESTS).json({ success: false, message: config.message, }); }, }); }; // General API rate limiter const apiLimiter = createRateLimiter( { windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS) || RATE_LIMITS.API.windowMs, max: parseInt(process.env.RATE_LIMIT_MAX_REQUESTS) || RATE_LIMITS.API.max, message: "Too many requests from this IP, please try again later.", }, "API", ); // Strict limiter for authentication endpoints const authLimiter = createRateLimiter( { windowMs: RATE_LIMITS.AUTH.windowMs, max: RATE_LIMITS.AUTH.max, skipSuccessfulRequests: true, message: "Too many login attempts, please try again after 15 minutes.", }, "Auth", ); // File upload limiter const uploadLimiter = createRateLimiter( { windowMs: RATE_LIMITS.UPLOAD.windowMs, max: RATE_LIMITS.UPLOAD.max, message: "Upload limit reached, please try again later.", }, "Upload", ); module.exports = { apiLimiter, authLimiter, uploadLimiter, };