import {existsSync, mkdirSync, readFileSync} from 'node:fs'; import {dirname, isAbsolute, resolve} from 'node:path'; export const LOCAL_ENV_FILES = ['.env', '.env.local', '.env.secrets.local']; export function buildProtectedEnvKeys(baseEnv) { return new Set( Object.entries(baseEnv) .filter(([, value]) => String(value ?? '').trim()) .map(([key]) => key), ); } export function loadEnvFile(path, target, protectedKeys) { if (!existsSync(path)) { return; } const rawText = readFileSync(path, 'utf8'); for (const rawLine of rawText.split(/\r?\n/u)) { const line = rawLine.trim(); if (!line || line.startsWith('#')) { continue; } const match = line.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/u); if (!match) { continue; } const [, key, rawValue] = match; if (protectedKeys.has(key)) { continue; } target[key] = rawValue.replace(/^['"]|['"]$/gu, ''); } } export function loadApiServerEnv(repoRootPath, target, protectedKeys) { const resolvedProtectedKeys = protectedKeys ?? buildProtectedEnvKeys(process.env); // shell > .env > .env.local > .env.secrets.local for (const fileName of LOCAL_ENV_FILES) { loadEnvFile(resolve(repoRootPath, fileName), target, resolvedProtectedKeys); } } export function mergeApiServerEnv(repoRootPath, baseEnv = process.env) { const mergedEnv = {...baseEnv}; loadApiServerEnv(repoRootPath, mergedEnv, buildProtectedEnvKeys(baseEnv)); return mergedEnv; } export function formatApiServerLogTimestamp(date = new Date()) { const pad = (value) => String(value).padStart(2, '0'); return [ date.getFullYear(), pad(date.getMonth() + 1), pad(date.getDate()), '-', pad(date.getHours()), pad(date.getMinutes()), pad(date.getSeconds()), ].join(''); } export function resolveApiServerLogFile( repoRootPath, env = process.env, now = new Date(), ) { const explicitLogFile = String( env.GENARRATIVE_API_SERVER_LOG_FILE ?? '', ).trim(); if (explicitLogFile) { return isAbsolute(explicitLogFile) ? explicitLogFile : resolve(repoRootPath, explicitLogFile); } const logDir = String(env.GENARRATIVE_API_SERVER_LOG_DIR ?? '').trim() || 'logs/api-server'; const resolvedLogDir = isAbsolute(logDir) ? logDir : resolve(repoRootPath, logDir); return resolve( resolvedLogDir, `api-server-${formatApiServerLogTimestamp(now)}.log`, ); } export function ensureParentDir(filePath) { mkdirSync(dirname(filePath), {recursive: true}); } export function resolveClientHost(hostName) { if (hostName === '0.0.0.0' || hostName === '::') { return '127.0.0.1'; } return hostName; }