import { ShareHttpError } from "@/lib/share/http-error"; export type FixedWindowIncrement = ( key: string, expiresInSeconds: number, ) => Promise; export async function enforceFixedWindowRateLimit({ key, limit, windowSeconds, increment, now = Date.now(), }: { key: string; limit: number; windowSeconds: number; increment: FixedWindowIncrement; now?: number; }) { const nowSeconds = Math.floor(now / 1000); const window = Math.floor(nowSeconds / windowSeconds); const retryAfter = windowSeconds - (nowSeconds % windowSeconds); let count: number; try { count = await increment( `erika:share:${key}:${window}`, windowSeconds + 1, ); } catch { throw new ShareHttpError("Rate limiting is temporarily unavailable", 503); } if (!Number.isFinite(count)) { throw new ShareHttpError("Rate limiting is temporarily unavailable", 503); } if (count > limit) { throw new ShareHttpError("Rate limit exceeded", 429, retryAfter); } }