From f315a7af510aedff59ba0988cd20bac5ed6befe7 Mon Sep 17 00:00:00 2001 From: Ho Ngoc Hai Date: Sun, 4 Jan 2026 12:00:23 +0700 Subject: [PATCH] refactor: Replaced dynamic rate limit creation with pre-configured role-based limiters. --- .../src/middlewares/rate-limit.middleware.ts | 133 ++++++++++++------ 1 file changed, 91 insertions(+), 42 deletions(-) diff --git a/services/iam-service/src/middlewares/rate-limit.middleware.ts b/services/iam-service/src/middlewares/rate-limit.middleware.ts index 1db64390..8d77ab35 100644 --- a/services/iam-service/src/middlewares/rate-limit.middleware.ts +++ b/services/iam-service/src/middlewares/rate-limit.middleware.ts @@ -1,11 +1,93 @@ import { logger } from '@goodgo/logger'; import { Request, Response, NextFunction } from 'express'; import rateLimit from 'express-rate-limit'; -import { RedisStore } from 'rate-limit-redis'; -import { getRedisClient } from '../config/redis.config'; import { rbacService } from '../modules/rbac/rbac.service'; +/** + * EN: Pre-configured rate limiters for different user tiers + * VI: Rate limiters được cấu hình trước cho các cấp độ người dùng khác nhau + */ +const superAdminLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, + max: 1000, + standardHeaders: true, + legacyHeaders: false, + handler: (_req, res) => { + res.status(429).json({ + success: false, + error: { + code: 'RATE_LIMIT_EXCEEDED', + message: 'Too many requests, please try again later', + }, + }); + }, +}); + +const adminLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, + max: 500, + standardHeaders: true, + legacyHeaders: false, + handler: (_req, res) => { + res.status(429).json({ + success: false, + error: { + code: 'RATE_LIMIT_EXCEEDED', + message: 'Too many requests, please try again later', + }, + }); + }, +}); + +const moderatorLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, + max: 300, + standardHeaders: true, + legacyHeaders: false, + handler: (_req, res) => { + res.status(429).json({ + success: false, + error: { + code: 'RATE_LIMIT_EXCEEDED', + message: 'Too many requests, please try again later', + }, + }); + }, +}); + +const userLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, + max: 100, + standardHeaders: true, + legacyHeaders: false, + handler: (_req, res) => { + res.status(429).json({ + success: false, + error: { + code: 'RATE_LIMIT_EXCEEDED', + message: 'Too many requests, please try again later', + }, + }); + }, +}); + +const guestLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, + max: 50, + standardHeaders: true, + legacyHeaders: false, + handler: (_req, res) => { + res.status(429).json({ + success: false, + error: { + code: 'RATE_LIMIT_EXCEEDED', + message: 'Too many requests, please try again later', + }, + }); + }, +}); + /** * EN: Dynamic rate limiting based on user role * VI: Giới hạn tốc độ động dựa trên vai trò người dùng @@ -18,58 +100,25 @@ export const dynamicRateLimit = async ( try { const userId = (req as any).user?.id || (req as any).user?.sub; - // Default limits - let windowMs = 15 * 60 * 1000; // 15 minutes - let max = 100; // 100 requests + let limiter = guestLimiter; if (userId) { // Get user roles const roles = await rbacService.getUserRoles(userId); - // Set limits based on role + // Select limiter based on role if (roles.includes('SUPER_ADMIN')) { - windowMs = 15 * 60 * 1000; - max = 1000; + limiter = superAdminLimiter; } else if (roles.includes('ADMIN')) { - windowMs = 15 * 60 * 1000; - max = 500; + limiter = adminLimiter; } else if (roles.includes('MODERATOR')) { - windowMs = 15 * 60 * 1000; - max = 300; + limiter = moderatorLimiter; } else { - // Regular user - windowMs = 15 * 60 * 1000; - max = 100; + limiter = userLimiter; } - } else { - // Unauthenticated users - stricter limits - windowMs = 15 * 60 * 1000; - max = 50; } - // Create rate limiter - const limiter = rateLimit({ - windowMs, - max, - standardHeaders: true, - legacyHeaders: false, - store: new RedisStore({ - // @ts-expect-error - rate-limit-redis types mismatch with ioredis - sendCommand: (...args: string[]) => getRedisClient().call(...args), - prefix: 'rl:', - }), - handler: (_req, res) => { - res.status(429).json({ - success: false, - error: { - code: 'RATE_LIMIT_EXCEEDED', - message: 'Too many requests, please try again later', - }, - }); - }, - }); - - // Apply rate limiter + // Apply selected rate limiter limiter(req, res, next); } catch (error) { logger.error('Rate limit middleware error', { error });