chore: 初始化仓库
中华文明全图鉴——文物全图系统(PC Web 地图 + NestJS API + 管理后台)。 含三大 IP(文物南迁北归 / 国宝海外回归 / 博物馆手艺人)、AI 文物对话、 文物地图与详情、以及 demo-video-kit 演示视频生成工具。
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
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;
|
||||
private static readonly MAX_REQUESTS = 20;
|
||||
private static readonly buckets = new Map<string, number[]>();
|
||||
|
||||
canActivate(context: ExecutionContext): boolean {
|
||||
const req = context.switchToHttp().getRequest<Request>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user