import fs from "fs"; import path from "path"; export type ReplicateMode = "cloud" | "local"; export type Settings = { replicateApiToken: string; jigsawApiKey: string; modelVersion: string; replicateEnabled: boolean; replicateMode: ReplicateMode; localEndpoint: string; adminPasswordHash: string; }; const DEFAULT_SETTINGS: Settings = { replicateApiToken: process.env["REPLICATE_API_TOKEN"] ?? "", jigsawApiKey: process.env["JIGSAWSTACK_API_KEY"] ?? "", modelVersion: "jigsawstack/text-translate:454df4c49941c05dea05175bd37686d0872c73c1f9366d1c2505db32ade52a89", replicateEnabled: false, replicateMode: (process.env["REPLICATE_MODE"] as ReplicateMode) ?? "cloud", localEndpoint: process.env["LOCAL_MODEL_ENDPOINT"] ?? "http://localhost:5030/predictions", adminPasswordHash: process.env["ADMIN_PASSWORD"] ?? "admin" }; /** * Resolve a writable path for settings.json. * Priority: * 1. SETTINGS_PATH env var (explicit override) * 2. /data/settings.json (default, works when data/ is writable) * 3. /tmp/lingvai-settings.json (fallback for read-only containers) */ function resolveSettingsPath(): string { if (process.env["SETTINGS_PATH"]) { return process.env["SETTINGS_PATH"]; } const primary = path.join(process.cwd(), "data", "settings.json"); try { const dir = path.dirname(primary); fs.mkdirSync(dir, { recursive: true }); // Test write access by opening with 'a' (append/create without truncating) const fd = fs.openSync(primary, "a"); fs.closeSync(fd); return primary; } catch { return path.join("/tmp", "lingvai-settings.json"); } } // Resolve once at module load so every call uses the same path const SETTINGS_PATH = resolveSettingsPath(); if (SETTINGS_PATH.startsWith("/tmp")) { console.warn( `[lingvai] data/settings.json is not writable. ` + `Settings will be stored at ${SETTINGS_PATH}. ` + `Mount a writable volume at /app/data or set SETTINGS_PATH to persist across restarts.` ); } export function readSettings(): Settings { try { if (!fs.existsSync(SETTINGS_PATH)) { return { ...DEFAULT_SETTINGS }; } const raw = fs.readFileSync(SETTINGS_PATH, "utf-8"); return { ...DEFAULT_SETTINGS, ...JSON.parse(raw) }; } catch { return { ...DEFAULT_SETTINGS }; } } export function writeSettings(updates: Partial): Settings { const current = readSettings(); const next = { ...current, ...updates }; const dir = path.dirname(SETTINGS_PATH); fs.mkdirSync(dir, { recursive: true }); fs.writeFileSync(SETTINGS_PATH, JSON.stringify(next, null, 2), "utf-8"); return next; }