Initial commit
This commit is contained in:
77
src/middleware/rateLimit.js
Normal file
77
src/middleware/rateLimit.js
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Rate Limiting Middleware
|
||||
*
|
||||
* Simple in-memory rate limiter.
|
||||
* For production, consider using Redis-backed solution.
|
||||
*/
|
||||
|
||||
import { config } from '../config.js';
|
||||
|
||||
// In-memory store for request counts
|
||||
const requestCounts = new Map();
|
||||
|
||||
// Cleanup old entries periodically
|
||||
setInterval(() => {
|
||||
const now = Date.now();
|
||||
for (const [key, data] of requestCounts) {
|
||||
if (now - data.windowStart > config.rateLimitWindowMs * 2) {
|
||||
requestCounts.delete(key);
|
||||
}
|
||||
}
|
||||
}, 60000); // Every minute
|
||||
|
||||
/**
|
||||
* Get client identifier from request
|
||||
*/
|
||||
function getClientId(req) {
|
||||
// Use X-Forwarded-For header if behind proxy
|
||||
const forwarded = req.headers['x-forwarded-for'];
|
||||
if (forwarded) {
|
||||
return forwarded.split(',')[0].trim();
|
||||
}
|
||||
return req.ip || (req.connection ? req.connection.remoteAddress : null) || 'unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* Rate limiting middleware
|
||||
*/
|
||||
export function rateLimit(req, res, next) {
|
||||
const clientId = getClientId(req);
|
||||
const now = Date.now();
|
||||
|
||||
let data = requestCounts.get(clientId);
|
||||
|
||||
if (!data || now - data.windowStart > config.rateLimitWindowMs) {
|
||||
// New window
|
||||
data = {
|
||||
windowStart: now,
|
||||
count: 1
|
||||
};
|
||||
requestCounts.set(clientId, data);
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
data.count++;
|
||||
|
||||
if (data.count > config.rateLimitMaxRequests) {
|
||||
const retryAfter = Math.ceil((data.windowStart + config.rateLimitWindowMs - now) / 1000);
|
||||
|
||||
res.set('Retry-After', retryAfter.toString());
|
||||
res.set('X-RateLimit-Limit', config.rateLimitMaxRequests.toString());
|
||||
res.set('X-RateLimit-Remaining', '0');
|
||||
res.set('X-RateLimit-Reset', new Date(data.windowStart + config.rateLimitWindowMs).toISOString());
|
||||
|
||||
return res.status(429).json({
|
||||
error: 'Too many requests',
|
||||
retry_after: retryAfter
|
||||
});
|
||||
}
|
||||
|
||||
// Add rate limit headers
|
||||
res.set('X-RateLimit-Limit', config.rateLimitMaxRequests.toString());
|
||||
res.set('X-RateLimit-Remaining', (config.rateLimitMaxRequests - data.count).toString());
|
||||
res.set('X-RateLimit-Reset', new Date(data.windowStart + config.rateLimitWindowMs).toISOString());
|
||||
|
||||
next();
|
||||
}
|
||||
Reference in New Issue
Block a user