trails/apps/planner/app/lib/rate-limit.ts

43 lines
1.1 KiB
TypeScript

interface RateLimitEntry {
count: number;
resetAt: number;
}
const store = new Map<string, RateLimitEntry>();
const DEFAULT_WINDOW_MS = 60 * 60 * 1000; // 1 hour
const DEFAULT_MAX_REQUESTS = 60;
// Clean up expired entries periodically
setInterval(() => {
const now = Date.now();
for (const [key, entry] of store) {
if (now > entry.resetAt) {
store.delete(key);
}
}
}, 60 * 1000);
export function checkRateLimit(
key: string,
options?: { windowMs?: number; maxRequests?: number },
): { allowed: boolean; remaining: number; retryAfterSeconds?: number } {
const windowMs = options?.windowMs ?? DEFAULT_WINDOW_MS;
const maxRequests = options?.maxRequests ?? DEFAULT_MAX_REQUESTS;
const now = Date.now();
let entry = store.get(key);
if (!entry || now > entry.resetAt) {
entry = { count: 0, resetAt: now + windowMs };
store.set(key, entry);
}
entry.count++;
if (entry.count > maxRequests) {
const retryAfterSeconds = Math.ceil((entry.resetAt - now) / 1000);
return { allowed: false, remaining: 0, retryAfterSeconds };
}
return { allowed: true, remaining: maxRequests - entry.count };
}