import { getRedis } from "./redis"; import { NextResponse } from "next/server"; export async function rateLimit( key: string, limit: number, windowSeconds: number ): Promise<{ ok: boolean; remaining: number; reset: number }> { const redis = getRedis(); const now = Date.now(); const windowStart = now - windowSeconds * 1000; const reset = Math.ceil((now + windowSeconds * 1000) / 1000); const pipeline = redis.pipeline(); pipeline.zremrangebyscore(key, "-inf", windowStart); pipeline.zadd(key, now, `${now}-${Math.random()}`); pipeline.zcard(key); pipeline.expire(key, windowSeconds); const results = await pipeline.exec(); const count = (results?.[2]?.[1] as number) ?? 0; const remaining = Math.max(0, limit - count); const ok = count <= limit; return { ok, remaining, reset }; } export async function applyRateLimit( key: string, limit: number, windowSeconds: number ): Promise { const { ok, remaining, reset } = await rateLimit(key, limit, windowSeconds); if (!ok) { return NextResponse.json( { error: "Too many requests", retryAfter: reset }, { status: 429, headers: { "X-RateLimit-Limit": String(limit), "X-RateLimit-Remaining": "0", "X-RateLimit-Reset": String(reset), "Retry-After": String(reset - Math.floor(Date.now() / 1000)), }, } ); } return null; }