Import source: backend/src/middleware/rateLimit
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
import type { RequestHandler } from 'express';
|
||||
import { HttpError } from './errorHandler.js';
|
||||
|
||||
const log = new Map<string, number[]>();
|
||||
|
||||
// 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();
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user