fifteen/apps/web/lib/cache.ts
2026-03-01 21:58:53 +01:00

67 lines
1.8 KiB
TypeScript

import { getRedis } from "./redis";
/** TTL in seconds for each cache category */
const TTL = {
API_CITIES: 3600, // 1 hour
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<T>(key: string): Promise<T | null> {
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<T>(
key: string,
value: T,
ttlKey: CacheTTLKey,
): Promise<void> {
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<void> {
const redis = getRedis();
try {
const stream = redis.scanStream({ match: pattern, count: 100 });
const keys: string[] = [];
for await (const batch of stream as AsyncIterable<string[]>) {
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, unknown>): 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);
}