Files
EpicNext-Cms/src/lib/cached-db.ts
T
openhands 7f7971f578
CI / check (push) Successful in 30s
CI / release (push) Skipped
CI / deploy (push) Successful in 1m26s
fix: resolve all biome lint errors and type issues
- Add file-level biome-ignore for noExplicitAny in prisma-facade.ts
  (intentional any for Prisma API compatibility surface)
- Fix noNonNullAssertion errors in cached-db.ts (redis null-guard fixes)
- Auto-fix formatting + organizeImports across modified files
- 0 tsc errors, 0 biome errors, 583 tests passing
2026-07-31 15:15:15 +02:00

62 lines
1.4 KiB
TypeScript

import "server-only";
import { logger } from "@/lib/logger";
import { redis } from "@/lib/redis";
const DEFAULT_CACHE_TTL = 60;
function isRedisAvailable(): boolean {
return !!redis && redis.status !== "end";
}
export async function cachedQuery<T>(
cacheKey: string,
queryFn: () => Promise<T>,
ttl: number = DEFAULT_CACHE_TTL,
): Promise<T> {
if (!isRedisAvailable()) {
return queryFn();
}
try {
const cached = await redis?.get(cacheKey);
if (cached !== null && cached !== undefined) {
return JSON.parse(cached) as T;
}
} catch (err) {
logger.warn("[cache] read failed, falling back to DB", {
key: cacheKey,
error: String(err),
});
}
const result = await queryFn();
try {
await redis?.setex(cacheKey, ttl, JSON.stringify(result));
} catch (err) {
logger.warn("[cache] write failed, continuing without cache", {
key: cacheKey,
error: String(err),
});
}
return result;
}
/** Invalidate cache keys matching a glob pattern. */
export async function invalidateCache(pattern: string): Promise<number> {
if (!isRedisAvailable()) return 0;
const r = redis;
if (!r) return 0;
const keys = await r.keys(pattern);
return keys.length > 0 ? r.del(...keys) : 0;
}
/** Invalidate a single cache key immediately. */
export async function invalidateKey(key: string): Promise<number> {
if (!isRedisAvailable()) return 0;
if (!redis) return 0;
return redis.del(key);
}