const WINDOW_MS = 60_000; const MAX_CARD_HITS = 60; const MAX_SIGNUP_HITS = 10; const cardHitsByIp = new Map(); const signupHitsByIp = new Map(); function checkLimit( store: Map, ip: string, maxHits: number, ): { allowed: true } | { allowed: false; retryAfterSeconds: number } { const now = Date.now(); const cutoff = now - WINDOW_MS; const prev = store.get(ip) ?? []; const recent = prev.filter((t) => t > cutoff); if (recent.length >= maxHits) { const retryAfterSeconds = Math.max( 1, Math.ceil((recent[0]! + WINDOW_MS - now) / 1000), ); store.set(ip, recent); return { allowed: false, retryAfterSeconds }; } recent.push(now); store.set(ip, recent); return { allowed: true }; } export function checkPublicCardRateLimit( ip: string, ): { allowed: true } | { allowed: false; retryAfterSeconds: number } { return checkLimit(cardHitsByIp, ip, MAX_CARD_HITS); } export function checkPublicSignupRateLimit( ip: string, ): { allowed: true } | { allowed: false; retryAfterSeconds: number } { return checkLimit(signupHitsByIp, ip, MAX_SIGNUP_HITS); }