feat: establish production SaaS foundation

This commit is contained in:
Codex
2026-07-12 19:26:57 +08:00
parent 1c2ce38cea
commit 39f7332f33
219 changed files with 20647 additions and 2628 deletions

View File

@@ -1,6 +1,6 @@
# Web 版上线前验收清单
更新时间2026-07-01
更新时间2026-07-11
这份清单用于先上线 H5 Web 题库。Taro 仍然是前端工程,后端以 Supabase Auth/JWT、PostgreSQL/RLS、`apps/api`、worker 为主。前端视觉和交互可以参考 `F:\project\参考\旧题库小程序前端文件` 和旧 Web 版,但不能继承旧 PocketBase 直连、旧鉴权或旧字段模型。
@@ -15,6 +15,24 @@
- 学生头像只做男女预设,不做上传、裁剪或第三方头像同步。
- 排行榜默认不请求、不展示;只有租户购买/开启活动且完成专项压测后再接独立页面。
- H5 构建目录必须包含 `index.html``js/``css/`,并且每个上线目录根部必须由部署方放置对应 `runtime-config.json`
- 三套 H5 只注册本 portal 页面;学生微信小程序使用独立 `dist/weapp-student` 目录和学生分包,不能覆盖 `dist/h5-student`
- 租户、会话、权限和主题由全局 App Provider 管理;业务缓存键按 portal、域名或 tenantCode、tenantId、userId 隔离,跨标签账号变化会触发重验,页面不得自行维护另一套长期身份状态。
## 首个平台超级管理员
生产库没有可登录平台管理员时,先在 Supabase Auth 创建或确认你的账号并取得 `auth.users.id`。只在生产运维终端运行服务器 CLI先 dry-run再使用精确确认短语写入
```bash
DATABASE_URL='<production-database-url>' \
BOOTSTRAP_PLATFORM_ADMIN_AUTH_USER_ID='<auth.users UUID>' \
BOOTSTRAP_PLATFORM_ADMIN_USERNAME='<operator username>' \
BOOTSTRAP_PLATFORM_ADMIN_NAME='<display name>' \
npm run bootstrap:platform-admin
npm run bootstrap:platform-admin -- --apply --confirm BOOTSTRAP_FIRST_PLATFORM_ADMIN
```
该命令不是公开 API不接收密码、验证码或 service role key不输出完整 Auth UUID、邮箱或手机号。写入后立即运行 `npm run readiness:production:db`,确认 active 超管、Auth 绑定和 `{"*":true}` 权限门禁通过;已有可登录超管时命令必须拒绝。
## 学生端验收旅程
@@ -76,12 +94,16 @@ npm run smoke:launch-persona
```bash
npm run test:readiness
npm run test:rls
npm run test:auth:foundation
npm run audit:runtime
npm run security:repo
npm run check:api
npm run check:worker
npm run check:taro
npm run build:taro:h5:student
npm run build:taro:h5:tenant
npm run build:taro:h5:platform
npm run build:taro:weapp:student
npm run readiness:production
npm run readiness:production:db
npm run smoke:taro:h5
@@ -90,9 +112,18 @@ node scripts/taro-h5-release-guardrails-test.js --require-dist
npm run smoke:launch-persona -- --write docs/refactor/launch-artifacts/launch-persona-smoke.json --write-md docs/refactor/launch-artifacts/launch-persona-smoke.md
```
`test:rls` 和完整 API/worker 集成测试是会写入数据的测试套件只允许连接本地、CI 或生产 schema/脱敏快照的隔离克隆库。克隆库必须在 `app_private.environment_safety` 显式标记 `environment='test'|'ci'``allow_destructive_tests=true`,然后单独留存动态 RLS 证据:
```bash
DATABASE_URL='<isolated-rls-clone-url>' \
npm run test:rls > docs/refactor/launch-artifacts/rls-tenant-isolation.log
```
禁止在 `source /etc/tiku-saas/api.env` 后直接运行 `test:rls``test:api``test:worker:*`。真实生产数据库只执行 `readiness:production:db` 等只读门禁;真实 API 验收使用无 seed 的 `smoke:auth:remote`、受控灰度租户 `smoke:launch-persona` 等专用脚本。
`smoke:launch-persona` 是真实 API 角色旅程烟测,必须进入生产上线证据;正式证据必须使用 `LAUNCH_SMOKE_AUTH_MODE=app_session`,通过 Bearer `tk_` session 验证身份,不使用旧 `x-user-id` 或平台本地 key。普通学生要能在 SVIP 权益下创建练习、答题、收藏题目、进入收藏复习和错题复习入口;租户管理员要能读取看板/主题/学生/销售转化并拒绝学生或跨租户访问;平台管理员要能读取租户/套餐/审计入口并拒绝学生访问平台后台。生产证据命令必须带 `--write docs/refactor/launch-artifacts/launch-persona-smoke.json`,确保 `production-launch-evidence.json` 引用的 artifact 是稳定路径,不是只存在带时间戳的本地报告。
`smoke:taro:h5` 会启动临时静态服务器和 mock API验证三套 H5 的 `index.html`、JS/CSS 资源、history fallback、公开 runtime config 和 `/api/tenant/resolve` 契约。`smoke:taro:h5:interaction` 会在真实 Chrome/Edge 中点击学生、租户后台、平台后台关键路径,覆盖静态烟测发现不了的 JS 运行时、直接 history 路由刷新和 Taro 点击事件问题;当前脚本覆盖 32 项检查,包括学生首页、题库、答题、收藏、错题/收藏复习、背单词、知识手册、资料短签名和水印、视频播放授权、分数线、AI 择校、消息中心、会员收银台下单/支付参数/订单状态,租户后台内容导入、公共题库采纳/同步/冲突处理、学生运营、营销/CRM/分佣、主题/角色/成员写操作,以及平台后台租户、账务、公共题库授权和员工写操作。`taro-h5-release-guardrails-test` 会扫描源码、三套 H5 产物和 runtime-config 边界,防止旧 PocketBase、`x-user-id``x-platform-admin-key`、数据库连接串和服务端密钥形态进入前端发布目录。若刚构建完但未放入真实 `runtime-config.json`,脚本允许 warning正式部署目录必须补齐。
`smoke:taro:h5` 会启动临时静态服务器和 mock API验证三套 H5 的 `index.html`、JS/CSS 资源、history fallback、公开 runtime config 和 `/api/tenant/resolve` 契约。`smoke:taro:h5:interaction` 会在真实 Chrome/Edge 中点击学生、租户后台、平台后台关键路径,覆盖静态烟测发现不了的 JS 运行时、直接 history 路由刷新和 Taro 点击事件问题;当前脚本覆盖 33 项检查,包括学生未登录 401、首页、题库、答题、收藏、错题/收藏复习、背单词、知识手册、资料短签名和水印、视频播放授权、分数线、AI 择校、消息中心、会员收银台下单/支付参数/订单状态,租户后台内容导入、公共题库采纳/同步/冲突处理、学生运营、营销/CRM/分佣、主题/角色/成员写操作,以及平台后台租户、账务、公共题库授权和员工写操作另有跨门户移动视口、Input 挂载竞态和 Button loading 200 次稳定节点探针`taro-h5-release-guardrails-test` 会扫描源码、三套 H5 产物和 runtime-config 边界,防止旧 PocketBase、`x-user-id``x-platform-admin-key`、数据库连接串和服务端密钥形态进入前端发布目录。若刚构建完但未放入真实 `runtime-config.json`,脚本允许 warning正式部署目录必须补齐。
写入生产上线证据时,三套正式发布目录必须先放入真实公开 `runtime-config.json`,再运行严格模式:
@@ -103,6 +134,15 @@ node scripts/taro-h5-release-guardrails-test.js --require-dist --require-runtime
npm run smoke:launch-persona -- --write docs/refactor/launch-artifacts/launch-persona-smoke.json --write-md docs/refactor/launch-artifacts/launch-persona-smoke.md > docs/refactor/launch-artifacts/launch-persona-smoke.log
```
每个 `checks[].artifact` 都必须同时填写真实 `artifactSha256`。三端静态目录已经切换到正式域名后,部署脚本应显式再执行严格线上校验:
```bash
LAUNCH_GATE_VERIFY_LIVE_H5=true \
npm run launch:gate -- --evidence /etc/tiku-saas/production-launch-evidence.json --verify-live-h5
```
证据中的 `liveH5.releaseManifestArtifact` 指向本次候选 `taro-h5-release-manifest.json``liveH5.releaseManifestSha256` 记录该 manifest 文件的 SHA-256。严格模式会请求学生端、租户后台和平台后台各自的 `index.html``runtime-config.json` 和主 app bundle要求 HTTP 成功、portal 正确、`apiBaseUrl` 与证据中的生产 HTTPS API 一致,并验证线上 index/app 哈希与候选发布目录一致。默认 launch gate 保持离线,不带显式开关时不会访问公网。
PNVS 短信登录上线前要用真实手机号跑一次远程 smoke。脚本不会读取或输出密钥它只调用公网 API发送验证码后在终端输入收到的短信验证码再确认 `/api/auth/me` 可用:
```bash
@@ -125,7 +165,19 @@ PNVS 只验收手机号登录/换绑验证码链路。催缴、营销、CRM 等
`security:repo` 是仓库自带的静态安全扫描,会拦截密钥形态、前端旧鉴权头、真实 runtime-config 和生产证据误入 Git。它不能替代真实 `@codex-security`;如插件在当前 Codex 环境暴露扫描工具,再补插件扫描结果。若工具不可用,不能把该项标记为已完成,只能在上线证据里标记为待补。
`readiness:production``readiness:production:db` 是生产阻断门禁:会拒绝 mock/未知短信 provider、弱密钥、`CORS=*`、旧身份头、local_dev 存储、非 HTTPS 对象存储公开 URL、阿里云 OSS 内网直签、未接外部资源扫描、localhost webhook以及租户短信/OAuth/支付公开配置缺字段、OAuth redirectUri/支付 notifyUrl 非 HTTPS、公开配置混入密钥、active provider 缺私密 `tenant_secrets` 等问题
`readiness:production``readiness:production:db` 是生产阻断门禁:会拒绝 mock/未知短信 provider、弱密钥、`CORS=*`、旧身份头、local_dev 存储、非 HTTPS 对象存储公开 URL、阿里云 OSS 内网直签、未接外部资源扫描、localhost webhook以及生产库残留 `local/test/ci``allow_destructive_tests=true` 标记、运行角色未通过 superuser bootstrap、租户公开配置缺字段/混入密钥、OAuth redirectUri/支付 notifyUrl 非 HTTPS、active provider 缺私密 `tenant_secrets`、active 租户公开 URL 指向 localhost/HTTP、缺少可登录平台管理员等问题。尚未发布主题只 warning 并回退平台默认主题,正式上线前仍要逐租户确认
首次应用数据库迁移前,由 self-hosted PostgreSQL/Supabase 的真正 superuser 执行一次:
```bash
DATABASE_ADMIN_URL='<secret-managed-superuser-url>' \
npm run bootstrap:db-runtime-roles -- \
--apply --confirm=BOOTSTRAP_BACKEND_RUNTIME_ROLES
```
该管理员连接只用于集群角色和官方 Supabase 基础镜像 `public` 扩展函数 ACL bootstrap不得进入 API/Worker 配置或发布证据。普通 migration 会验证 `tiku_api/tiku_worker` 已是 `LOGIN NOINHERIT NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION BYPASSRLS` 且无父角色,并在 `anon/authenticated` 仍可执行任何 `public` 函数时直接阻断。每次 Supabase 镜像或扩展升级后都要重跑该幂等 bootstrap 与数据库 readiness。
微信小程序产物还必须在微信开发者工具和至少一台真机验证:打开 `apps/taro/dist/weapp-student`确认主包只保留启动页、学生页面位于分包、tenantCode 能解析正确租户、短信/微信登录能建立当前租户会话,并完成刷题、支付调起、文件预览、音视频和分享能力。租户后台与平台后台本阶段只发布桌面 H5。
生产 API 推荐:
@@ -133,9 +185,15 @@ PNVS 只验收手机号登录/换绑验证码链路。催缴、营销、CRM 等
ALLOW_LEGACY_AUTH_HEADERS=false
ALLOW_PLATFORM_ADMIN_KEY=false
CORS_ORIGIN=https://student.example.com,https://tenant-admin.example.com,https://platform-admin.example.com
CORS_TENANT_DOMAINS_ENABLED=true
CORS_TENANT_DOMAIN_CACHE_TTL_MS=60000
CORS_TENANT_DOMAIN_NEGATIVE_CACHE_TTL_MS=10000
CORS_TENANT_DOMAIN_CACHE_MAX_ENTRIES=10000
AUTH_SMS_PROVIDER=aliyun-pnvs
```
`CORS_ORIGIN` 中的学生/租户后台域名只适用于平台自营的少量固定 Origin合作租户自定义域名必须由 `active tenant_domains + active tenants` 动态准入。上线验收必须同时验证 active Origin 通过inactive/unknown Origin 的 OPTIONS 和普通请求返回 `403 CORS_ORIGIN_DENIED`,伪造 Host 无法绕过,且无 Origin 的 `/health` 仍可用。
生产 worker 推荐:
```text