You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
35 lines
1.1 KiB
35 lines
1.1 KiB
import { fail } from "@/lib/response";
|
|
import { checkIpRateLimit } from "@/modules/cache/ip-rate-limiter";
|
|
import { checkRateLimit } from "@/modules/cache/rate-limiter";
|
|
import type { NextResponse } from "next/server";
|
|
|
|
function extractClientIp(request: Request): string {
|
|
const forwarded = request.headers.get("x-forwarded-for");
|
|
if (forwarded) {
|
|
return forwarded.split(",")[0]?.trim() || "unknown";
|
|
}
|
|
return request.headers.get("x-real-ip") ?? "unknown";
|
|
}
|
|
|
|
/** 客户 + IP 双限流,超限返回 429 响应 */
|
|
export async function enforceRateLimits(
|
|
request: Request,
|
|
customerId: string,
|
|
): Promise<NextResponse | null> {
|
|
const ip = extractClientIp(request);
|
|
const ipResult = await checkIpRateLimit(ip);
|
|
if (!ipResult.allowed) {
|
|
const message = ipResult.banned
|
|
? "IP 已被临时封禁,请稍后再试"
|
|
: "请求过于频繁,请稍后再试";
|
|
return fail("RATE_LIMITED", message, 429);
|
|
}
|
|
|
|
const customerResult = await checkRateLimit(customerId);
|
|
if (!customerResult.allowed) {
|
|
return fail("RATE_LIMITED", "请求过于频繁,请稍后再试", 429);
|
|
}
|
|
|
|
return null;
|
|
}
|