www_site/docs/phase-4-report.md

5.9 KiB
Raw Blame History

Phase 4 CMS 基础报告

日期2026-09-09 范围P4-01 至 P4-03 状态CMS 数据边界、模型、公共内容 API、可复现迁移、受控管理员、访问控制与真实内容闭环均已验收通过。

1. 已完成

数据边界与模型

  • 新增独立 Payload CMS 应用:apps/cms
  • CMS 使用独立 PostgreSQL 数据库 kaotings_cms 和独立数据库账号,不读取业务 API 用户表。
  • 已配置 systemd 服务 kaotings-cms,仅监听 127.0.0.1:3001
  • Caddy 保留 /cms 外部边界,不向公网开放 CMS 回环端口。
  • 建立模型:UsersProductsMediaSiteSettingsHomeContent
  • Product 支持 draftpublishedunpublishedslug、分类、摘要、描述、媒体、features、specs、SEO 和排序字段。
  • Media 为受控上传集合,普通访客只读媒体,写入需要 CMS 用户。

公共内容 API 与 Caddy 前缀

  • Next.js basePath 为 /cmsCaddy 用 handle /cms/* 原样透传给 CMS 服务(不剥离前缀),避免资源地址/重定向丢失 /cms
  • /cms/admin/cms/admin/create-first-user 可正常渲染 Payload 管理界面200资源以 /cms/_next/... 加载正常,无重定向循环。
  • 公共 Products API GET /cms/api/products 返回 200 和空集合,未泄露草稿。
  • Website 内容适配器已切换到 CMS 公共 APIlib/content.tsgetProducts?where[status][equals]=published 拉取,cache: no-storeCMS 未发布产品时显示明确空状态。

数据库初始化固化P4-01

  • 核对执行记录:payload_migrations 仅含 20260909_100120_initialbatch 1与现有 kaotings_cms 结构一致。
  • 已重新生成初始迁移并修正类型导入,命名为 20260909_100120_initial 以对齐已应用记录,避免重复应用。
  • 迁移文件提交进仓库:apps/cms/src/migrations/20260909_100120_initial.ts
  • 使用独立临时库 kaotings_cms_verify 从仓库 payload migrate 重建,得到 14 张表,列类型比对 SCHEMA_IDENTICAL;随后删除临时库,现有 kaotings_cms 未清空。
  • 迁移类型导入已修正(MigrateUpArgs/MigrateDownArgsimport typesql 用值导入),本地与服务端构建均通过。

受控初始化 CMS 管理员P4-02

  • 通过一次性受控路由 apps/cms/src/app/api/seed-admin/route.ts 初始化管理员,凭据来自 /etc/kaotings/cms-admin.secretsroot-only 0600未写入 Git/报告/日志。
  • 路由以 x-cms-seed-token 守卫,且仅在用户表为空时创建;已存在用户时返回 409 {"ok":false,"error":"user exists"},防止重复初始化。
  • 管理员:tech@kaotings.comdisplayName: CMS Admin)。登录验证 200 {"message":"Authentication Passed"}
  • 修正seed 路由不得在 Next 运行时调用 payload.destroy()(会销毁共享数据库连接池,导致后续请求 500

访问控制验证P4-02

  • 匿名 POST /cms/api/users/first-register403(已有用户后 create-first-user 失效)。
  • 匿名 POST /cms/api/users(注册)→ 403
  • 匿名 GET /cms/api/usersGET /cms/api/users/1(读取用户资料)→ 403
  • 匿名 PATCH /cms/api/users/1(提权/修改)→ 403
  • 匿名 GET /cms/api/accesscanAccessAdmin:false(枚举到字段级权限,非仅状态码)。
  • 管理员 GET /cms/api/accesscanAccessAdmin:true
  • 普通网站用户属独立数据库 kaotings(角色 kaotings_appCMS 使用 kaotings_cms 且签发自有 payload-token JWT网站 kaotings_session cookie 不会被 CMS 接受,跨库无法获取 CMS 管理会话。

真实内容闭环P4-03

  • 以管理员登录后创建明确标注的测试产品slug p4-test-draft-product
    • 草稿:公共列表/API docs:[],官网 /products 与详情不显示。
    • 发布:GET /cms/api/products 返回该产品;官网 /products 列表显示,详情页渲染正确。
    • 修改:更新 summary/name 后,公共列表与官网详情立即反映新内容(no-store,无缓存陈旧)。
    • 下架:status:unpublished 后,公共列表/API 为空,官网详情 404 Not Found
  • 测试产品已下架(当前 status:unpublished),不对公网提供内容。

2. 修复的集成问题

  • /cms/admin 前缀重定向/404Next 恢复 basePath: "/cms"Caddy 改用 handle /cms/* 透传,不再由 Caddy 剥离前缀。
  • Website CMS_PUBLIC_API_URL 需指向 http://127.0.0.1:3001/cms(此前缺 /cms 前缀导致官网 /products 500已更新 kaotings-web.service 环境变量。
  • Payload Admin 的 serverFunction/importMap 接线:改用生成的真实 importMap@payloadcms/next/layoutshandleServerFunctions
  • 产品 slug 查询统一使用已验证的筛选方式:GET /cms/api/products?where[slug][equals]=<slug>(返回 200。Payload 的 GET /cms/api/products/:slug 是按 id 处理的路由,不是 slug 路由,任何单产品按 slug 取数都必须用 where[slug][equals]= 筛选,不得把按 id 的路由当作按 slug 使用。

3. 范围边界

  • CMS 不管理业务用户、Role/Plan、额度、TTS 任务或认证会话。
  • P4-04 至 P4-07 的用户、VIP、额度、TTS 管理界面尚未开始。
  • 旧 Big-TTS Web 不参与 CMS推理服务继续由新网站 API 调用。

4. 后续

  • 本命中遗留的按 slug 详情 REST 500/cms/api/products/:slug)已确认是 Payload 把该路径当作按 id 处理所致;公网产品展示闭环走 where[status][equals]=published 列表与按 slug 筛选,不受影响,后续不得引入按 id 路由取 slug。
  • 迁移、seed 路由与集成修复已提交仓库并通过本地/服务端构建CMS 现有数据库未清空,有受控备份(infra/scripts/backup-cms-db.sh)。
  • CMS 管理员一次性 seed 路由已下线,初始化令牌已撤销,管理员恢复流程见 docs/cms-admin-recovery.md