Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 2x 2x 2x 43x 43x 43x 23x 23x 20x 20x 123x 85x 83x 29x 28x 28x 24x 24x 24x 24x 20x | /** Rate limit: max requests per IP in the time window */
export const RATE_LIMIT = 3;
export const RATE_WINDOW_MS = 15 * 60 * 1000; // 15 min
export const hits = new Map<string, { count: number; reset: number }>();
export function isRateLimited(ip: string): boolean {
const now = Date.now();
const entry = hits.get(ip);
if (!entry || now > entry.reset) {
hits.set(ip, { count: 1, reset: now + RATE_WINDOW_MS });
return false;
}
entry.count++;
return entry.count > RATE_LIMIT;
}
export function sanitize(value: unknown): string {
if (value === null || value === undefined) return "";
if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") return "";
return String(value).trim().slice(0, 2000);
}
export function isValidEmail(email: string): boolean {
if (email.length > 254) return false;
const at = email.indexOf("@");
if (at <= 0 || at !== email.lastIndexOf("@")) return false;
const local = email.slice(0, at);
const domain = email.slice(at + 1);
const dot = domain.lastIndexOf(".");
if (dot <= 0 || dot === domain.length - 1) return false;
return /^[^\s@]+$/.test(local) && /^[^\s@]+$/.test(domain);
}
|