import path from 'node:path' import fs from 'node:fs' import dotenv from 'dotenv' import { projectRoot } from './paths.js' /** * `.env` / `.env.build` 를 읽어 `process.env` 에 주입. * * 여러 파일을 순서대로 읽되 `override:false` 로 병합하므로 **먼저 로드된 값이 * 우선**. 두 도메인(패키지 빌드용 vs 개발/서버용) 이 한 함수에서 자연스럽게 * 분리됨: * * 1. 패키징된 Electron 앱: `process.resourcesPath/.env.build` * — electron-builder 가 빌드 시점 `.env.build` 를 함께 배포. 패키지된 exe * 에서 가장 먼저 적용되는 값. * 2. 패키징된 Electron 앱: `process.resourcesPath/.env` * — 운영자가 패키징 후 직접 `.env` 를 옆에 두고 덮어쓰는 경우 폴백. * 3. `<프로젝트 루트>/.env` * — 개발 실행(npm start / npm run installer*) 및 서버 운영용. 서버의 * `PORT/HOST/SESSION_SECRET` 처럼 dev 에서 반드시 살아 있어야 하는 값들이 * 있어, `.env.build` 보다 먼저 로드해 우선권을 줌. * 4. `<프로젝트 루트>/.env.build` * — dev 환경에서 빌드용 값(예: 운영 도메인 SITE_BASE_URL)을 테스트하고 * 싶을 때 사용. `.env` 에 없는 키만 채움. * * - 이미 설정된 환경변수는 덮어쓰지 않음(쉘/systemd 에서 넘긴 값이 최우선). * - 존재하지 않는 후보는 조용히 건너뜀. * - 서버/설치기/리소스팩설치기 진입점에서 한 번씩 호출. */ export function loadEnv(): void { const candidates: string[] = [] const resourcesPath = (process as NodeJS.Process & { resourcesPath?: string }).resourcesPath if (typeof resourcesPath === 'string' && resourcesPath.length > 0) { candidates.push(path.join(resourcesPath, '.env.build')) candidates.push(path.join(resourcesPath, '.env')) } candidates.push(path.join(projectRoot, '.env')) candidates.push(path.join(projectRoot, '.env.build')) for (const envPath of candidates) { if (fs.existsSync(envPath)) { dotenv.config({ path: envPath, override: false, quiet: true }) } } } /** * 사이트 베이스 URL. 관리 사이트가 호스팅되는 외부 주소(설치기가 manifest 를 * 받아가는 도메인). 기본값은 로컬 개발용 `http://127.0.0.1:3000`. */ export function getSiteBaseUrl(): string { const raw = (process.env.SITE_BASE_URL ?? 'http://127.0.0.1:3000').trim() return raw.replace(/\/+$/, '') } /** 사이트 베이스 URL + `/manifest.json`. `MANIFEST_URL` 가 따로 지정되면 그 값을 우선. */ export function getManifestUrl(): string { const explicit = process.env.MANIFEST_URL?.trim() if (explicit) return explicit return `${getSiteBaseUrl()}/manifest.json` }