refactor(server): non-DI islands read env via app-config

This commit is contained in:
jubnl
2026-07-25 15:58:02 +02:00
parent 7f795a2199
commit fb9abdb76c
11 changed files with 64 additions and 71 deletions
+5 -4
View File
@@ -1,6 +1,7 @@
import Database from 'better-sqlite3';
import path from 'path';
import fs from 'fs';
import { readEnv } from '../app-config';
import { createTables } from './schema';
import { runMigrations } from './migrations';
import { runSeeds } from './seeds';
@@ -8,16 +9,16 @@ import { Place, Tag } from '../types';
// In test mode each vitest worker gets an isolated in-memory DB so that
// parallel forks can't race on the same file or share migration state.
const isTest = process.env.NODE_ENV === 'test';
const isTest = readEnv().app.isTest;
let dbPath: string;
if (isTest) {
dbPath = ':memory:';
} else if (process.env.TREK_DB_FILE) {
} else if (readEnv().db.trekDbFile) {
// Explicit DB file (used by the Playwright E2E harness to run against an
// isolated, throwaway database instead of the real data/travel.db). Purely
// additive — when unset the default path below is used exactly as before.
dbPath = process.env.TREK_DB_FILE;
dbPath = readEnv().db.trekDbFile!;
const dir = path.dirname(dbPath);
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
} else {
@@ -62,7 +63,7 @@ const db = new Proxy({} as Database.Database, {
},
});
if (process.env.DEMO_MODE?.toLowerCase() === 'true') {
if (readEnv().demo.enabled) {
try {
const { seedDemoData } = require('../demo/demo-seed');
seedDemoData(_db);
+3 -2
View File
@@ -1,3 +1,4 @@
import { readEnv } from '../app-config';
import { encrypt_api_key } from '../services/apiKeyCrypto';
import Database from 'better-sqlite3';
@@ -2720,7 +2721,7 @@ function runMigrations(db: Database.Database): void {
`CREATE TABLE IF NOT EXISTS migrations (id integer PRIMARY KEY AUTOINCREMENT NOT NULL, timestamp bigint NOT NULL, name varchar NOT NULL);`,
);
db.exec(`INSERT INTO migrations (timestamp, name) VALUES (1777810195344, 'InitialSchema1777810195344');`);
db.exec(`INSERT INTO app_settings (key, value) VALUES ('app_version', '${process.env.APP_VERSION || '3.0.14'}')`);
db.exec(`INSERT INTO app_settings (key, value) VALUES ('app_version', '${readEnv().app.appVersion || '3.0.14'}')`);
},
// trim leading/trailing whitespace from stored usernames and emails
() => {
@@ -2738,7 +2739,7 @@ function runMigrations(db: Database.Database): void {
db.exec(`INSERT INTO schema_version_new (version) SELECT version FROM schema_version`);
db.exec(`DROP TABLE schema_version`);
db.exec(`ALTER TABLE schema_version_new RENAME TO schema_version`);
db.exec(`UPDATE app_settings SET value = '${process.env.APP_VERSION || '3.0.15'}' WHERE key = 'app_version'`);
db.exec(`UPDATE app_settings SET value = '${readEnv().app.appVersion || '3.0.15'}' WHERE key = 'app_version'`);
},
// Migration: OAuth 2.0 client_credentials grant — allow user-owned confidential
// clients to skip the browser consent flow entirely and obtain tokens directly
+7 -5
View File
@@ -1,5 +1,6 @@
import Database from 'better-sqlite3';
import crypto from 'crypto';
import { readEnv } from '../app-config';
// bcrypt cost factor for the seeded admin password — kept in sync with authService.
const BCRYPT_COST = 12;
@@ -9,14 +10,15 @@ const BCRYPT_COST = 12;
// are only relevant after the first user exists; at that point seeds have already
// finished and skip via the userCount > 0 guard above.
function isOidcOnlyConfigured(): boolean {
if (process.env.OIDC_ONLY?.toLowerCase() !== 'true') return false;
return !!(process.env.OIDC_ISSUER && process.env.OIDC_CLIENT_ID);
const oidc = readEnv().oidc;
if (!oidc.only) return false;
return !!(oidc.issuer && oidc.clientId);
}
function seedAdminAccount(db: Database.Database): void {
try {
const env_admin_email = process.env.ADMIN_EMAIL;
const env_admin_pw = process.env.ADMIN_PASSWORD;
const env_admin_email = readEnv().adminBootstrap.email;
const env_admin_pw = readEnv().adminBootstrap.password;
const adminEnvProvided = !!(env_admin_email || env_admin_pw);
const userCount = (db.prepare('SELECT COUNT(*) as count FROM users').get() as { count: number }).count;
@@ -34,7 +36,7 @@ function seedAdminAccount(db: Database.Database): void {
// Demo mode seeds its own admin (admin@trek.app, username 'admin') right after this.
// Creating a first-run admin here would grab username 'admin' first and make the demo
// seeder fail on the UNIQUE(username) constraint, leaving the demo user uncreated.
if (process.env.DEMO_MODE?.toLowerCase() === 'true') return;
if (readEnv().demo.enabled) return;
if (isOidcOnlyConfigured()) {
console.log('');