Fixed-window check. Returns retry-after seconds when the caller is over the limit, else null.
(ip: string, now: number)
| 72 | |
| 73 | /** Fixed-window check. Returns retry-after seconds when the caller is over the limit, else null. */ |
| 74 | function rateLimit(ip: string, now: number): number | null { |
| 75 | const entry = rateLimitHits.get(ip) |
| 76 | if (!entry || now >= entry.resetAt) { |
| 77 | rateLimitHits.set(ip, { count: 1, resetAt: now + RATE_LIMIT_WINDOW_MS }) |
| 78 | return null |
| 79 | } |
| 80 | if (entry.count >= RATE_LIMIT_MAX) { |
| 81 | return Math.ceil((entry.resetAt - now) / 1000) |
| 82 | } |
| 83 | entry.count += 1 |
| 84 | return null |
| 85 | } |
| 86 | |
| 87 | /** Drop expired buckets so the Map doesn't grow unbounded on a long-lived instance. */ |
| 88 | function sweepRateLimit(now: number): void { |