/** * In-memory sliding window rate limiter. * Single-instance, single-user container — no need for Redis. */ interface RateLimitEntry { timestamps: number[]; } const store = new Map(); const WINDOW_MS = 60 * 1000; // 1 minute const MAX_REQUESTS = 10; export function checkRateLimit( key: string, options: { windowMs?: number; maxRequests?: number } = {} ): { allowed: boolean; retryAfterMs: number } { const now = Date.now(); const windowMs = options.windowMs ?? WINDOW_MS; const maxRequests = options.maxRequests ?? MAX_REQUESTS; const entry = store.get(key) ?? { timestamps: [] }; // Remove timestamps outside the window entry.timestamps = entry.timestamps.filter((t) => now - t < windowMs); if (entry.timestamps.length >= maxRequests) { const oldestInWindow = entry.timestamps[0]; const retryAfterMs = windowMs - (now - oldestInWindow); return { allowed: false, retryAfterMs }; } entry.timestamps.push(now); store.set(key, entry); return { allowed: true, retryAfterMs: 0 }; }