interface DeviceUsage { count: number; firstUsedAt: number; } // In-memory usage map (or keyed by hashed client token) const usageStore = new Map(); export interface RateLimitResult { allowed: boolean; remaining: number; resetMinutes: number; } export function checkRateLimit( clientId: string, maxAllowed: number, cooldownMinutes: number ): RateLimitResult { if (!maxAllowed || maxAllowed <= 0) { return { allowed: true, remaining: 999999, resetMinutes: 0 }; } const now = Date.now(); const cooldownMs = cooldownMinutes * 60 * 1000; const usage = usageStore.get(clientId); if (!usage) { return { allowed: true, remaining: maxAllowed, resetMinutes: 0 }; } // Check if cooldown period has elapsed since first use if (now - usage.firstUsedAt > cooldownMs) { usageStore.delete(clientId); return { allowed: true, remaining: maxAllowed, resetMinutes: 0 }; } const remaining = Math.max(0, maxAllowed - usage.count); const remainingCooldownMs = cooldownMs - (now - usage.firstUsedAt); const resetMinutes = Math.ceil(remainingCooldownMs / (60 * 1000)); return { allowed: usage.count < maxAllowed, remaining, resetMinutes, }; } export function incrementRateLimit(clientId: string): void { const now = Date.now(); const usage = usageStore.get(clientId); if (!usage) { usageStore.set(clientId, { count: 1, firstUsedAt: now }); } else { usage.count += 1; } }