feat: first-run config.json auto-generation on blank PVC

- 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)
This commit is contained in:
Falkan
2026-03-19 23:40:20 -04:00
parent 2c5d267d84
commit 5b89585505
2 changed files with 18 additions and 5 deletions

View File

@@ -31,9 +31,6 @@ spec:
value: "https://humorunits.com" value: "https://humorunits.com"
- name: ALLOWED_ORIGINS - name: ALLOWED_ORIGINS
value: "https://humorunits.com,https://www.humorunits.com,https://dickloads.com,https://www.dickloads.com" 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: volumeMounts:
- name: data - name: data
mountPath: /app/data mountPath: /app/data

View File

@@ -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 { resolve } from 'path';
import type { UnitsData } from '$lib/types'; import type { UnitsData } from '$lib/types';
@@ -25,6 +26,9 @@ export interface AppConfig {
/** Read units.json synchronously. Returns parsed UnitsData. */ /** Read units.json synchronously. Returns parsed UnitsData. */
export function loadData(): 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'); const raw = readFileSync(UNITS_PATH, 'utf8');
return JSON.parse(raw) as UnitsData; return JSON.parse(raw) as UnitsData;
} }
@@ -36,8 +40,20 @@ export function saveData(data: UnitsData): void {
renameSync(tmp, UNITS_PATH); 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 { 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 <password>');
return defaultConfig;
}
const raw = readFileSync(CONFIG_PATH, 'utf8'); const raw = readFileSync(CONFIG_PATH, 'utf8');
return JSON.parse(raw) as AppConfig; return JSON.parse(raw) as AppConfig;
} }