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 { 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, database: StatsDb = defaultDb ): Promise { 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 { return hashSettings(await getSettings(database)); }