import rateLimit from 'express-rate-limit'; /** 公开搜索接口:较宽松 */ export const searchLimiter = rateLimit({ windowMs: 60 * 1000, max: 150, standardHeaders: true, legacyHeaders: false, keyGenerator: (req) => req.socket.remoteAddress ?? 'unknown', message: { error: '搜索请求过于频繁,请稍后再试', code: 429 }, }); /** 管理接口(admin/*):较严格 */ export const adminLimiter = rateLimit({ windowMs: 60 * 1000, max: 30, standardHeaders: true, legacyHeaders: false, keyGenerator: (req) => req.socket.remoteAddress ?? 'unknown', message: { error: '操作过于频繁,请稍后再试', code: 429 }, }); /** 登录接口:极严格,防暴力破解 */ export const loginLimiter = rateLimit({ windowMs: 60 * 1000, max: 5, standardHeaders: true, legacyHeaders: false, keyGenerator: (req) => req.socket.remoteAddress ?? 'unknown', message: { error: '登录尝试次数过多,请一分钟后重试', code: 429 }, }); /** 转存/保存接口:中等等级 */ export const saveLimiter = rateLimit({ windowMs: 60 * 1000, max: 30, standardHeaders: true, legacyHeaders: false, keyGenerator: (req) => req.socket.remoteAddress ?? 'unknown', message: { error: '转存操作过于频繁,请稍后再试', code: 429 }, }); /** 默认全局限流(兜底,未匹配上述规则的路由) */ const defaultLimiter = rateLimit({ windowMs: 60 * 1000, max: 100, standardHeaders: true, legacyHeaders: false, keyGenerator: (req) => req.socket.remoteAddress ?? 'unknown', message: { error: 'Too many requests, please try again later.', code: 429 }, }); export default defaultLimiter; // NOTE v0.2.8: Fixed keyGenerator to use getClientIP() instead of req.socket.remoteAddress // This ensures rate limiting works correctly behind reverse proxy (OpenResty)