DB 없는 /setup 설치 마법사 아키텍처
목표
PortalConsole는 DATABASE_URL이 없는 최초 기동에서도 HTTP 서버와 설치 화면을 제공한다. 설치자는 브라우저에서 MySQL 또는 PostgreSQL 연결을 시험하고, schema migration과 최초 관리자 생성을 수행한 뒤, 선택한 배포 대상의 환경변수를 승인 후 설정한다. 설치가 끝난 서비스는 재시작 뒤 일반 PortalConsole runtime으로 전환된다.
/setup은 정상 관리자 UI의 대체 화면이 아니라 DB가 없는 최초 기동에만 열리는 일회성 bootstrap control plane이다.DATABASE_URL이 설정된 서비스에서는/setup이 설치 완료 안내만 보여 준다.
상태 전이
설치 중에는 process memory에만 DB connection URL, 설치 접근 token, provider token, 최초 관리자 password를 보관한다. 서버는 URL 전체나 token·password를 log, audit event, setup plan, browser-visible status에 기록하지 않는다.
HTTP 및 API 경계
| 경로 또는 procedure | 설치 전 | 설치 후 | 책임 |
|---|---|---|---|
GET / 및 /admin/* | /setup으로 302 | 일반 관리자 UI | 설치 전 업무 UI 차단 |
GET /setup | 설치 마법사 | 설치 완료 안내 | 최초 설치 state 표시 |
setup.status | public | public | DB 구성 여부·지원 선택지 반환; 비밀값 제외 |
setup.validateDatabase | public, setup token 없이 연결 시험 | 거부 | host/port/TLS/권한 검증 |
setup.plan | public, secret masking | 거부 | provider에 쓸 key 목록과 승인 범위 확인 |
setup.install | setup token 검사 후 허용 | 거부 | migration, 첫 관리자, 승인된 provider 설정 |
| 포털 REST, PKCE, UiPlatform route | 비활성 | 정상 | schema·secret 이전 접근 차단 |
공개 인터넷의 production setup은 PORTAL_CONSOLE_SETUP_TOKEN이 반드시 필요하다. 개발 환경에서는 로컬 검증을 위해 생략할 수 있지만, public deployment에서 token이 없으면 setup.install을 거부한다. setup access token은 provider의 service secret 또는 온프레미스 secret manager에 bootstrap 값으로만 둔다.
설치 입력 모델
| 영역 | 입력 | 보안 처리 |
|---|---|---|
| 데이터베이스 | dialect, connection URL | password input; 검증·migration connection에만 사용 |
| 초기 관리자 | username, display name, password | password는 scrypt hash만 DB에 저장 |
| 선택 서비스 | FileStore URL, UiPlatform URL | non-secret URL만 runtime environment에 저장 |
| deployment | manual/관리형 클라우드, provider IDs, API token, deploy toggle | token은 provider request에만 사용 후 browser state에서 삭제 |
| 설치 승인 | provider write acknowledgement | 관리형 클라우드 write·deploy 직전 필수 |
MANUAL은 Docker Compose, VM, Kubernetes, 내부 CI처럼 PortalConsole가 provider API에 직접 접근하지 않는 경우다. 설치 결과는 생성된 runtime environment values를 한 번만 반환하며, 운영자가 secret manager에 저장한 뒤 재시작한다. 관리형 클라우드 또는 관리형 클라우드를 선택하면 DATABASE_URL, DATABASE_DIALECT, JWT_SECRET, PORTAL_JWT_SECRET, 선택한 FileStore/UiPlatform endpoint와 setup disable marker가 대상 service에 전달된다.
Provider 적용 규칙
| provider | variable write | deploy | 확인 방법 |
|---|---|---|---|
| 관리형 클라우드 | 서비스별 add/update API를 key 단위로 호출 | installer가 승인한 경우 deploy API 호출 | 관리형 클라우드 deploy ID와 service health 확인 |
| 관리형 클라우드 | GraphQL variableUpsert를 service/environment scope로 호출 | installer가 승인한 경우 environmentTriggersDeploy 호출 | 관리형 클라우드 deployment status query와 service health 확인 |
| Manual | 외부 write 없음 | 운영자가 수행 | one-time environment bundle 적용 후 /admin 확인 |
관리형 클라우드의 full-list environment variable update API는 body에 없는 값을 제거하므로 setup은 이를 사용하지 않고 service variable의 key 단위 update를 사용한다. 관리형 클라우드 API 변경은 deploy API를 별도로 호출해야 적용되며, 관리형 클라우드 variable 변경은 staged review/deploy 흐름을 갖는다.[1] [2] [3]
Database migration
PortalConsole는 dialect별 migration folder를 선택한다.
| dialect | migration folder | runtime migrator |
|---|---|---|
| MySQL | drizzle/ | drizzle-orm/mysql2/migrator |
| PostgreSQL | drizzle/postgres-migrations/ | drizzle-orm/node-postgres/migrator |
설치 transaction의 순서는 connection validation → migration → initial admin insertion → provider environment update다. provider 설정에서 실패해도 database schema와 최초 관리자는 이미 안전하게 존재할 수 있으므로, 동일 관리자 password로 재시도할 때는 초기 관리자 insertion을 idempotent하게 처리한다. 다른 관리자 계정이 존재하면 setup은 중단하여 소유권 충돌을 방지한다.
포털 재사용 계약
각 포털은 동일한 setup state model과 UI pattern을 사용한다. 단독 포털은 LOCAL identity로 자체 DB와 첫 관리자를 생성한다. PortalConsole 연동 포털은 PortalConsole setup이 완료된 뒤에만 central bundle을 검증·수신하고, 자체 DB connection이나 PortalConsole 관리자 password를 받지 않는다.
FileStore와 UiPlatform은 독립 선택 서비스다. setup에서 URL을 등록할 수 있지만, 사용자에게 보이는 attachment UI와 local theme fallback은 central service 연결 실패에도 포털의 업무 화면을 차단하지 않아야 한다.
구현 파일
| 파일 | 책임 |
|---|---|
server/setupBootstrap.ts | input validation, DB validation/migration, first admin creation, provider environment write, secret masking |
server/routers.ts | public setup.status, validateDatabase, plan, install procedures |
server/index.ts | DB 없는 setup mode 기동, root/admin redirect, setup 전 REST route 비활성화, dialect별 normal migration |
client/src/pages/SetupWizard.tsx | four-stage setup UI, connection validation, plan review, provider approval, one-time manual bundle |
.env.onprem.example | 최소 setup access token과 runtime/provider secret guidance |