const WINDOW_MS = 60_000; const LIMIT = 10; const buckets = new Map(); export function resetRateLimit(): void { buckets.clear(); } /** True when the caller is still inside 10 requests per minute. */ export function allowRequest(ip: string, now = Date.now()): boolean { const fresh = (buckets.get(ip) ?? []).filter((t) => now - t < WINDOW_MS); if (fresh.length >= LIMIT) { buckets.set(ip, fresh); return false; } fresh.push(now); buckets.set(ip, fresh); return true; } /** Last hop. Apache appends the real client after any address the browser sent. */ export function clientIp(header: string | null): string { if (!header) return "direct"; const parts = header.split(",").map((part) => part.trim()).filter(Boolean); return parts[parts.length - 1] || "direct"; }