5.9 KiB
5.9 KiB
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 回环端口。 - 建立模型:
Users、Products、Media、SiteSettings、HomeContent。 - Product 支持
draft、published、unpublished,slug、分类、摘要、描述、媒体、features、specs、SEO 和排序字段。 - Media 为受控上传集合,普通访客只读媒体,写入需要 CMS 用户。
公共内容 API 与 Caddy 前缀
- Next.js basePath 为
/cms,Caddy 用handle /cms/*原样透传给 CMS 服务(不剥离前缀),避免资源地址/重定向丢失/cms。 /cms/admin、/cms/admin/create-first-user可正常渲染 Payload 管理界面(200),资源以/cms/_next/...加载正常,无重定向循环。- 公共 Products API
GET /cms/api/products返回200和空集合,未泄露草稿。 - Website 内容适配器已切换到 CMS 公共 API;
lib/content.ts的getProducts以?where[status][equals]=published拉取,cache: no-store,CMS 未发布产品时显示明确空状态。
数据库初始化固化(P4-01)
- 核对执行记录:
payload_migrations仅含20260909_100120_initial(batch 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/MigrateDownArgs用import type,sql用值导入),本地与服务端构建均通过。
受控初始化 CMS 管理员(P4-02)
- 通过一次性受控路由
apps/cms/src/app/api/seed-admin/route.ts初始化管理员,凭据来自/etc/kaotings/cms-admin.secrets(root-only 0600),未写入 Git/报告/日志。 - 路由以
x-cms-seed-token守卫,且仅在用户表为空时创建;已存在用户时返回409 {"ok":false,"error":"user exists"},防止重复初始化。 - 管理员:
tech@kaotings.com(displayName: CMS Admin)。登录验证200 {"message":"Authentication Passed"}。 - 修正:seed 路由不得在 Next 运行时调用
payload.destroy()(会销毁共享数据库连接池,导致后续请求 500)。
访问控制验证(P4-02)
- 匿名
POST /cms/api/users/first-register→403(已有用户后 create-first-user 失效)。 - 匿名
POST /cms/api/users(注册)→403。 - 匿名
GET /cms/api/users、GET /cms/api/users/1(读取用户资料)→403。 - 匿名
PATCH /cms/api/users/1(提权/修改)→403。 - 匿名
GET /cms/api/access:canAccessAdmin:false(枚举到字段级权限,非仅状态码)。 - 管理员
GET /cms/api/access:canAccessAdmin:true。 - 普通网站用户属独立数据库
kaotings(角色kaotings_app),CMS 使用kaotings_cms且签发自有payload-tokenJWT,网站kaotings_sessioncookie 不会被 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。
- 草稿:公共列表/API
- 测试产品已下架(当前
status:unpublished),不对公网提供内容。
2. 修复的集成问题
/cms/admin前缀重定向/404:Next 恢复basePath: "/cms",Caddy 改用handle /cms/*透传,不再由 Caddy 剥离前缀。- Website
CMS_PUBLIC_API_URL需指向http://127.0.0.1:3001/cms(此前缺/cms前缀导致官网/products500):已更新kaotings-web.service环境变量。 - Payload Admin 的 serverFunction/importMap 接线:改用生成的真实
importMap和@payloadcms/next/layouts的handleServerFunctions。 - 产品 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。