import { mkdirSync } from "node:fs"; import { dirname, resolve } from "node:path"; export type AppConfig = { nodeEnv: string; host: string; port: number; databasePath: string; storageDir: string; jwtSecret: string; cookieName: string; cookieSecure: boolean; openLibraryEnabled: boolean; libraryPathAliases: Array<{ from: string; to: string }>; initialAdminEmail: string; initialAdminPassword: string; initialAdminPasswordIsDefault: boolean; }; const DEFAULT_INITIAL_ADMIN_EMAIL = "admin@readabook.local"; const DEFAULT_INITIAL_ADMIN_PASSWORD = "readabook-admin-change-me"; export function loadConfig(): AppConfig { const databasePath = resolve(process.env.DATABASE_PATH ?? "./data/readabook.sqlite"); const storageDir = resolve(process.env.STORAGE_DIR ?? "./data/storage"); mkdirSync(dirname(databasePath), { recursive: true }); mkdirSync(storageDir, { recursive: true }); return { nodeEnv: process.env.NODE_ENV ?? "development", host: process.env.HOST ?? "0.0.0.0", port: Number(process.env.PORT ?? 3000), databasePath, storageDir, jwtSecret: process.env.JWT_SECRET ?? "dev-change-me-readabook", cookieName: process.env.AUTH_COOKIE_NAME ?? "readabook_session", cookieSecure: process.env.COOKIE_SECURE === "true", openLibraryEnabled: process.env.OPEN_LIBRARY_ENABLED !== "false", libraryPathAliases: parseLibraryPathAliases(process.env.LIBRARY_PATH_ALIASES), initialAdminEmail: process.env.INITIAL_ADMIN_EMAIL ?? DEFAULT_INITIAL_ADMIN_EMAIL, initialAdminPassword: process.env.INITIAL_ADMIN_PASSWORD ?? DEFAULT_INITIAL_ADMIN_PASSWORD, initialAdminPasswordIsDefault: !process.env.INITIAL_ADMIN_PASSWORD }; } function parseLibraryPathAliases(value: string | undefined): Array<{ from: string; to: string }> { if (!value) return []; return value .split(";") .map((entry) => entry.trim()) .filter(Boolean) .map((entry) => { const separator = entry.indexOf("="); if (separator === -1) return null; const from = entry.slice(0, separator).trim(); const to = entry.slice(separator + 1).trim(); return from && to ? { from, to } : null; }) .filter((entry): entry is { from: string; to: string } => Boolean(entry)); }