import { getRedis } from "./redis"; /** TTL in seconds for each cache category */ const TTL = { API_CITIES: 30, // 30 seconds — city status changes during ingest API_POIS: 300, // 5 minutes API_GRID: 600, // 10 minutes API_STATS: 120, // 2 minutes API_ISOCHRONES: 3600, // 1 hour GEOFABRIK_INDEX: 86400, // 24 hours SESSION: 28800, // 8 hours } as const; export type CacheTTLKey = keyof typeof TTL; export async function cacheGet(key: string): Promise { const redis = getRedis(); try { const raw = await redis.get(key); if (!raw) return null; return JSON.parse(raw) as T; } catch { return null; } } export async function cacheSet( key: string, value: T, ttlKey: CacheTTLKey, ): Promise { const redis = getRedis(); try { await redis.set(key, JSON.stringify(value), "EX", TTL[ttlKey]); } catch (err) { console.error("[cache] set error:", err); } } export async function cacheDel(pattern: string): Promise { const redis = getRedis(); try { const stream = redis.scanStream({ match: pattern, count: 100 }); const keys: string[] = []; for await (const batch of stream as AsyncIterable) { keys.push(...batch); } if (keys.length > 0) { await redis.del(...keys); } } catch (err) { console.error("[cache] del error:", err); } } /** Stable hash of query params for cache keys (djb2, not cryptographic) */ export function hashParams(params: Record): string { const sorted = Object.keys(params) .sort() .map((k) => `${k}=${JSON.stringify(params[k])}`) .join("&"); let hash = 5381; for (let i = 0; i < sorted.length; i++) { hash = ((hash << 5) + hash) ^ sorted.charCodeAt(i); } return (hash >>> 0).toString(16); }