refactor: move sessionSecret+passwordHash to env vars, seed data/ from defaults/
- SESSION_SECRET and PASSWORD_HASH moved out of config.json into env vars - data.ts: AppConfig no longer holds secrets; loadConfig/loadData seed from defaults/ on first run if data/ files are missing - auth.ts: requireAuth/authRequest read SESSION_SECRET from process.env directly - login/+server.ts: reads PASSWORD_HASH and SESSION_SECRET from process.env - defaults/config.json: ships with image (no secrets) - defaults/units.json: ships with image as initial unit data - package.json: add dotenv dep; start/serve load .env via -r dotenv/config - Dockerfile: copy defaults/ into image; data/ is PVC-only - .env.example: documents required env vars for local dev - Remove k8s/ — managed externally
This commit is contained in:
@@ -1,17 +1,17 @@
|
||||
import { readFileSync, writeFileSync, renameSync, existsSync, mkdirSync } from 'fs';
|
||||
import { randomBytes } from 'crypto';
|
||||
import { readFileSync, writeFileSync, renameSync, existsSync, mkdirSync, copyFileSync } from 'fs';
|
||||
import { resolve } from 'path';
|
||||
import type { UnitsData } from '$lib/types';
|
||||
|
||||
// Path is relative to project root (where the server runs from)
|
||||
const DATA_DIR = resolve('data');
|
||||
const DEFAULTS_DIR = resolve('defaults');
|
||||
const UNITS_PATH = resolve(DATA_DIR, 'units.json');
|
||||
const CONFIG_PATH = resolve(DATA_DIR, 'config.json');
|
||||
const DEFAULT_UNITS_PATH = resolve(DEFAULTS_DIR, 'units.json');
|
||||
const DEFAULT_CONFIG_PATH = resolve(DEFAULTS_DIR, 'config.json');
|
||||
|
||||
export interface AppConfig {
|
||||
adminPath: string;
|
||||
passwordHash: string;
|
||||
sessionSecret: string;
|
||||
/** Label shown next to the YOLO mode toggle. Defaults to "YOLO mode". */
|
||||
yoloLabel?: string;
|
||||
/** Description shown inline after the label. Defaults to "(cross-group conversions)". */
|
||||
@@ -24,10 +24,24 @@ export interface AppConfig {
|
||||
yoloVisibility?: 'auto' | 'never';
|
||||
}
|
||||
|
||||
/** Read units.json synchronously. Returns parsed UnitsData. */
|
||||
/** Ensure data/ directory exists and seed missing files from defaults/. */
|
||||
function ensureDataDir(): void {
|
||||
mkdirSync(DATA_DIR, { recursive: true });
|
||||
if (!existsSync(UNITS_PATH) && existsSync(DEFAULT_UNITS_PATH)) {
|
||||
copyFileSync(DEFAULT_UNITS_PATH, UNITS_PATH);
|
||||
console.log('[humor-units] First run: seeded data/units.json from defaults/units.json');
|
||||
}
|
||||
if (!existsSync(CONFIG_PATH) && existsSync(DEFAULT_CONFIG_PATH)) {
|
||||
copyFileSync(DEFAULT_CONFIG_PATH, CONFIG_PATH);
|
||||
console.log('[humor-units] First run: seeded data/config.json from defaults/config.json');
|
||||
}
|
||||
}
|
||||
|
||||
/** Read units.json synchronously. Seeds from defaults on first run. */
|
||||
export function loadData(): UnitsData {
|
||||
ensureDataDir();
|
||||
if (!existsSync(UNITS_PATH)) {
|
||||
throw new Error(`[humor-units] units.json not found at ${UNITS_PATH}. Mount it via a ConfigMap or PVC.`);
|
||||
throw new Error(`[humor-units] units.json not found at ${UNITS_PATH} and no default available.`);
|
||||
}
|
||||
const raw = readFileSync(UNITS_PATH, 'utf8');
|
||||
return JSON.parse(raw) as UnitsData;
|
||||
@@ -40,19 +54,11 @@ export function saveData(data: UnitsData): void {
|
||||
renameSync(tmp, UNITS_PATH);
|
||||
}
|
||||
|
||||
/** Read config.json synchronously. Creates a default config on first run if missing. */
|
||||
/** Read config.json synchronously. Seeds from defaults on first run. */
|
||||
export function loadConfig(): AppConfig {
|
||||
ensureDataDir();
|
||||
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;
|
||||
throw new Error(`[humor-units] config.json not found at ${CONFIG_PATH} and no default available.`);
|
||||
}
|
||||
const raw = readFileSync(CONFIG_PATH, 'utf8');
|
||||
return JSON.parse(raw) as AppConfig;
|
||||
|
||||
Reference in New Issue
Block a user