refactor: Replaced dynamic rate limit creation with pre-configured role-based limiters.

This commit is contained in:
Ho Ngoc Hai
2026-01-04 12:00:23 +07:00
parent fc33f027d6
commit f315a7af51

View File

@@ -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 });