- Add incident reporting routes and logic to handle creation, updating, and retrieval of incidents. - Implement visibility rules for users based on roles (reporters, supervisors). - Create tests for incident reporting functionality to ensure correctness and reliability. - Introduce site and area management routes, including validation for area hierarchy. - Establish user management routes with role-based access control for creating and updating users. - Add setup routes for initializing the application with an organization and admin user. - Implement shared enums and schemas for consistent validation and data handling across the application. - Create a test helper for setting up an in-memory database and API client for testing. - Add TypeScript configuration for server-side code with strict type checking and module resolution.
32 lines
990 B
TypeScript
32 lines
990 B
TypeScript
/** Fixed-window in-memory limiter. Fine for a single server process; move to the database if we scale out. */
|
|
export class RateLimiter {
|
|
private hits = new Map<string, { count: number; resetAt: number }>()
|
|
private readonly limit: number
|
|
private readonly windowMs: number
|
|
|
|
constructor(limit: number, windowMs: number) {
|
|
this.limit = limit
|
|
this.windowMs = windowMs
|
|
}
|
|
|
|
/** Records an attempt; returns false once the key has exceeded the limit. */
|
|
attempt(key: string, now = Date.now()): boolean {
|
|
const entry = this.hits.get(key)
|
|
if (!entry || entry.resetAt <= now) {
|
|
this.hits.set(key, { count: 1, resetAt: now + this.windowMs })
|
|
if (this.hits.size > 10_000) this.prune(now)
|
|
return true
|
|
}
|
|
entry.count += 1
|
|
return entry.count <= this.limit
|
|
}
|
|
|
|
reset(key: string): void {
|
|
this.hits.delete(key)
|
|
}
|
|
|
|
private prune(now: number) {
|
|
for (const [key, entry] of this.hits) if (entry.resetAt <= now) this.hits.delete(key)
|
|
}
|
|
}
|