Deployment
瑞成 PMS 以單一 Next.js 服務(next-app/)部署。主要目標是 Zeabur; GCP Cloud Run + Cloud SQL 為備援路線。deploy-config skill 兩條路都有說明。
三個環境(dev → stg → prd)
每個環境各有一個 git 分支與一個 Zeabur 專案,各自擁有獨立的 PostgreSQL(資料庫從不跨環境共用):
| 環境 | 分支 | 網址 | 資料 |
|---|---|---|---|
| dev | dev | https://dev-pms.rei-chen.com | db:seed 完整示範資料;ENABLE_DEMO_LOGIN=true |
| stg | stg | https://stg-pms.rei-chen.com | db:seed-admin + db:seed-baseline,無示範資料 |
| prd | prd | https://pms.rei-chen.com | 同 stg;只在使用者明確下指令後才部署,且先 pg_dump 備份 |
晉升順序固定 dev → stg → prd,不跳級。三個環境的專案/服務/環境 ID 與連結見 docs/deployment/zeabur-environments.md。目前三個環境都在同一台 2C4G 伺服器上(6 個服務),執行期餘裕很小。
發版與晉升的實際指令(v1.3.0 起腳本化)
在乾淨的 origin/main checkout(不要在有未提交變更的工作樹)執行,順序固定:
# 1. 資料庫:先驗證過的備份(pg_dump → ~/pms-backups/),再 migrate;暫時開的對外連接埠一定會關掉
scripts/deploy/zeabur-db.sh <dev|stg|prd> backup
scripts/deploy/zeabur-db.sh <dev|stg|prd> migrate # HEAD 必須等於 origin/main,否則拒絕;prd 另需 CONFIRM_PRD=yes
scripts/deploy/zeabur-db.sh <dev|stg|prd> sql scripts/checks/frozen-date-scan.sql # 唯讀稽核掃描
# 2. 應用:一定從 next-app/ 上傳(不是 repo 根目錄,否則會建出 404 的 app)
cd next-app && zeabur deploy --project-id <PID> --service-id <SID> --environment-id <EID> -i=false
# 3. 驗證:真的登入 + 核心頁 + 執行期 log + 側欄版本,不是只看 200
scripts/deploy/verify-env.sh <env> --version <x.y.z>
# 4. 紀錄:把 dev/stg/prd 分支併到與 main 同一棵樹(純 merge,不 force)
scripts/deploy/sync-env-branches.sh dev stg prd- migration 不會在啟動時自動執行。
next-app的start就是next start,執行期映像是 Next.js standalone(沒有 pnpm、沒有 drizzle-kit),所以必須由zeabur-db.sh migrate從本機對資料庫開暫時連接埠執行。先 migrate、再部署程式;migration 設計為純新增,舊版程式在新 schema 上仍可運作,回滾才安全。 zeabur-db.sh與rotate-pg-password.sh不會把密碼印出來。輪替資料庫密碼用scripts/deploy/rotate-pg-password.sh <dev|stg>:新密碼只存在~/pms-secrets/<env>-postgres-password.txt(權限 600,請移進密碼管理員後刪檔)。- 要把使用者名冊 CSV 匯入 stg/prd(不必先登入 App、同樣的稽核軌跡):
scripts/deploy/import-roster.sh <env> <csv> [--dry-run|--verify] [--expect-badges],流程與 prd 規則見docs/deployment/roster-import.md。 *.zeabur.app網域仍可用,但文件與連結一律用*.rei-chen.com。- 使用者手冊(
user-docs/)的 Cloudflare Pages 部署目前因 GitHub Actions 帳務問題無法由 CI 執行,需手動npx wrangler@4 pages deploy;修好前,每次手冊變更都要手動發佈。
Zeabur Deployment
Environment Variables (set in Zeabur before build)
# Required (runtime)
DATABASE_URL=postgresql://user:pass@host:5432/db
AUTH_SECRET=<openssl rand -base64 32>
AUTH_URL=https://your-app.zeabur.app
AUTH_TRUST_HOST=true
# Optional — build-time (baked into the bundle)
NEXT_PUBLIC_APP_URL=https://your-app.zeabur.app
NEXT_PUBLIC_SENTRY_DSN=https://...@sentry.io/...瑞成 PMS 以工牌編號 + 密碼(Credentials)登入,不使用 OAuth;也沒有金流 (Stripe / ECPay 等 billing 功能已從產品移除)。因此無需設定 OAuth 或 payment provider 環境變數。
執行期開關(皆為執行期環境變數,切換不需重新建置):ENABLE_DEMO_LOGIN(只開在 dev)、 ENABLE_API_DOCS(prd 不開)、ENABLE_LOGIN_LOCKOUT(預設開)、NOTIFICATION_SCAN_CRON(覆寫預設的每小時整點燈號掃描,時區 Asia/Taipei)。
NEXT_PUBLIC_*are baked at build time — set them in the Zeabur dashboard before the build runs.AUTH_SECRET,DATABASE_URL, and other secrets are read at runtime; keep them out ofNEXT_PUBLIC_*.
Deploy Steps
- Push your repo to GitHub.
- Create a Zeabur project → Add Service → point at
next-app/. - Zeabur auto-detects Next.js via
next-app/zbpack.json. - Set environment variables in the Zeabur dashboard.
- Trigger a deploy from
next-app/(zeabur deploy;Zeabur 會忽略.gitignore,靠next-app/.zeaburignore排除node_modules/、.next/,否則上傳超過方案的 250 MB 上限)。Health check:https://your-app.zeabur.app/api/health。 - 字型已自架(
@fontsource-variable/*),建置不再需要連到 Google Fonts(舊版曾因此在 Zeabur 建置失敗)。
Migrations 不會在啟動時自動執行(start 就是 next start)——請用上方的 scripts/deploy/zeabur-db.sh <env> migrate。
GCP Cloud Run + Cloud SQL
Build from next-app/Dockerfile, push to Artifact Registry, deploy to Cloud Run, and attach Cloud SQL (Postgres) via the Cloud SQL connector. lib/db.ts lazy-inits the connection so the build succeeds without DATABASE_URL — the runtime reads it from the Cloud Run env. See the deploy-config skill (Road 2) for the CLI walkthrough.
Cloudflare Pages (dev-docs)
The dev-docs/ VitePress site deploys to Cloudflare Pages via the gh-cf-deploy skill.
Build settings:
- Build command:
pnpm build - Build output directory:
.vitepress/dist - Root directory:
dev-docs - Environment variables:
VITEPRESS_API_BASE(將 API playground 指向你部署後的 Next.js app)
Docker
A root docker-compose.yml runs the whole stack (Postgres, Mailpit, Next.js app) locally:
docker compose up --build -d # app on http://localhost:3000, Mailpit on http://localhost:8025
docker compose down # stop (add -v to drop the db volume)For production Docker, build next-app/Dockerfile directly.