From 5b89585505c87566586557d00c1471333612a00b Mon Sep 17 00:00:00 2001 From: Falkan Date: Thu, 19 Mar 2026 23:40:20 -0400 Subject: [PATCH] feat: first-run config.json auto-generation on blank PVC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - loadConfig(): if config.json missing, create data/ dir, write default config with random sessionSecret and empty passwordHash, log instructions - loadData(): throw clear error if units.json missing instead of crashing opaquely - deployment.yaml: remove envFrom secretRef (SESSION_SECRET/PASSWORD_HASH not read from env — config.json on PVC is the source of truth) --- k8s/deployment.yaml | 3 --- src/lib/server/data.ts | 20 ++++++++++++++++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/k8s/deployment.yaml b/k8s/deployment.yaml index 4b828d5..74e857c 100644 --- a/k8s/deployment.yaml +++ b/k8s/deployment.yaml @@ -31,9 +31,6 @@ spec: value: "https://humorunits.com" - name: ALLOWED_ORIGINS value: "https://humorunits.com,https://www.humorunits.com,https://dickloads.com,https://www.dickloads.com" - envFrom: - - secretRef: - name: humor-units-secrets # SESSION_SECRET, PASSWORD_HASH volumeMounts: - name: data mountPath: /app/data diff --git a/src/lib/server/data.ts b/src/lib/server/data.ts index 5e4af00..f2f3806 100644 --- a/src/lib/server/data.ts +++ b/src/lib/server/data.ts @@ -1,4 +1,5 @@ -import { readFileSync, writeFileSync, renameSync } from 'fs'; +import { readFileSync, writeFileSync, renameSync, existsSync, mkdirSync } from 'fs'; +import { randomBytes } from 'crypto'; import { resolve } from 'path'; import type { UnitsData } from '$lib/types'; @@ -25,6 +26,9 @@ export interface AppConfig { /** Read units.json synchronously. Returns parsed UnitsData. */ export function loadData(): UnitsData { + if (!existsSync(UNITS_PATH)) { + throw new Error(`[humor-units] units.json not found at ${UNITS_PATH}. Mount it via a ConfigMap or PVC.`); + } const raw = readFileSync(UNITS_PATH, 'utf8'); return JSON.parse(raw) as UnitsData; } @@ -36,8 +40,20 @@ export function saveData(data: UnitsData): void { renameSync(tmp, UNITS_PATH); } -/** Read config.json synchronously. */ +/** Read config.json synchronously. Creates a default config on first run if missing. */ export function loadConfig(): AppConfig { + if (!existsSync(CONFIG_PATH)) { + mkdirSync(DATA_DIR, { recursive: true }); + const defaultConfig: AppConfig = { + adminPath: 'admin', + passwordHash: '', + sessionSecret: randomBytes(32).toString('hex'), + }; + writeFileSync(CONFIG_PATH, JSON.stringify(defaultConfig, null, 2) + '\n', 'utf8'); + console.log('[humor-units] First run: generated default config.json with random sessionSecret.'); + console.log('[humor-units] Set an admin password via: node scripts/set-password.js '); + return defaultConfig; + } const raw = readFileSync(CONFIG_PATH, 'utf8'); return JSON.parse(raw) as AppConfig; }