/** * 速率限制器门面(audit-P1-6) * * 公共 API 与原 `rate-limit.ts` 单文件保持一致,差异: * - `rateLimit()` 与 `resetRateLimit()` 改为 `Promise` 返回,以支持异步后端 * - 内部根据 `RATE_LIMIT_DRIVER` 环境变量选择实现: * - `memory`(默认):单实例内存滑动窗口 * - `redis`:基于 @upstash/ratelimit + @upstash/redis 的分布式实现 * - 调用方需 `await rateLimit(...)` / `await resetRateLimit(...)` * * `rateLimitKey` / `RATE_LIMIT_RULES` / `rateLimitHeaders` 保持同步纯函数。 */ import { env } from "@/env.mjs" import type { RateLimiter, RateLimitParams, RateLimitResult } from "./types" export type { RateLimiter, RateLimitParams, RateLimitResult } from "./types" export { RATE_LIMIT_RULES, rateLimitHeaders, rateLimitKey } from "./rules" import { MemoryRateLimiter } from "./memory-limiter" import { RedisRateLimiter } from "./redis-limiter" /** * 单例限流器实例(按 env 选择实现,进程级复用)。 * * RedisRateLimiter 类本身在 import 时不会加载 @upstash/* 依赖—— * 重依赖通过类方法内的 `await import(...)` 懒加载, * 仅当 `RATE_LIMIT_DRIVER=redis` 且实际调用 `limit()` 时才加载。 */ let singleton: RateLimiter | null = null /** * 获取当前进程的限流器实例。 * * - 默认返回内存实现 * - 当 `RATE_LIMIT_DRIVER=redis` 时返回 Redis 实现 * - Redis 实现懒加载所需依赖,未安装 @upstash/ratelimit/redis 时首次调用抛错 */ export function getRateLimiter(): RateLimiter { if (singleton) return singleton if (env.RATE_LIMIT_DRIVER === "redis") { singleton = new RedisRateLimiter() } else { singleton = new MemoryRateLimiter() } return singleton } /** * 检查请求是否应被允许。 * * 无论成功失败均累加计数(便于失败次数累计锁定)。 * * **变更**:返回 `Promise`,调用方需 `await`。 */ export function rateLimit(params: RateLimitParams): Promise { return getRateLimiter().limit(params) } /** * 重置指定 key 的计数。 * * 用于成功登录后清除失败计数等场景。 * * **变更**:返回 `Promise`,调用方需 `await`。 */ export function resetRateLimit(key: string): Promise { return getRateLimiter().reset(key) }