fix(admin): remove manual reasons and repair code creation
Independent Staging Quality Gate / validate (push) Has been cancelled
Independent Staging Quality Gate / publish (push) Has been cancelled

This commit is contained in:
Jesse_Chen
2026-08-16 16:49:05 +08:00
parent 9df43e8112
commit c712787ec2
24 changed files with 180 additions and 206 deletions
+20 -6
View File
@@ -3466,19 +3466,33 @@
- 相关记录:BUG-177、BUG-198、BUG-199
- 修复版本:本地未提交候选
## BUG-207 | 管理端业务操作重复要求邮箱验证码复核
## BUG-207 | 管理端业务操作重复要求邮箱验证码与手工原因
- 状态:resolved本地修复,未提交、未发布)
- 状态:resolved补充修复已完成,待提交与发布)
- 首次发现:2026-08-16
- 最近更新:2026-08-16
- 影响面:管理端兑换码生成/编辑/撤销、管理员角色变更、账务与订阅调整、商品保存/发布、功能开关发布、易支付设置等写操作。
- 用户现象:管理员已经登录后台并具备对应权限,执行批量生成兑换码等日常营销操作时仍需发送并等待邮箱验证码,造成重复验证与操作中断。
- 用户现象:管理员已经登录后台并具备对应权限,执行批量生成兑换码等日常营销操作时仍需发送并等待邮箱验证码;首轮修复删除邮箱验证后,业务确认弹窗仍强制填写“操作原因”,日常营销操作仍被无必要的二次输入打断。
- 根因:多个管理写入被统一接入 operation-level `requireHighRiskAdminMutation`,公共 `ReasonActionModal` 又内置权限级邮箱 OTP challenge/proof,导致账户登录安全与每次业务操作二次验证被错误叠加到所有管理业务。
- 修复:所有管理业务写入统一改用 `requireAdminMutation`,继续强制管理员会话、细粒度权限与可信 Origin;公共确认弹窗只保留必填操作原因与确认状态;删除 `/api/admin/reauth`、high-risk challenge/proof cookie 及相关 UI 参数。兑换码批量生成改为“确认生成”
- 修复:所有管理业务写入统一改用 `requireAdminMutation`,继续强制管理员会话、细粒度权限与可信 Origin;删除 `/api/admin/reauth`、high-risk challenge/proof cookie 及相关 UI 参数。公共业务确认弹窗改为无需填写任何内容的 `ConfirmActionModal`,兑换码、管理员角色、订单/订阅调整、商品、功能开关、易支付与用户重置请求体不再要求客户端 `reason`;服务端按动作注入固定审计标识并继续传给原数据库 RPC 的非空 reason 参数
- 安全边界:保留 request ID、数据库权限检查、领域 RPC、append-only 审计、最后一位 Owner 保护;保留账户级 Better Auth TOTP MFA 和一次性恢复码;普通用户登录、注册与找回密码的邮箱 OTP 不受影响。
- 验证:新增全局合同扫描,锁定管理路由、组件和公共库中不得恢复 operation-level reauth;兑换码、角色、账务、商品、功能开关、易支付、MFA 与普通登录 OTP 聚焦测试 75/75 通过`tsc --noEmit`目标 ESLint 与 `git diff --check` 通过。
- 防复发:新增管理业务时只能在登录、权限、Origin、原因和审计边界内扩展;不得把邮箱 OTP 放回公共业务确认弹窗或新建操作级 reauth API。账户级 MFA 与普通用户身份验证必须保持独立。
- 验证:管理端权限、业务确认、兑换码/账务合同、账户级 MFA 等 5 个聚焦测试文件共 47/47 通过`tsc --noEmit`17 个目标源文件 ESLint 与 `git diff --check` 通过。首轮邮箱复核移除提交已发布到 staging,但“无需手工原因”的补充修复尚未发布。
- 防复发:新增管理业务时只能在登录、权限、Origin、服务端审计标识和数据库审计边界内扩展;不得把邮箱 OTP 或手工原因输入放回公共业务确认弹窗。账户级 MFA 与普通用户身份验证必须保持独立。
- 相关记录:BUG-155、BUG-156
- 修复版本:本地未提交候选(首轮邮箱复核移除:`4f6cf5782d3871a28e531a4dff9bcc6a2633ce09`
## BUG-208 | self-hosted 管理端生成兑换码返回通用 500
- 状态:resolved(本地修复,待提交与发布)
- 首次发现:2026-08-16
- 最近更新:2026-08-16
- 影响面:self-hosted runtime 的 `POST /api/admin/codes` 批量生成兑换码;列表读取、普通用户兑换与 production 未在本次诊断中验证。
- 用户现象:具备权限的管理员提交合法点数、数量、到期时间和备注后,接口返回 `500 {"error":"后台服务暂时不可用"}`,无法生成兑换码。
- 根因:`runCodeRpc()` 将 JavaScript 对象数组直接作为 `$5::jsonb` 参数交给 `node-postgres`。实际 `pg` serializer 会把该值编码成 PostgreSQL array 文本(例如 `{"{\\"codeHash\\":...}"}`),而不是 JSON 数组;RPC 的 `jsonb` 入参因此无法按预期解析,底层错误又被通用管理错误映射收敛为 500。staging 容器原始 SQLSTATE 未取得,因此不记录未经验证的具体错误码。
- 修复:调用 `public.admin_create_redemption_codes` 前显式执行 `JSON.stringify(input.p_codes)`RPC 签名、权限、request ID、明文码仅单次返回和 append-only 审计保持不变。同时由服务端注入 `admin_console_create_redemption_codes` 审计标识,不再依赖客户端原因。
- 验证:本地使用项目实际 `pg` serializer 对比确认:对象数组会被编码为 PostgreSQL array 文本,显式序列化后保持合法 JSON 数组文本;新增源码合同锁定 `JSON.stringify(input.p_codes)`、服务端审计标识和无客户端 reason,相关 5 个聚焦测试文件共 47/47 通过;`tsc --noEmit`、目标 ESLint 与 `git diff --check` 通过。
- 防复发:self-hosted `pg``jsonb` 参数必须显式 JSON 序列化;数据库/源码合同测试需要锁定 `JSON.stringify(input.p_codes)`,不能只依赖 Supabase RPC 测试覆盖参数编码。
- 相关记录:BUG-155、BUG-207
- 修复版本:本地未提交候选
## BUG-208 | 生时校正 opening 首步依赖模型主动加载 Skill,失败时只返回 run.started → run.failed