Files
ridebus-admin/backend/src/middleware/rateLimiter.js
2026-04-28 22:51:54 +03:00

67 lines
1.9 KiB
JavaScript

const rateLimit = require('express-rate-limit');
const redisClient = require('../config/redis');
// In-memory fallback store if Redis is unavailable
const memoryStore = new Map();
async function getRateLimitStore() {
// Custom Redis store
return {
async increment(key) {
try {
const current = await redisClient.get(key);
const count = current ? parseInt(current) + 1 : 1;
await redisClient.setEx(key, 3600, count.toString()); // 1 hour TTL
return { totalHits: count, resetTime: null };
} catch (error) {
// Fallback to memory store
const count = (memoryStore.get(key) || 0) + 1;
memoryStore.set(key, count);
setTimeout(() => memoryStore.delete(key), 3600000); // 1 hour
return { totalHits: count, resetTime: null };
}
},
async decrement(key) {
try {
await redisClient.decr(key);
} catch (error) {
const count = memoryStore.get(key) || 0;
memoryStore.set(key, Math.max(0, count - 1));
}
},
async resetKey(key) {
try {
await redisClient.del(key);
} catch (error) {
memoryStore.delete(key);
}
}
};
}
const createRateLimiter = (maxRequests = 1000, windowMs = 3600000) => {
return rateLimit({
windowMs, // 1 hour default
max: maxRequests,
standardHeaders: true,
legacyHeaders: false,
keyGenerator: (req) => {
// Use API key if available, otherwise IP
return req.apiKeyInfo?.id || req.ip;
},
handler: (req, res) => {
res.status(429).json({
error: 'Too many requests',
message: 'Rate limit exceeded. Please try again later.',
retryAfter: Math.ceil(windowMs / 1000)
});
},
skip: (req) => {
// Skip rate limiting for health checks
return req.path === '/health';
}
});
};
module.exports = createRateLimiter;