English {#english}
Tenant Onboarding Process and Demo Web (www) — Plan
Purpose: Document Prego’s tenant_id and tenant_slug model (Frappe multi-tenant), document the tenant onboarding flow from existing plans and current code, and define scope and requirements for a test demo user site (package choice → Sign-in → Stripe → Ansible/Pulumi/Zuplo).
No code generation — flow, planning, and requirements only.
References: tenant-provisioning-flow.md, provision-tenant-workflow-design.md, www-pregoi-com-signup-to-provisioning-flow.md, tenant-subdomain-dns-design.md, api-control-plane-implementation-plan.md Appendix C.
1. Prego Multi-Tenant Model Summary
| Item | Content |
|---|---|
| Model | Frappe multi-tenant: Site = 1 Tenant, DB per site. |
| tenant_id | Internal customer ID. Control Plane D1 tenants_master.tenant_id PK. UUID recommended; short_id = first 8 chars for tenant-{short_id}.pregoi.com. |
| tenant_slug | User-facing slug; same as subdomain_slug. 1:1 with {subdomain_slug}.pregoi.com. |
| Ownership | tenant_id, tenant_slug, canonical_hostname in Control Plane D1; after provisioning, Zuplo gets tenant_id, slug, subdomain. |
2. Onboarding Flow Summary
Sequence: Demo www package (free/paid) → Sign-in → (paid) Stripe Checkout or (free) POST /v1/tenants → Webhook/API → Control Plane (tenant_id, D1 Pending, provision_jobs) → decidePlacement → workflow_dispatch → Pulumi → Ansible (Frappe site, API Key) → Cloudflare DNS (canonical + user CNAME) → Zuplo Sync → provision-complete → D1·KV updated, tenant Active.
Identifiers: tenant_id (UUID), short_id (first 8), canonical_hostname tenant-{short_id}.pregoi.com, subdomain_slug user subdomain {subdomain_slug}.pregoi.com. Slug and subdomain rules (§2.4): reserved names (www, app, api, …), length 3–63, blocklist, D1 UNIQUE, validation API.
3. Demo Web (apps/www) Requirements
- Location:
Prego/apps/www. Package selection, Sign-in, Stripe, (optional) job status. Deploy: Cloudflare Pages or static. - Packages: Free → POST /v1/tenants (subdomain_slug, tier=free). Paid → Stripe Checkout with metadata (plan_tier, requested_region, subdomain_slug).
- Sign-in: After sign-in, create Checkout Session (paid) or call POST /v1/tenants (free). success_url/cancel_url to demo www.
- User vs system subdomain: User =
{subdomain_slug}.pregoi.com(CNAME to canonical). System =tenant-{short_id}.pregoi.com. - UI/Backend split (§3.5): Demo UI in separate layer; API/client layer for Control Plane, Stripe, validation; same flow reusable for production UI.
§§4–17 (Ansible multi-tenant input/output, Pulumi vs pipeline DNS, API Key, Zuplo metadata, D1 schema gaps, flow diagram, §10 implementation checklist, §11 Frappe required fields post-payment, §12 user registration/additional_users, §13 wizard steps 1–7, §14 App/DB/Redis separation and placement, §15 billing D1/KV and user/admin UI, §16 consolidated changes: 6 steps, Step 4 fields/limits, Step 6 admin URL, §17 references) — full tables, step-by-step mapping, and checklist are in the Korean section below.
한국어 {#korean}
테넌트 온보딩 프로세스 및 데모 웹(www) 기획서
목적: Frappe 멀티테넌트 모델 기반 Prego의 tenant_id·tenant_slug 관리 체계를 정리하고, 테넌트 온보딩 플로우를 기존 기획·현행 코드 분석에 따라 문서화한다. 이어서 테스트용 데모 사용자 웹사이트(패키지 선택 → Sign-in → Stripe 결제 → Ansible/Pulumi/Zuplo 연동) 구현 범위와 요건을 정의한다.
코드 생성 없음 — 플로우 정리·기획·요건만 포함.
참조: tenant-provisioning-flow.md, provision-tenant-workflow-design.md, www-pregoi-com-signup-to-provisioning-flow.md, tenant-subdomain-dns-design.md, api-control-plane-implementation-plan.md Appendix C.
1. Prego 멀티테넌트 모델 요약
| 항목 | 내용 |
|---|---|
| 모델 | Frappe 멀티테넌트: Site 단위 DB 격리, 1 Site = 1 Tenant. |
| tenant_id | 내부 고객사 식별자. Control Plane D1 tenants_master.tenant_id PK. 현재 Stripe Webhook에서 미전달 시 tenant_${Date.now()}_${random} 생성(32자 절단). 권장: UUID로 통일 시 canonical tenant-{short_id}.pregoi.com에서 short_id = UUID 앞 8자로 일관 적용 가능. |
| tenant_slug | 사용자/노출용 슬러그. 기획서에서는 subdomain_slug와 동일 개념으로 사용 가능. {subdomain_slug}.pregoi.com 형태의 사용자 지정 서브도메인과 1:1. |
| 관리 주체 | tenant_id·tenant_slug(subdomain_slug)·canonical_hostname은 Control Plane D1에 저장; 프로비저닝 완료 후 Zuplo에는 tenant_id, slug, subdomain(또는 origin) 정리하여 등록. |
2. 현재 Prego 코드 기준 Tenant 온보딩 플로우 정리
기존 기획서(tenant-provisioning-flow, www-pregoi-com-signup-to-provisioning-flow, provision-tenant-workflow-design)와 현행 코드를 기준으로 한 단계별 플로우이다.
2.1 전체 시퀀스 (가입 → 패키지 → 결제 → 프로비저닝 → Zuplo)
[데모 웹 www] 패키지 선택 (무료 / 유료) ↓[Sign-in] 포털 사용자 인증 (Gmail SSO 또는 이메일) ↓[유료 시] Stripe Checkout → 결제 완료[무료 시] Control Plane POST /v1/tenants 직접 호출 ↓Stripe Webhook (checkout.session.completed) 또는 POST /v1/tenants ↓Control Plane: tenant_id 확정, D1 tenants_master(Pending), provision_jobs(Pending) 삽입 ↓배치 결정 (decidePlacement) → target_server_id 또는 create_new_server ↓workflow_dispatch (job_id, tenant_id, region, ...) ↓[전 구간 자동화] Pulumi(필요 시) → Ansible(multi-tenant 생성, API Key 발급) → Cloudflare DNS(서브도메인) → Zuplo Sync → Control Plane 콜백 ↓provision_jobs=Completed, tenants_master=Active, tenant_runtime·TENANT_ORIGINS(KV) 갱신2.2 단계별 담당·입출력 (현행·기획 대조)
| 단계 | 담당 | 입력 | 출력 | 현행 코드/기획 |
|---|---|---|---|---|
| 1. 패키지 선택 | 데모 웹 (apps/www) | — | plan_tier (free | basic | professional | enterprise), (선택) requested_region, subdomain_slug | 기획: www-pregoi-com §3.3. 구현: 데모용 www 폴더 신규. |
| 2. Sign-in | 데모 웹 + Auth | 이메일 또는 OAuth | 포털 사용자 세션, (선택) 사용자→tenant 소유권 | 기획: §3.2. Gmail SSO·이메일 가입 미구현. |
| 3. 결제(유료) | Stripe Checkout | plan_tier, metadata(tenant_id, plan_tier, requested_region, subdomain_slug) | checkout.session.completed | Control Plane: webhooks-stripe.ts. metadata에 subdomain_slug 추가 시 D1·워크플로 전달 필요. |
| 3’. 무료 | Control Plane API | POST /v1/tenants { tier, region, subdomain_slug? } | 202 + job_id | tenants.ts. subdomain_slug 수용 시 D1 저장. |
| 4. Webhook·큐 적재 | Control Plane | Stripe event / POST /v1/tenants | tenant_id, job_id, provision_jobs(Pending) | webhooks-stripe.ts: tenant_id 생성, tenants_master INSERT, provision_jobs INSERT. 갭: tenants_master에 subdomain_slug·canonical_hostname 컬럼 없음 → 마이그레이션 필요. |
| 5. 배치 결정 | decidePlacement | tenant_id, plan_tier, region | target_server_id | create_new_server, region | placement.js. |
| 6. 트리거 | Control Plane | job_id, tenant_id, region, target_server_id, create_new_server | workflow_dispatch 호출 | webhooks-stripe.ts. 전달 보강: subdomain_slug, canonical_hostname(tenant_id 기반 계산) 전달 시 워크플로에서 DNS·Zuplo에 활용. |
| 7. Pulumi | prego-pulumi / 워크플로 | region, (선택) create_new_server | server_ip, (노드용 DNS node-01.pregoi.com 등) | 테넌트별 DNS는 Pulumi가 아닌 **프로비저닝 단계(Cloudflare API)**에서 수행(tenant-subdomain-dns-design §9). |
| 8. Ansible | prego-ansible | server_ip, tenant_id, db_root_password, (선택) plan_tier·plan_limits | Frappe 사이트(canonical_hostname = tenant-{short_id}.pregoi.com), Administrator API Key | playbook.yml: tenant_id, canonical_hostname, site_name. artifacts/tenant_api_key.txt. |
| 9. Cloudflare DNS | 워크플로/스크립트/Worker | tenant_id, canonical_hostname, subdomain_slug | ① canonical A/CNAME: tenant-{short_id}.pregoi.com → 오리진 ② 사용자 CNAME: {subdomain_slug}.pregoi.com → canonical | tenant-subdomain-dns-design 방식 C. 현행: Pulumi는 노드용 DNS만. 테넌트 DNS는 프로비저닝 파이프라인 내 Cloudflare API 호출로 구현 필요. |
| 10. Zuplo Sync | infra/zuplo_sync 또는 동등 | tenant_id, api_key, (선택) slug, subdomain | Zuplo Consumer·API Key 등록, metadata/tags에 tenantId·slug·subdomain 정리 | tenant-provisioning-flow §4. Zuplo에 tenant_id, slug(tenant_slug), subdomain(사용자 URL) 메타 반영 요건. |
| 11. Control Plane 콜백 | provision-complete | job_id, tenant_id, status, host, site_name, canonical_hostname, origin_url(사용자 URL) | provision_jobs=Completed, tenants_master=Active, tenant_runtime, TENANT_ORIGINS KV | internal.ts. 이미 canonical_hostname·origin_url 수신 가능. |
2.3 식별자·도메인 규칙 (기획서 반영)
| 식별자 | 규칙 | 용도 |
|---|---|---|
| tenant_id | UUID 권장(또는 현행 tenant_* 형식). D1 PK, Ansible·Frappe site·Zuplo 식별. | 내부 일관 식별. |
| short_id | tenant_id(UUID) 앞 8자. 비-UUID면 tenant_id 앞 8자. | canonical 서브도메인: tenant-{short_id}.pregoi.com. |
| canonical_hostname | tenant-{short_id}.pregoi.com. Frappe site·라우팅·시스템 내부 고정. | Ansible site_name, TENANT_ORIGINS 키. |
| subdomain_slug (tenant_slug) | 사용자 입력. 소문자·숫자·하이픈, 예약어 제외. D1 unique. | 사용자 노출 URL: {subdomain_slug}.pregoi.com (CNAME → canonical). Zuplo slug/subdomain 메타. |
2.4 Slug·서브도메인 명 구분 및 Tenant 서브도메인 Rule
사용자는 slug와 서브도메인용 name을 구분해서 입력하며, 필요 시 둘 중 하나를 선택해 사용할 수 있게 한다.
| 구분 | 의미 | 입력·선택 |
|---|---|---|
| slug (tenant_slug) | 내부·API·라우팅용 식별자. 예: acme-corp. | 사용자 입력. 서브도메인 명과 동일하게 쓸 수 있고, 다르게 둘 수도 있음. |
| 서브도메인 명 (subdomain name) | 실제 노출 URL에 쓰이는 이름. {subdomain_name}.pregoi.com. | 사용자 입력. “slug와 동일하게 사용” 체크 시 slug 값을 그대로 서브도메인으로 사용. |
Tenant 서브도메인 Rule — 등록 가능한 서브도메인(및 slug)에 대한 규칙을 정의한다.
| 규칙 유형 | 내용 |
|---|---|
| 등록 불가 서브도메인 (예약어) | www, app, api, admin, mail, staging, dev, prego, pregoi, support, help, status, login, signup, billing, dashboard, node-01, control, internal, portal 등. 운영·시스템에서 사용하는 이름 전부. 목록은 D1 또는 설정 파일로 관리하고, 검증 API에서 조회. |
| 글자수 제한 | 최소 3자, 최대 63자 (DNS 라벨 제한). 정규식 예: ^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$. 양 끝은 영숫자, 중간은 소문자·숫자·하이픈만. |
| 불량단어 (Blocklist) | 부적절·상표·금지어 목록. 별도 테이블 또는 설정으로 관리. 입력값(또는 slug/서브도메인 명)이 목록에 포함되면 등록 불가. |
| 중복 | D1 tenants_master.subdomain_slug(및 서브도메인 명이 slug와 다를 경우 해당 컬럼) UNIQUE. 가입·변경 시 중복 검사 필수. |
| 저장 | 가입 시 subdomain_slug(slug), subdomain_name(서브도메인용, 선택 시 slug와 동일) 컬럼에 저장. DNS에는 subdomain_name(또는 slug)으로 {name}.pregoi.com 생성. |
3. 데모 사용자 웹사이트 (apps/www) 요건
테스트용 데모 사이트는 Prego 레포 내 apps/www 에 두고, 패키지 선택 → Sign-in → Stripe 결제 → (백엔드에서 프로비저닝 트리거) 까지의 흐름을 검증한다.
3.1 위치·스택
| 항목 | 내용 |
|---|---|
| 경로 | Prego/apps/www (또는 Prego/apps/demo-www). 사용자 요청대로 apps 아래 www 폴더. |
| 역할 | 테스트용 데모 웹: 패키지 선택 페이지, Sign-in, Stripe 결제 연동, (선택) job 상태 조회. |
| 배포 | Cloudflare Pages 또는 정적 호스팅. 도메인은 기존 www.pregoi.com 또는 별도 데모 서브도메인. |
3.2 SaaS 무료·유료 패키지
| 패키지 | plan_tier | 결제 | 비고 |
|---|---|---|---|
| 무료 (Free) | free | 없음. 선택 시 바로 Control Plane POST /v1/tenants 호출. | subdomain_slug 필수 입력, region 기본 sg. |
| 유료 (Basic 등) | basic | professional | enterprise | Stripe Checkout Session 생성 후 리다이렉트, 완료 시 Webhook으로 프로비저닝. | metadata에 plan_tier, requested_region, subdomain_slug, (선택) tenant_id. |
- 패키지 선택 페이지: 무료/유료 카드 UI, 플랜명·가격·요약 기능.
- 선택 시: (유료) Sign-in 후 Checkout Session 생성 API 호출 → Stripe 리다이렉트; (무료) Sign-in 후 subdomain_slug 입력 → POST /v1/tenants.
3.3 Sign-in 후 Stripe 결제 연결
- 유료 선택 시: Sign-in(또는 가입) 완료 후 백엔드에서 Stripe Checkout Session 생성.
metadata: plan_tier, requested_region, subdomain_slug (사용자 입력), tenant_id(선택, 없으면 Control Plane에서 생성).- success_url / cancel_url: 데모 www 경로.
- 사용자가 Stripe에서 결제 완료 → Stripe → Control Plane Webhook → provision_jobs 삽입 → workflow_dispatch → Pulumi → Ansible → DNS → Zuplo → 콜백.
3.4 사용자 지정 subdomain vs 시스템 내부 subdomain
- 사용자 지정 서브도메인: 가입/패키지 단계에서 입력한
subdomain_slug. 최종 URL:https://{subdomain_slug}.pregoi.com. DNS는 CNAME으로 canonical을 가리킴. - 시스템 내부 서브도메인:
tenant-{short_id}.pregoi.com. short_id = tenant_id 앞 8자. Frappe site·라우팅·Ansible site_name으로만 사용.
데모 www에서 subdomain_slug 입력 필드 제공, 중복/예약어 검증은 Control Plane API 또는 전용 검증 엔드포인트로 수행.
3.5 데모 웹 구성 원칙 (UI·Backend 분리, 상용 전환 용이)
데모 웹은 기능 개발과 플로우 시뮬레이션을 위한 코드로, UI와 Backend를 분리하여 구성한다. 목표는 나중에 상용 웹 UI로 교체가 용이하도록 하는 것이다.
| 항목 | 요건 |
|---|---|
| UI 레이어 | 데모 전용 UI 컴포넌트·페이지는 별도 디렉터리/모듈로 두고, API 호출·플로우 상태만 주입받도록 설계. 상용화 시 동일 API 스펙을 쓰는 새 UI로 교체만 하면 됨. |
| Backend 레이어 | Control Plane API 호출, Stripe Session 생성, 검증 API 등은 서비스/API 클라이언트 계층으로 분리. 데모용 Mock과 실서비스 엔드포인트를 설정으로 전환 가능하게. |
| 플로우 시뮬레이션 | 단계별 상태(패키지 선택 → 서브도메인 입력 → 결제/무료 신청 → 프로비저닝 대기)를 상태 머신 또는 단계 ID로 관리해, 상용 UI에서도 동일 플로우를 재사용하기 쉽게. |
| 교체 시 | 상용 웹 UI는 데모 UI를 교체하고, Backend/API 계층·Control Plane 연동은 그대로 두거나 환경 변수만 전환. |
4. Ansible을 통한 Multi-Tenant 생성
- 입력: server_ip, tenant_id, canonical_hostname (=
tenant-{short_id}.pregoi.com), db_root_password, (선택) plan_tier·plan_limits. - 동작: prego-ansible playbook — Docker, Frappe,
bench new-site <canonical_hostname>, Administrator API Key 발급, artifact에 api_key 기록. - 출력:
artifacts/tenant_api_key.txt(다음 단계 Zuplo Sync·콜백에서 사용).
현행 playbook.yml은 이미 tenant_id, canonical_hostname, site_name 변수와 API Key artifact를 지원함. tenant_id는 UUID 또는 Control Plane이 부여한 값 그대로 전달.
5. Pulumi로 Cloudflare에 서브도메인 등록
- 테넌트별 DNS는 Pulumi 리소스로 정적 정의하지 않고, 프로비저닝 파이프라인 내에서 Cloudflare API로 생성하는 구성을 기획서(tenant-subdomain-dns-design §9)가 권장함.
- 등록 내용
- canonical:
tenant-{short_id}.pregoi.com→ A 또는 CNAME(오리진/Tunnel). - 사용자 URL:
{subdomain_slug}.pregoi.com→ CNAME →tenant-{short_id}.pregoi.com.
- canonical:
- 담당: GitHub Actions 워크플로 단계 또는 전용 Worker/스크립트. Pulumi는 노드(서버)·공통 인프라만 담당.
6. Ansible으로 tenant_id·Administrator API Key 발급
- Ansible이 이미 수행: Frappe 사이트 생성 후 Administrator용 API Key 생성,
artifacts/tenant_api_key.txt에 기록. - tenant_id: 워크플로 입력으로 전달되며, Ansible
-e tenant_id=...로 사용. Frappe site 식별은 canonical_hostname(site_name)으로만 하면 됨.
7. Zuplo에 tenant_id·slug·subdomain 정리
- 동작: Zuplo Sync(infra/zuplo_sync.ts 또는 동등)에서 Consumer 생성·API Key 등록 시, metadata/tags에 아래를 정리해 두는 것을 요건으로 함.
- tenant_id: 내부 식별자.
- slug (tenant_slug): 사용자 서브도메인 slug.
subdomain_slug와 동일. - subdomain: 사용자 노출 FQDN.
{subdomain_slug}.pregoi.com.
- 필요 시 canonical_hostname도 메타에 포함해 라우팅·정책에서 사용 가능.
8. 데이터·스키마 보강 (D1)
현행 D1 스키마와 기획서 간 갭 반영:
| 테이블/컬럼 | 현행 | 기획 반영 |
|---|---|---|
| tenants_master | tenant_id, status, region, plan_tier, created_at, updated_at | subdomain_slug TEXT UNIQUE, canonical_hostname TEXT (또는 생성 규칙으로 런타임 계산). 마이그레이션 추가. |
| plan_tier | basic, professional, enterprise | free tier 추가. PLAN_TIERS/검증 로직에 free 포함. |
| provision_jobs | job_id, tenant_id, trace_id, region, status, plan_tier, infra_mode | (선택) subdomain_slug, canonical_hostname 컬럼으로 워크플로 전달 보강. |
9. Tenant Onboarding Process Flow (요약 다이어그램)
[데모 www] 패키지 선택(무료/유료) → Sign-in ↓유료: Stripe Checkout (metadata: plan_tier, subdomain_slug, requested_region)무료: POST /v1/tenants (tier=free, subdomain_slug, region) ↓Control Plane: tenant_id 확정(UUID 권장), tenants_master(Pending)+subdomain_slug/canonical_hostname, provision_jobs(Pending) ↓decidePlacement → workflow_dispatch(job_id, tenant_id, region, subdomain_slug, canonical_hostname, ...) ↓Pulumi(필요 시) → server_ip ↓Ansible: tenant_id, canonical_hostname → Frappe site + Administrator API Key → artifact ↓Cloudflare API: canonical A/CNAME + subdomain_slug CNAME ↓Zuplo Sync: tenant_id, api_key, slug(=subdomain_slug), subdomain(=subdomain_slug.pregoi.com) ↓POST /internal/provision-complete: job_id, tenant_id, canonical_hostname, origin_url ↓D1·KV 갱신, 테넌트 Active10. 구현 체크리스트 (데모 www·백엔드 연동)
- apps/www 폴더 생성 및 패키지 선택 페이지 (무료/유료 카드).
- 무료: subdomain_slug 입력, POST /v1/tenants 연동 (tier=free).
- 유료: Stripe Checkout Session 생성 API 연동(POST /v1/checkout), metadata에 subdomain_slug·subdomain_name·plan_tier·requested_region.
- Sign-in: PREGO_AUTH_URL 설정 시 3단계·헤더에서 로그인 리다이렉트(return_url·signin=success) 연동.
- Control Plane: tenants_master에 subdomain_slug·canonical_hostname 등 컬럼 마이그레이션; Webhook·POST /v1/tenants에서 저장·워크플로 입력으로 전달.
- 프로비저닝 워크플로: Cloudflare API 단계(canonical + 사용자 CNAME), canonical_hostname·origin_url 콜백 반영.
- Zuplo Sync: Consumer metadata에 tenant_id, slug, subdomain 정리.
- 데모 www에서 GET /v1/jobs/:id로 프로비저닝 상태 표시 및 완료 시 origin_url(https://{subdomain}.pregoi.com) 링크 안내.
- §3.5 데모 웹 UI·Backend(api.js) 분리 구조로 구현해 상용 UI 교체 용이하게 구성.
- §2.4 Slug·서브도메인 명 구분 입력·선택, Tenant 서브도메인 Rule(예약어·글자수·불량단어·중복·실시간 검사) 적용.
- §11 결제 후 Frappe tenant 필수 정보(회사명, 관리자 이메일·이름 등) 입력 폼 및 전달.
- §12 온보드 시 사용자 이메일·이름으로 등록·추가 플로우(팀원 초대). 데모 www 5단계 팀원 1명 입력 → POST /v1/tenants에 additional_users 전달, D1 tenants_master.additional_users(JSON) 저장. 프로비저닝·Ansible에서 해당 컬럼 읽어 사용자 생성 시 연동 가능.
- §13 Tenant Onboard Wizard 단계별 UI·상태 관리.
- §14 App/DB/Redis 분리, Control Plane 배치 로직에 따른 Pulumi·Ansible 서버·DB·Redis 바인딩, Cloudflare 서브도메인 자동 설정(워크플로·infra 반영).
- §15 결제 정보 D1·KV 관리, Billing 사용자 UI(프록시 연동)·시스템 관리자용 테스트 페이지(GET /internal/billing-test, GET /internal/billing?tenant_id=).
11. Frappe Tenant 생성 필수 정보 (결제 후 입력)
Frappe 테넌트(사이트) 생성을 위해 필요한 필수 정보는 결제 완료 후 단계에서 사용자에게 입력받는다. 결제 전에는 패키지·서브도메인(slug/name)만 수집하고, 결제(또는 무료 신청) 확정 후 다음 정보를 추가 수집한다.
| 필수 항목 | 설명 | 저장·전달 |
|---|---|---|
| 회사명 (법인명 전체) | 공식 문서·송장용. Frappe Company 등에 원본 유지. 최대 30자. | D1 tenants_master.company_name, Ansible/Frappe 전달. |
| 서브도메인 / 호스트(Slug) | 2단계에서 입력한 값. 5단계에서는 읽기 전용 표시. | tenants_master.subdomain_slug·subdomain_name, site_name과 동일. |
| 약어(abbr) | Frappe 계정과목 등에 사용. 2~5자, 선택. 비워두면 Frappe에서 자동 생성 가능. | D1 tenants_master.abbr, GET /internal/billing·Ansible 연동. |
| 관리자 이메일 | 테넌트 최초 Administrator 계정 이메일. | D1; Ansible에서 bench add-user·API Key 발급 시 사용. |
| 관리자 이름 | Administrator 표시명. | Ansible/Frappe 사용자 생성 시 전달. |
| 리전(선택) | 패키지 단계에서 수집. 유료 시 requested_region. | tenants_master.region, 워크플로 입력. |
입력 시점: 결제 완료(또는 무료 신청 완료) 직후 “테넌트 설정” 또는 “사이트 정보 입력” 스텝에서 폼으로 수집. 수집 완료 후 provision_jobs 삽입·workflow_dispatch 트리거.
12. 온보드 시 사용자 등록 (이메일·이름)
온보딩 과정에서 테넌트 사용자를 이메일과 이름으로 쉽게 등록·추가할 수 있게 한다.
| 요건 | 내용 |
|---|---|
| 최초 사용자 | 결제 후 입력 단계에서 “관리자 이메일·이름” 수집. 해당 사용자가 테넌트의 첫 Administrator. |
| 추가 사용자 | 온보드 위저드 내 “팀원 초대” 또는 “사용자 추가” 스텝에서 이메일·이름만 입력해 추가. (비밀번호는 초대 메일·설정 링크로 나중에 설정.) |
| 저장 | 포털 측: D1 또는 별도 User 테이블에 포털 사용자(이메일·이름·tenant_id 소유권). Frappe 측: 프로비저닝 완료 후 해당 사이트에 User 생성(Ansible 또는 Control Plane→Frappe API). |
| 간소화 | 가입 시 최소 필드만: 이메일, 이름(표시명). 추가 필드(전화번호 등)는 선택 또는 사후 프로필에서. |
이메일·이름만으로 “초대 목록”을 만들어 두고, 프로비저닝 완료 후 Frappe 사이트에 사용자 생성·초대 메일 발송하는 플로우로 정리.
13. Tenant Onboard Wizard (단계별)
테넌트 온보딩을 단계별 위저드로 정리해, 사용자가 한 번에 하나의 단계만 진행하도록 한다.
| 단계 | 제목 | 내용 |
|---|---|---|
| 1 | 패키지 선택 | 무료 / Basic / Professional / Enterprise 선택. (선택) 리전. |
| 2 | 서브도메인 | Slug·서브도메인 명 입력(또는 “slug와 동일” 선택). 실시간 유효성·중복·예약어·불량단어 검사. |
| 3 | Sign-in / 가입 | 포털 로그인 또는 회원가입(이메일·이름 등). |
| 4 | 결제(유료 시) | Stripe Checkout 리다이렉트 → 결제 완료 후 return_url으로 복귀. 무료는 이 단계 스킵. |
| 5 | 테넌트 정보 입력 | 회사명(법인명 전체, 최대 30자), 서브도메인/Slug 확인 표시, 약어(2~5자 선택), 관리자 이메일·이름. (선택) 추가 사용자(이메일·이름) 목록. |
| 6 | 확인 및 제출 | 입력 내용 요약, “시작하기” 클릭 시 provision_jobs 등록·workflow_dispatch 트리거. |
| 7 | 프로비저닝 대기 | job_id로 GET /v1/jobs/:id 폴링. 완료 시 “테넌트 준비 완료”, https://{subdomain}.pregoi.com 링크 안내. |
위저드 상태(현재 단계·입력값)는 클라이언트 상태 또는 세션/임시 저장으로 유지. 상용 UI로 교체 시 동일 단계 정의를 재사용.
14. Prego 인프라 분리 및 동적 할당 (Control Plane·Pulumi·Ansible·Cloudflare)
Prego는 Application 서버, DB, Redis가 분리되어 있으며, 자원의 동적 할당은 prego-control-plane 레포의 로직(배치 결정·노드·메트릭)에 따라 이뤄진다. Pulumi·Ansible이 특정 서버·DB·Redis 정보를 바인딩해 설치를 진행하고, Cloudflare 서브도메인 설정이 자동으로 수행되어야 한다.
| 구성 요소 | 역할 |
|---|---|
| Application 서버 | Frappe Bench·앱 실행. Pulumi로 VM 생성 또는 기존 노드(node_id) 지정. |
| DB | MariaDB. 전용 DB 서버 또는 App 서버와 분리. Ansible 인벤토리에서 db_host, db_root_password 등으로 바인딩. |
| Redis | 캐시·세션. 전용 Redis 서버 또는 공유. Ansible에서 redis_host, redis_password 등 바인딩. |
| 자원 동적 할당 | prego-control-plane의 배치 로직(예: decidePlacement, nodes, server_metrics)에 따라 기존 노드에 배치(target_server_id) 또는 신규 서버 생성(create_new_server). 워크플로 입력으로 job_id, tenant_id, region, target_server_id, create_new_server 전달. |
| Pulumi | region·create_new_server에 따라 Hetzner 등에 서버 생성, 노드 등록(POST /internal/nodes). 기존 노드 사용 시 스킵. |
| Ansible | 선정된 App 서버 IP, DB 호스트·비밀번호, Redis 호스트·비밀번호를 인벤토리/변수로 바인딩해 playbook 실행. Frappe·bench new-site·API Key 발급. |
| Cloudflare | 프로비저닝 파이프라인 내에서 서브도메인 설정 자동 수행: canonical tenant-{short_id}.pregoi.com, 사용자 URL {subdomain_name}.pregoi.com CNAME. API 호출로 레코드 생성·갱신. |
정리: 단일 서버가 아닌 App / DB / Redis 분리 아키텍처를 전제로, Control Plane이 “어디에 올릴지” 결정하고, Pulumi·Ansible이 그에 맞는 서버·DB·Redis 바인딩으로 설치·설정하며, Cloudflare DNS는 파이프라인 단계에서 자동 반영된다.
15. 결제 정보·빌링·인보이스 관리 및 UI
사용자 결제 정보는 D1에서 관리하고, KV에 기본 정보를 연동해 두며, Billing·Payment·Invoice 발행 등을 이 기반으로 진행한다. 이에 맞는 사용자용 UI와 시스템 관리자용 테스트 페이지를 둔다.
| 항목 | 내용 |
|---|---|
| D1 관리 | 결제·구독·고객 정보: billing_customers, subscriptions, invoices(또는 동일 목적 테이블). Stripe customer_id, subscription_id, plan_tier, 결제 수단 메타 등. tenants_master와 tenant_id로 연결. |
| KV 연동 | 테넌트 기본 정보·상태·플랜 요약을 KV에 캐시(예: tenant:{tenant_id}:billing). 엣지·API에서 빠른 조회, D1이 Source of Truth. |
| Billing·Payment·Invoice | Stripe Webhook으로 결제·구독·인보이스 이벤트 수신 → D1 갱신. 인보이스 발행·다운로드 링크는 D1·Stripe 기반으로 제공. |
| 사용자 UI | 내 결제 정보: 등록된 결제 수단, 현재 플랜, 구독 상태. 결제 내역·인보이스: 목록·다운로드. 플랜 변경·해지: 업그레이드/다운그레이드/해지 신청. 데모 www 또는 상용 포털의 “Billing” 섹션으로 제공. |
| 시스템 관리자용 테스트 페이지 | 테스트·검증용: 테넌트별 결제 상태 조회, Stripe Webhook 재발송·테스트, 인보이스 목록·상태 확인, (선택) Mock 결제·테스트 구독 생성. prego-control-plane 대시보드 확장 또는 별도 /admin/billing-test 등 보호된 경로. |
요약: 결제·빌링·인보이스는 D1 + KV로 관리하고, 사용자용 Billing UI와 관리자용 테스트 페이지를 기획에 포함한다.
16. 온보딩 상세 변경 사항 (통합)
아래는 onboarding-plan-limits-team-invite-step6-admin-plan, onboarding-step2-removal-step5-changes-plan, onboarding-step4-company-subdomain-abbr-admin-name-plan 3개 기획서를 통합한 요건 요약이다.
16.1 Step 2 제거 및 6단계 전환
| 변경 전 | 변경 후 |
|---|---|
| Step 2 서브도메인 별도 단계 | 삭제 — Step 4(테넌트 정보)에 통합 |
| 7단계 | 6단계: 1 패키지 → 2 로그인 → 3 결제 → 4 테넌트 정보 → 5 확인 → 6 프로비저닝 |
16.2 Step 4(테넌트 정보) 필드·UI
| 항목 | 요건 |
|---|---|
| 회사명 | placeholder “company pte ltd”. 최대 60자. 소문자 저장. |
| 서브도메인 | 초기 빈 값. 회사명 입력 시 첫 단어 기반 자동 채움(소문자). 수정 가능. |
| 약어 | 초기 빈 값. 회사명 첫 2글자 대문자 자동 채움. 2~5자. |
| 관리자 | First name / Last name 분리. 전화번호(선택). |
| 팀원 추가 | ”팀원 추가” 버튼(회색). 최대 인원 도달 시 버튼 숨김. |
16.3 플랜별 팀원 초대 한도
| 패키지 | 최대 팀원 |
|---|---|
| Free, Basic | 5 |
| Professional, Enterprise | 10 |
16.4 Step 6 프로비저닝 대기·관리자
- GET /v1/jobs/:id 폴링, Completed 시 origin_url 표시. Failed 시 에러 메시지·관리자 안내.
- 관리자 URL: /internal/dashboard, /internal/billing-test, /internal/onboarding-status, /internal/onboarding-dashboard (Runbook 참조).
17. 참조 문서
| 문서 | 용도 |
|---|---|
| tenant-provisioning-flow.md | Stripe→큐→배치→전 구간 자동화 흐름. |
| provision-tenant-workflow-design.md | 워크플로 입출력·Ansible·Zuplo·콜백 스펙. |
| www-pregoi-com-signup-to-provisioning-flow.md | www 접속→가입→패키지→결제→리소스 할당 단계별 구현 상태. |
| tenant-subdomain-dns-design.md | 방식 C(canonical + 사용자 CNAME), 검증 규칙, DNS 담당. |
| api-control-plane-implementation-plan.md Appendix C | 테넌트 생애주기·상태 전이. |