You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sims-hq/apps/hq/src/rate-limit.ts

22 lines
1.0 KiB
TypeScript

/**
* A dependency-free fixed-window rate limiter keyed by an opaque string (client IP,
* account email, staff id, …). Returns true while the key is under `limit` in the
* current `windowMs` window, false once it is exhausted. Expired buckets are swept
* lazily so the map cannot grow without bound under key rotation. In its own module
* so both the api router and the public-share route can share it without a cycle.
*/
export function makeRateLimiter(limit: number, windowMs: number): (key: string, now?: number) => boolean {
const buckets = new Map<string, { count: number; resetAt: number }>()
return (key: string, now = Date.now()): boolean => {
if (buckets.size > 5000) for (const [k, b] of buckets) if (now >= b.resetAt) buckets.delete(k)
const bucket = buckets.get(key)
if (bucket === undefined || now >= bucket.resetAt) {
buckets.set(key, { count: 1, resetAt: now + windowMs })
return true
}
if (bucket.count >= limit) return false
bucket.count += 1
return true
}
}