Vrai-Prix — l'évaluation du vrai prix des propriétés résidentielles au Québec.
TypeScript 90.2%
JavaScript 3.5%
Python 3.4%
CSS 1.9%
HTML 0.6%
1// Auteur : Simon-Pierre Boucher — contact@spboucher.ai2/**3 * Cache mémoire simple (par processus) pour les lectures coûteuses de la base4 * de coûts : aperçu, assemblages, indices. Invalidé par TTL et par la clé5 * `last_ingest` (cost_meta) mise à jour à chaque ingestion.6 */7import { metaGet } from "./db";89interface Entry<T> { value: T; expires: number; ingest: string | null }10const store = new Map<string, Entry<unknown>>();1112export function cached<T>(key: string, ttlMs: number, compute: () => T): T {13 let ingest: string | null = null;14 try { ingest = metaGet("last_ingest"); } catch { /* base indisponible */ }15 const e = store.get(key) as Entry<T> | undefined;16 if (e && e.expires > Date.now() && e.ingest === ingest) return e.value;17 const value = compute();18 store.set(key, { value, expires: Date.now() + ttlMs, ingest });19 return value;20}2122export function invalidateCache(): void {23 store.clear();24}2526/** Limiteur de débit en mémoire (par clé, fenêtre glissante). */27const hits = new Map<string, number[]>();28export function rateLimit(key: string, max: number, windowMs: number): boolean {29 const now = Date.now();30 const arr = (hits.get(key) ?? []).filter((t) => now - t < windowMs);31 if (arr.length >= max) { hits.set(key, arr); return false; }32 arr.push(now);33 hits.set(key, arr);34 return true;35}3637export function clientKey(req: Request): string {38 const xff = req.headers.get("x-forwarded-for");39 return (xff ? xff.split(",").pop()!.trim() : req.headers.get("x-real-ip")) ?? "local";40}41