/** * portal/index.mjs — 고객 포털 프로세스 진입점. * 설정 → DB(마이그레이션) → 보관소 → 작업 엔진 → 어댑터(HAL·Webmaker·AriMail) → 앱 조립 → 기동. 주기 작업(세션 정리·예약 만료·사용량 경고)은 portal/scheduler.mjs. */ import { loadConfig } from './config.mjs'; import { openDb } from '../lib/db.mjs'; import { openVault } from '../lib/vault.mjs'; import { createJobEngine } from '../lib/jobs.mjs'; import { allMigrations } from './schema.mjs'; import { buildApp } from './app.mjs'; import { join } from 'node:path'; import { existsSync } from 'node:fs'; const VERSION = JSON.parse((await import('node:fs')).readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version; const log = (...a) => console.log(new Date().toISOString(), ...a); const cfg = loadConfig(); const store = openDb(join(cfg.dataDir, 'portal.sqlite'), await allMigrations()); const vault = openVault(cfg.dataDir); const jobs = createJobEngine(store, { log, workerId: `portal@${process.pid}` }); const deps = { store, vault, jobs, config: cfg, log, version: VERSION }; // 어댑터는 있을 때만(병렬 개발 중) 붙인다. for (const [name, file] of [['hal', './hal/client.mjs'], ['webmaker', './webmaker/adapter.mjs'], ['arimail', './mail/arimail.mjs']]) { const p = new URL(file, import.meta.url); if (existsSync(p)) { const m = await import(p); deps[name] = m.create ? m.create(deps) : m; } } const app = await buildApp(deps); if (existsSync(new URL('./scheduler.mjs', import.meta.url))) { const s = await import('./scheduler.mjs'); s.start(deps); } jobs.start(); await app.listen(cfg.port, cfg.host); log(`[portal] Ariweb Suite 고객 포털 v${VERSION} — http://${cfg.host}:${cfg.port} (공개 주소 ${cfg.publicUrl}, 데이터 ${cfg.dataDir})`); process.on('SIGTERM', () => { jobs.stop(); process.exit(0); });