ready to test

Testing

Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
2026-06-22 10:06:58 +08:00
parent bf48c95467
commit 439bb33f77
189 changed files with 17559 additions and 686 deletions
+30 -2
View File
@@ -6,23 +6,41 @@
* - 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.' },
});
@@ -32,6 +50,7 @@ const authLimiter = rateLimit({
max: 20,
standardHeaders: true,
legacyHeaders: false,
store: makeStore('auth'),
message: { status: 'error', message: 'Too many auth attempts. Please wait 15 minutes.' },
});
@@ -41,15 +60,23 @@ const otpLimiter = rateLimit({
max: 5,
standardHeaders: true,
legacyHeaders: false,
store: makeStore('otp'),
message: { status: 'error', message: 'Too many OTP requests. Please wait 15 minutes.' },
});
/** Password change, account delete */
/**
* 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.' },
});
@@ -59,7 +86,8 @@ const adminLimiter = rateLimit({
max: 200,
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 };
module.exports = { globalLimiter, authLimiter, otpLimiter, sensitiveOpsLimiter, adminLimiter };