const rateLimit = require('express-rate-limit'); function normalizePhone(value) { return String(value || '').replace(/\D/g, ''); } // Tenant login — 5 failed attempts per 15 min per IP const loginLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 5, skipSuccessfulRequests: true, standardHeaders: true, legacyHeaders: false, message: { error: 'Too many login attempts. Try again in 15 minutes.' }, }); // Tenant registration — 3 accounts per hour per IP const registerLimiter = rateLimit({ windowMs: 60 * 60 * 1000, max: 3, standardHeaders: true, legacyHeaders: false, message: { error: 'Too many registration attempts. Try again in an hour.' }, }); // Portal purchase — 5 per 10 min per IP (prevents M-Pesa spam to any phone) const purchaseLimiter = rateLimit({ windowMs: 10 * 60 * 1000, max: 5, standardHeaders: true, legacyHeaders: false, keyGenerator: req => { const siteId = req.params?.siteId || 'unknown-site'; const phone = normalizePhone(req.body?.phone) || req.ip || 'unknown-phone'; return `${siteId}:${phone}`; }, message: { error: 'Too many purchase attempts. Please wait before trying again.' }, }); // Forgot password — 3 OTP requests per 15 min per IP (prevents SMS bombing) const forgotPasswordLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 3, standardHeaders: true, legacyHeaders: false, message: { error: 'Too many reset requests. Try again in 15 minutes.' }, }); // Portal auth / code entry — 10 per 15 min per IP (prevents code brute force) const portalAuthLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 10, standardHeaders: true, legacyHeaders: false, message: { error: 'Too many authorization attempts. Try again in 15 minutes.' }, }); // Phone change OTP — 3 requests per 30 min per IP (prevents SMS bombing new numbers) const phoneOtpLimiter = rateLimit({ windowMs: 30 * 60 * 1000, max: 3, standardHeaders: true, legacyHeaders: false, message: { error: 'Too many verification requests. Try again in 30 minutes.' }, }); module.exports = { loginLimiter, registerLimiter, forgotPasswordLimiter, purchaseLimiter, portalAuthLimiter, phoneOtpLimiter };