Spaces:
Sleeping
Sleeping
assafvayner HF Staff
fix(ui): shared safe-next validation, no lockout on success, logout origin check, tab/feed polish
b11153a Download src/lib/server/auth/rate-limit.ts from assafvayner/splitwise: direct link, hf CLI and curl.
- Browser
- Download file 1.18 kB
-
https://huggingface.co/spaces/assafvayner/splitwise/resolve/main/src/lib/server/auth/rate-limit.ts
- Command line
-
hf download hf://spaces/assafvayner/splitwise/src/lib/server/auth/rate-limit.ts
-
curl -L -o rate-limit.ts https://huggingface.co/spaces/assafvayner/splitwise/resolve/main/src/lib/server/auth/rate-limit.ts
1.18 kB
| /** Above this many tracked keys, prune stale entries on the next `allow` before inserting. */ | |
| const MAX_TRACKED_KEYS = 10_000; | |
| /** Sliding-window limiter: at most `max` allowed calls per key within `windowMs`. In-memory only. */ | |
| export class RateLimiter { | |
| private hits = new Map<string, number[]>(); | |
| constructor( | |
| private readonly max: number, | |
| private readonly windowMs: number, | |
| private readonly now: () => number = Date.now | |
| ) {} | |
| /** Number of keys currently tracked. Exposed for tests. */ | |
| get size(): number { | |
| return this.hits.size; | |
| } | |
| allow(key: string): boolean { | |
| const t = this.now(); | |
| const recent = (this.hits.get(key) ?? []).filter((h) => t - h < this.windowMs); | |
| if (recent.length >= this.max) { | |
| if (recent.length === 0) this.hits.delete(key); | |
| else this.hits.set(key, recent); | |
| return false; | |
| } | |
| if (this.hits.size > MAX_TRACKED_KEYS) { | |
| for (const [k, hs] of this.hits) { | |
| const mostRecent = hs.length > 0 ? Math.max(...hs) : -Infinity; | |
| if (t - mostRecent >= this.windowMs) this.hits.delete(k); | |
| } | |
| } | |
| recent.push(t); | |
| this.hits.set(key, recent); | |
| return true; | |
| } | |
| reset(key: string): void { | |
| this.hits.delete(key); | |
| } | |
| } | |