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.
22 lines
1.0 KiB
TypeScript
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
|
|
}
|
|
}
|