hotel-cms 管理後台
hotel-cms 管理後台
Section titled “hotel-cms 管理後台”飯店內部管理後台(多租戶 SaaS)。賣給多家飯店,每家登入只看到自己的資料。 是整個套裝的整合面:把 omni-bot(館內 bot)、travelplus-chatbot(網站 AI 客服)、 topreview-hotel(評論情報)接在一起,讓飯店 admin 在一個後台管理。
來源:
hotel-cms/CLAUDE.md§0。
技術棧:Cloudflare Pages(Astro 6 SSR)+ Hono API routes + Svelte 5 + Drizzle(D1)+ KV(session)+ Logto 登入。
來源:
hotel-cms/CLAUDE.md§1。
本頁是人類操作手冊。完整 step-by-step runbook 在
hotel-cms/documentation/deployment.md(D35); LLM/agent 工作合約在hotel-cms/CLAUDE.md+01-CLAUDE.md。本頁只引用,不重寫。
角色與授權(操作前必懂)
Section titled “角色與授權(操作前必懂)”| 角色 | 看得到的範圍 | 授權真源 |
|---|---|---|
platform_admin(superadmin) |
跨店,列全部飯店 | requirePlatformAdmin / isPlatformAdmin(platform API) |
manager / staff |
只看 user_hotels 裡的店 |
resolveHotelOwner(admin API,session→user→hotel+bots) |
- 多租戶鐵則:所有 DB 查詢
WHERE必含hotelId/slug過濾;無hotelId的查詢在 UI 即擋。
來源:
hotel-cms/CLAUDE.md§2、§4。
- ⚠️ superadmin 偵測走 Logto User role → id_token
rolesclaim(roles: ["superadmin"]);動 platform 授權前 [MUST] 先讀hotel-cms/CLAUDE.md§9 +TODO.md(雙路徑真源src/lib/platform-org.ts)。
環境矩陣(dev/prod 完全隔離)
Section titled “環境矩陣(dev/prod 完全隔離)”hotel-cms 採兩 checkout 完全隔離:單一真源 = origin/main,push 只在 dev checkout,prod checkout 只 pull→review→ship。
| 環境 | checkout | CF 帳號 | Pages URL | 隔離 |
|---|---|---|---|---|
| dev | /home/peter/Code/hotel-cms-dev/ |
獨立 dev 帳號 | hotel-cms-dev.pages.dev |
獨立帳號/D1/KV/Logto dev app |
| prod | /home/peter/Code/hotel-cms/ |
prod 帳號 | cms.h-ok.com |
獨立帳號/D1/KV/Logto prod app |
wrangler.toml為 per-checkout gitignored(dev/prod 不同帳號 → 不同 D1/KV ID),由./scripts/ship.sh setup從.env+wrangler.toml.example生成;committed 只留模板。
來源:
hotel-cms/documentation/deployment.md〈環境矩陣〉、hotel-cms/scripts/ship.sh。
部署(ship.sh)
Section titled “部署(ship.sh)”完整 step-by-step:
hotel-cms/documentation/deployment.md〈部署指令〉。
# 首次 / fresh clone:生成 gitignored wrangler.toml + wrangler types + 裝 git hooks./scripts/ship.sh setup # 需 .env 有 PAGES_PROJECT / D1_DATABASE_NAME/ID / KV_NAMESPACE_ID
# dev(checkout: ~/Code/hotel-cms-dev/)——不要求 main+cleancd /home/peter/Code/hotel-cms-dev./scripts/ship.sh dev # tsc→astro-check→lint→test→build→部署 hotel-cms-dev.pages.dev
# prod(checkout: ~/Code/hotel-cms/)——require main+clean + 全套 gatecd /home/peter/Code/hotel-cmsgit pull --ff-only origin main # ahead>0 = 有 prod-only commit,停下查./scripts/ship.sh prod # → 部署 cms.h-ok.comship.sh 的 gate(checks())= tsc --noEmit → astro check → lint → test,任一失敗即停。
來源:
hotel-cms/scripts/ship.sh、hotel-cms/documentation/deployment.md〈部署指令〉。
全域政策(2026-07-17 鬆綁):直接 npm run deploy / wrangler pages deploy 也是允許路徑;ship.sh 是可選保險(內建 gate),重大變更建議走。
Secrets 與變數
Section titled “Secrets 與變數”完整表:
hotel-cms/documentation/deployment.md〈Secrets 與變數〉;變數交叉一致見hotel-cms/documentation/variables.md。
Secrets(wrangler pages secret put,只 server via @/lib/runtime):
| Secret | 用途 | 輪換 |
|---|---|---|
SESSION_SECRET |
KV session cookie 簽章 | 定期 |
OMNIBOT_ADMIN_TOKEN |
呼叫 omni-bot admin API 的 Bearer | 與 omni-bot 同步 |
TPH_PROVISION_TOKEN |
呼叫 topreview-hotel provision | 與 topreview 同步 |
DEEPSEEK_API_KEY |
DeepSeek AI 文案/標籤 | 依 DeepSeek |
SERVICE_TOKEN / PLATFORM_TOKEN |
provision / platform 內部 service-token 閘 | 定期 |
LOGTO_APP_ID/_SECRET |
OIDC Traditional Web app 憑證 | 與 Logto app 同步 |
LOGTO_M2M_ID/_SECRET |
Logto Mgmt API(成員邀請/預建帳號) | 與 Logto M2M 同步 |
LOGTO_WEBHOOK_SECRET |
inbound Logto webhook HMAC | 與 webhook 同步 |
Vars(wrangler.toml [vars],皆強制 https):OMNIBOT_BASE_URL(必填)、LOGTO_ENDPOINT(https://auth.h-ok.com)、PLATFORM_ORG_ID(必填;未設 → org-based 路徑 fail-closed)、DEEPSEEK_BASE_URL、REVIEWS_BASE_URL/TPH_BASE_URL(optional)。
⚠️ secret 輪換後必須 redeploy——舊 deployment 拿不到新綁定。 來源:
hotel-cms/documentation/deployment.md〈維運 Runbook〉。
Migration(D1 schema)
Section titled “Migration(D1 schema)”migration 先行於 worker deploy(drizzle/*.sql,目前 0000–0006)。schema 變更後 prod 部署 [MUST] 跑:
npm run db:migrate:all # apply 全部到 remote(prod D1)# 或單檔:wrangler d1 execute DB --remote --file=./drizzle/<n>.sql --yesnpm run db:migrate:local # local(dev D1)- 多數 migration 是
ADD COLUMN/CREATE TABLE IF NOT EXISTS(向前相容)→ code 容忍欄位缺失(shadow 共存不 crash),但功能在 apply 前不生效。
來源:
hotel-cms/documentation/deployment.md〈Migration 順序〉〈回滾程序〉。
-
GET /回 200;GET /api/auth/me(未登入)回{ user: null }200 - superadmin 登入 →
/api/auth/me回role: "platform_admin"+roles: ["superadmin"] -
/admin列出飯店清單(platform_admin 列全部) - 任一飯店 dashboard 可開、complaint list 可讀
- Logto PostResetPassword webhook:合法簽章 → 200、錯簽 → 401
⚠️ auth/session 類變更上線後,既有使用者需登出再重新登入(舊 KV session 24h TTL 內不生效)。 來源:
hotel-cms/documentation/deployment.md〈部署後驗證〉。
整合的服務(誰呼誰)
Section titled “整合的服務(誰呼誰)”| 服務 | 接點 | 憑證 |
|---|---|---|
| omni-bot | admin API /admin/tenants/:storeId/...;一家飯店 = omni-bot 3 個 storeId(館內/客訴/評論) |
hotel-cms 持 OMNIBOT_ADMIN_TOKEN(Bearer);omni-bot 憑證不出 omni-bot |
| topreview-hotel | 評論情報 dashboard(獨立部署);provision 走 TPH_BASE_URL |
TPH_PROVISION_TOKEN |
| travelplus-chatbot-api | 網站 AI 客服(管理 API) | (見 hotel-cms/CLAUDE.md §8) |
- content PUT 後清 KV tenant cache(omni-bot admin-worker 負責)。
來源:
hotel-cms/CLAUDE.md§8。
- log:Cloudflare Pages dashboard → project → Functions logs / Real-time logs;或
wrangler pages deployment tail。 - KV session 解析失敗:
console.error帶[session]前綴;Logto callback JIT 失敗 log[logto callback](只記 sub + errType,不記 PII)。 - 作廢某 user 全部 session(移除成員/改密碼/緊急):程式
invalidateUserSessions(userId);手動wrangler kv key delete --binding SESSIONS "session:<sid>"。 - 開新飯店 onboarding:
/platform/hotels/new(platform_admin)或scripts/provision-logto-demo.ts;模式判斷decideOnboardMode(hotelExists)。
來源:
hotel-cms/documentation/deployment.md〈維運 Runbook〉。
合規紅線(操作時不可違)
Section titled “合規紅線(操作時不可違)”- 🔴 禁 review gating——surveyResponses 設計(≤2 recovery、≥4 review),CMS 編輯守 invariant,不可引導留好評。
- 🔴 訂房不歸因——KPI/dashboard 不得出現訂房歸因數字。
- 🔴 回覆速度賣服務 SLA,不賴排名因子。
- 🔴 多租戶隔離——
hotelId查詢邊界。
來源:
hotel-cms/CLAUDE.md§7。
真源(file-level cite)
Section titled “真源(file-level cite)”| 本頁段落 | repo 真源 |
|---|---|
| 概述/技術棧/架構決策/紅線/整合 | hotel-cms/CLAUDE.md |
| 環境矩陣/部署/secrets/migration/驗證/runbook | hotel-cms/documentation/deployment.md |
| 變數交叉一致 | hotel-cms/documentation/variables.md |
| ship.sh 三模式 + gate | hotel-cms/scripts/ship.sh |
| 快速開始 | hotel-cms/README.md |
維護者:本頁內容變更時 [MUST] 同步確認上述 repo 檔仍指向正確段落,並更新
last_verified_head。跑npm run check:cites驗證。