refactor: standalone re-usable cache util
This commit is contained in:
parent
43411ab8e4
commit
f86548e942
2 changed files with 104 additions and 13 deletions
|
|
@ -2,13 +2,9 @@ import { getCapabilities } from "../../core/capabilities";
|
|||
import { config } from "../../core/config";
|
||||
import type { UpdateInfoDto } from "./system.dto";
|
||||
import semver from "semver";
|
||||
import { cache } from "../../utils/cache";
|
||||
|
||||
let updateCache: {
|
||||
data: UpdateInfoDto;
|
||||
timestamp: number;
|
||||
} | null = null;
|
||||
|
||||
const CACHE_TTL = 60 * 60 * 1000; // 1 hour
|
||||
const CACHE_TTL = 60 * 60;
|
||||
|
||||
const getSystemInfo = async () => {
|
||||
return {
|
||||
|
|
@ -24,9 +20,11 @@ interface GitHubRelease {
|
|||
}
|
||||
|
||||
const getUpdates = async (): Promise<UpdateInfoDto> => {
|
||||
const now = Date.now();
|
||||
if (updateCache && now - updateCache.timestamp < CACHE_TTL) {
|
||||
return updateCache.data;
|
||||
const CACHE_KEY = `system:updates:${config.appVersion}`;
|
||||
|
||||
const cached = cache.get<UpdateInfoDto>(CACHE_KEY);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
@ -72,10 +70,7 @@ const getUpdates = async (): Promise<UpdateInfoDto> => {
|
|||
missedReleases,
|
||||
};
|
||||
|
||||
updateCache = {
|
||||
data,
|
||||
timestamp: now,
|
||||
};
|
||||
cache.set(CACHE_KEY, data, CACHE_TTL);
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
|
|
|
|||
96
app/server/utils/cache.ts
Normal file
96
app/server/utils/cache.ts
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
import { Database } from "bun:sqlite";
|
||||
import path from "node:path";
|
||||
import fs from "node:fs";
|
||||
import { DATABASE_URL } from "../core/constants";
|
||||
|
||||
export const ONE_DAY_IN_SECONDS = 60 * 60 * 24;
|
||||
|
||||
export interface CacheOptions {
|
||||
dbPath?: string;
|
||||
}
|
||||
|
||||
export const createCache = (options: CacheOptions = {}) => {
|
||||
const defaultPath = path.join(path.dirname(DATABASE_URL), "cache.db");
|
||||
const dbPath = options.dbPath || defaultPath;
|
||||
|
||||
const dir = path.dirname(dbPath);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
|
||||
const db = new Database(dbPath);
|
||||
|
||||
db.run("CREATE TABLE IF NOT EXISTS cache (key TEXT PRIMARY KEY, value TEXT, expiration INTEGER)");
|
||||
|
||||
const set = (key: string, value: unknown, expirationSeconds = ONE_DAY_IN_SECONDS) => {
|
||||
const expiration = Date.now() + expirationSeconds * 1000;
|
||||
const stmt = db.prepare("INSERT OR REPLACE INTO cache (key, value, expiration) VALUES (?, ?, ?)");
|
||||
stmt.run(key, JSON.stringify(value), expiration);
|
||||
};
|
||||
|
||||
const get = <T>(key: string): T | undefined => {
|
||||
const stmt = db.prepare("SELECT value, expiration FROM cache WHERE key = ?");
|
||||
const row = stmt.get(key) as { value: string; expiration: number } | undefined;
|
||||
|
||||
if (!row) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (row.expiration < Date.now()) {
|
||||
const delStmt = db.prepare("DELETE FROM cache WHERE key = ?");
|
||||
delStmt.run(key);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(row.value) as T;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
const del = (key: string) => {
|
||||
const stmt = db.prepare("DELETE FROM cache WHERE key = ?");
|
||||
stmt.run(key);
|
||||
};
|
||||
|
||||
const getByPrefix = <T>(prefix: string): { key: string; value: T }[] => {
|
||||
const stmt = db.prepare("SELECT key, value, expiration FROM cache WHERE key LIKE ?");
|
||||
const rows = stmt.all(`${prefix}%`) as { key: string; value: string; expiration: number }[];
|
||||
|
||||
const now = Date.now();
|
||||
const results: { key: string; value: T }[] = [];
|
||||
|
||||
for (const row of rows) {
|
||||
if (row.expiration < now) {
|
||||
const delStmt = db.prepare("DELETE FROM cache WHERE key = ?");
|
||||
delStmt.run(row.key);
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
results.push({
|
||||
key: row.key,
|
||||
value: JSON.parse(row.value) as T,
|
||||
});
|
||||
} catch {
|
||||
// Ignore malformed entries
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
const clear = () => {
|
||||
db.run("DELETE FROM cache");
|
||||
};
|
||||
|
||||
return {
|
||||
set,
|
||||
get,
|
||||
del,
|
||||
getByPrefix,
|
||||
clear,
|
||||
};
|
||||
};
|
||||
|
||||
export const cache = createCache();
|
||||
Loading…
Reference in a new issue