mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
95 lines
3.9 KiB
JavaScript
95 lines
3.9 KiB
JavaScript
/***********************************************************************************************************************************************************************
|
|
* File Name: rateLimiter.middleware.js
|
|
* Type of Program: Middleware
|
|
* Description: Express-rate-limit configurations for different route tiers.
|
|
* - globalLimiter → applied to ALL routes (1000 req / 15 min)
|
|
* - authLimiter → applied to login/register (20 req / 15 min)
|
|
* - otpLimiter → applied to OTP send/verify (5 req / 15 min)
|
|
* - sensitiveOpsLimiter → password change, account delete (10 req / hour)
|
|
* - adminLimiter → admin routes (200 req / 15 min)
|
|
*
|
|
* Store selection is driven by CACHE_DRIVER:
|
|
* redis → RedisStore (shared across processes; required in production)
|
|
* memory → in-process MemoryStore (fine for local dev, single process)
|
|
* Author: rgrgogu
|
|
* Date Created: Oct. 6, 2025
|
|
* Date Modified: Jun. 19, 2026
|
|
***********************************************************************************************************************************************************************
|
|
* HOW TO USE:
|
|
* const { authLimiter } = require('../middleware/rateLimiter.middleware');
|
|
* router.post('/login', authLimiter, loginHandler);
|
|
***********************************************************************************************************************************************************************/
|
|
const rateLimit = require('express-rate-limit');
|
|
const redisClient = require('../config/redis.config');
|
|
|
|
const windowMs15 = 15 * 60 * 1000; // 15 minutes
|
|
|
|
const makeStore = redisClient
|
|
? (() => {
|
|
const { RedisStore } = require('rate-limit-redis');
|
|
return (prefix) => new RedisStore({
|
|
sendCommand: (...args) => redisClient.sendCommand(args),
|
|
prefix: `rl:${prefix}:`,
|
|
});
|
|
})()
|
|
: () => undefined; // undefined → express-rate-limit uses its default MemoryStore
|
|
|
|
/** Applied globally in server.js */
|
|
const globalLimiter = rateLimit({
|
|
windowMs: windowMs15,
|
|
max: 1000,
|
|
standardHeaders: true,
|
|
legacyHeaders: false,
|
|
store: makeStore('global'),
|
|
message: { status: 'error', message: 'Too many requests, please try again later.' },
|
|
});
|
|
|
|
/** Login & register routes */
|
|
const authLimiter = rateLimit({
|
|
windowMs: windowMs15,
|
|
max: 20,
|
|
standardHeaders: true,
|
|
legacyHeaders: false,
|
|
store: makeStore('auth'),
|
|
message: { status: 'error', message: 'Too many auth attempts. Please wait 15 minutes.' },
|
|
});
|
|
|
|
/** OTP send / verify */
|
|
const otpLimiter = rateLimit({
|
|
windowMs: windowMs15,
|
|
max: 5,
|
|
standardHeaders: true,
|
|
legacyHeaders: false,
|
|
store: makeStore('otp'),
|
|
message: { status: 'error', message: 'Too many OTP requests. Please wait 15 minutes.' },
|
|
});
|
|
|
|
/**
|
|
* Sensitive write operations — bulk actions, uploads, financial transactions.
|
|
* Keys by authenticated user ID when available, falls back to IP.
|
|
* Keying by IP alone is unfair on shared networks (office NAT, university Wi-Fi)
|
|
* where one user triggering the limit would block everyone behind the same IP.
|
|
*/
|
|
const sensitiveOpsLimiter = rateLimit({
|
|
windowMs: 60 * 60 * 1000, // 1 hour
|
|
max: 10,
|
|
keyGenerator: (req) => req.user?.user_id?.toString() ?? req.ip,
|
|
standardHeaders: true,
|
|
legacyHeaders: false,
|
|
store: makeStore('sensitive'),
|
|
message: { status: 'error', message: 'Too many sensitive operations. Please wait 1 hour.' },
|
|
});
|
|
|
|
/** Admin routes — per authenticated user, not per IP */
|
|
const adminLimiter = rateLimit({
|
|
windowMs: windowMs15,
|
|
max: 200,
|
|
keyGenerator: (req) => req.user?.user_id?.toString() ?? req.ip,
|
|
standardHeaders: true,
|
|
legacyHeaders: false,
|
|
store: makeStore('admin'),
|
|
message: { status: 'error', message: 'Too many admin requests. Please wait 15 minutes.' },
|
|
});
|
|
|
|
module.exports = { globalLimiter, authLimiter, otpLimiter, sensitiveOpsLimiter, adminLimiter };
|