67 lines
1.8 KiB
TypeScript
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);
|
|
}
|