import type { RequestHandler } from 'express'; import { HttpError } from './errorHandler.js'; const log = new Map(); // Prune entries older than 1 hour every 10 minutes setInterval(() => { const cutoff = Date.now() - 3_600_000; for (const [ip, ts] of log) { const fresh = ts.filter((t) => t > cutoff); if (fresh.length === 0) log.delete(ip); else log.set(ip, fresh); } }, 600_000).unref(); /** * Sliding-window rate limiter — in-memory, per IP. * maxRequests allowed within windowMs milliseconds. */ export function rateLimit(maxRequests: number, windowMs: number): RequestHandler { return (req, res, next) => { const ip = req.ip ?? req.socket.remoteAddress ?? 'unknown'; const now = Date.now(); const windowStart = now - windowMs; const timestamps = (log.get(ip) ?? []).filter((t) => t > windowStart); if (timestamps.length >= maxRequests) { const retryAfterSecs = Math.ceil((timestamps[0] - windowStart) / 1000); res.setHeader('Retry-After', String(retryAfterSecs)); return next(new HttpError(429, 'RATE_LIMITED', `Too many requests — retry after ${retryAfterSecs}s`)); } timestamps.push(now); log.set(ip, timestamps); next(); }; }