mirror of
https://github.com/pupperpowell/bibdle.git
synced 2026-10-22 05:54:10 -04:00
83 lines
2.7 KiB
TypeScript
83 lines
2.7 KiB
TypeScript
import { db as defaultDb } from '$lib/server/db';
|
|
import { dashboardSettings } from '$lib/server/db/schema';
|
|
import {
|
|
SETTINGS_DEFAULTS,
|
|
type SettingsKey,
|
|
type StatsSettings
|
|
} from '$lib/types/stats';
|
|
import type { StatsDb } from './db';
|
|
|
|
const SETTINGS_KEYS = Object.keys(SETTINGS_DEFAULTS) as SettingsKey[];
|
|
|
|
/** `core_plays_per_week` → `STATS_CORE_PLAYS_PER_WEEK` */
|
|
export function settingsEnvVar(key: SettingsKey): string {
|
|
return `STATS_${key.toUpperCase()}`;
|
|
}
|
|
|
|
/** Defaults from `SETTINGS_DEFAULTS`, overridden by `STATS_*` env vars. */
|
|
export function envSettings(): StatsSettings {
|
|
const out = { ...SETTINGS_DEFAULTS } as StatsSettings;
|
|
for (const key of SETTINGS_KEYS) {
|
|
const raw = Bun.env[settingsEnvVar(key)];
|
|
if (raw === undefined || raw === '') continue;
|
|
const value = Number(raw);
|
|
if (Number.isFinite(value)) out[key] = value;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function coerce(key: SettingsKey, raw: string): number {
|
|
const value = Number(raw);
|
|
return Number.isFinite(value) ? value : SETTINGS_DEFAULTS[key];
|
|
}
|
|
|
|
/**
|
|
* Reads persisted overrides from `dashboard_settings` and overlays them on the
|
|
* env/default baseline. Safe to call when the table does not yet exist.
|
|
*/
|
|
export async function getSettings(database: StatsDb = defaultDb): Promise<StatsSettings> {
|
|
const settings = envSettings();
|
|
try {
|
|
const rows = await database.select().from(dashboardSettings);
|
|
for (const row of rows) {
|
|
if ((SETTINGS_KEYS as string[]).includes(row.key)) {
|
|
settings[row.key as SettingsKey] = coerce(row.key as SettingsKey, row.value);
|
|
}
|
|
}
|
|
} catch {
|
|
// Table missing / DB unavailable — fall back to defaults.
|
|
}
|
|
return settings;
|
|
}
|
|
|
|
/** Upserts a partial set of setting overrides. */
|
|
export async function updateSettings(
|
|
partial: Partial<StatsSettings>,
|
|
database: StatsDb = defaultDb
|
|
): Promise<StatsSettings> {
|
|
const now = new Date();
|
|
for (const [key, value] of Object.entries(partial)) {
|
|
if (!(SETTINGS_KEYS as string[]).includes(key)) continue;
|
|
if (value === undefined || value === null || !Number.isFinite(value)) continue;
|
|
await database
|
|
.insert(dashboardSettings)
|
|
.values({ key, value: String(value), updatedAt: now })
|
|
.onConflictDoUpdate({
|
|
target: dashboardSettings.key,
|
|
set: { value: String(value), updatedAt: now }
|
|
});
|
|
}
|
|
return getSettings(database);
|
|
}
|
|
|
|
/** Stable short hash of the resolved settings — used to detect stale snapshots. */
|
|
export function hashSettings(settings: StatsSettings): string {
|
|
const canonical = SETTINGS_KEYS.map((k) => `${k}=${settings[k]}`).join(';');
|
|
const digest = new Bun.CryptoHasher('sha256').update(canonical).digest('hex');
|
|
return digest.slice(0, 12);
|
|
}
|
|
|
|
export async function configVersion(database: StatsDb = defaultDb): Promise<string> {
|
|
return hashSettings(await getSettings(database));
|
|
}
|