import { CanActivate, ExecutionContext, HttpException, HttpStatus, Injectable, } from "@nestjs/common"; import { Request } from "express"; /** * 简单的内存级 IP 限流守卫:滑动窗口。 * 用于保护成本敏感的 AI 接口,防止被刷爆额度。 */ @Injectable() export class RateLimitGuard implements CanActivate { private static readonly WINDOW_MS = 60_000; // 单条回答会按句拆分为多次 TTS 调用,故放宽阈值(仍可防刷)。 private static readonly MAX_REQUESTS = 120; private static readonly buckets = new Map(); canActivate(context: ExecutionContext): boolean { const req = context.switchToHttp().getRequest(); const forwarded = req.headers["x-forwarded-for"]; const ip = (Array.isArray(forwarded) ? forwarded[0] : forwarded?.split(",")[0])?.trim() || req.ip || "unknown"; const now = Date.now(); const recent = (RateLimitGuard.buckets.get(ip) ?? []).filter( (t) => now - t < RateLimitGuard.WINDOW_MS ); if (recent.length >= RateLimitGuard.MAX_REQUESTS) { throw new HttpException( "请求过于频繁,请稍后再试", HttpStatus.TOO_MANY_REQUESTS ); } recent.push(now); RateLimitGuard.buckets.set(ip, recent); return true; } }