BillTracker/middleware/rateLimiter.js

97 lines
2.5 KiB
JavaScript

'use strict';
const rateLimit = require('express-rate-limit');
function makeLimiter(max, windowMs, message) {
return rateLimit({
windowMs,
max,
standardHeaders: 'draft-7',
legacyHeaders: false,
// Override default handler so the response is always JSON, not HTML
handler(req, res) {
res.status(429).json({ error: message });
},
});
}
// 10 login attempts per 15 minutes per IP — brute-force protection
const loginLimiter = makeLimiter(
10, 15 * 60 * 1000,
'Too many login attempts. Please try again in 15 minutes.',
);
// 5 password-change attempts per 15 minutes per IP
const passwordLimiter = makeLimiter(
5, 15 * 60 * 1000,
'Too many password change attempts. Please try again in 15 minutes.',
);
// 20 import preview/apply requests per 15 minutes per IP
const importLimiter = makeLimiter(
20, 15 * 60 * 1000,
'Too many import requests. Please try again in 15 minutes.',
);
// 30 export requests per 15 minutes per IP
const exportLimiter = makeLimiter(
30, 15 * 60 * 1000,
'Too many export requests. Please try again in 15 minutes.',
);
// 30 admin mutation actions per 15 minutes per IP (backup/restore/cleanup)
const adminActionLimiter = makeLimiter(
30, 15 * 60 * 1000,
'Too many admin actions. Please try again in 15 minutes.',
);
// 20 OIDC login/callback requests per 15 minutes per IP
const oidcLimiter = makeLimiter(
20, 15 * 60 * 1000,
'Too many authentication requests. Please try again in 15 minutes.',
);
// 5 backup operations per 60 minutes per IP (backup creation, restore, import)
const backupOperationLimiter = makeLimiter(
5, 60 * 60 * 1000,
'Too many backup operations. Please try again in 60 minutes.',
);
// 3 demo data clear operations per 15 minutes per IP
const demoDataLimiter = makeLimiter(
3, 15 * 60 * 1000,
'Too many demo data clear operations. Please try again in 15 minutes.',
);
// ── Export all limiters plus reset function ────────────────────────────────────
const allLimiters = [
loginLimiter,
passwordLimiter,
importLimiter,
exportLimiter,
adminActionLimiter,
oidcLimiter,
backupOperationLimiter,
demoDataLimiter,
];
function resetStores() {
for (const limiter of allLimiters) {
if (limiter.store.reset) {
limiter.store.reset();
}
}
}
module.exports = {
loginLimiter,
passwordLimiter,
importLimiter,
exportLimiter,
adminActionLimiter,
oidcLimiter,
backupOperationLimiter,
demoDataLimiter,
resetStores,
};