Skip to content

feat(failure-rules): 错误类型枚举强校验 + 多选 UI#187

Merged
g1331 merged 1 commit into
masterfrom
feat/failure-rule-error-type-enum
May 24, 2026
Merged

feat(failure-rules): 错误类型枚举强校验 + 多选 UI#187
g1331 merged 1 commit into
masterfrom
feat/failure-rule-error-type-enum

Conversation

@g1331
Copy link
Copy Markdown
Owner

@g1331 g1331 commented May 24, 2026

背景

上一个 PR(#186)补齐了 UpstreamNoContentStreamError 这条新错误类型后,复盘失败规则编辑界面,发现一个长期存在的可用性缺口:

  • 前端 error_types 字段是纯 CSV 文本输入,parseCsvList(...) as FailoverErrorType[] 只是 TS 强转,运行时不做白名单校验。
  • 三处 admin route 的 Zod schema 都是 z.array(z.string().trim().min(1)),任何非空字符串都被接受。
  • 服务层 ruleMatchesEvidenceincludes 做严格匹配,evidence.errorType 始终是合法枚举值,所以拼错的规则永远不会命中,但不报错、不日志、不前端反馈

结果:用户随手写错一个 token,规则创建成功、列表显示"已启用"、但在生产里永远是僵尸规则。属于典型的 silent footgun。

改动

后端

  • 新增 src/lib/constants/failover-error-types.ts,作为 FailoverErrorType 的运行时单一来源:导出常量数组、failoverErrorTypeSchema(zod enum)、isKnownFailoverErrorType 类型守卫。
  • 三处 admin route schema 改用 z.array(failoverErrorTypeSchema),拼错直接返回 400 并附允许值列表。
  • assertValidRuleMatch 增加 enum 校验作为第二防线(防御绕过 admin API 直接调 service 的路径)。
  • ruleMatchesEvidence 命中含未知值的历史规则时,以 per-rule-id 去重的方式 log.warn 一次,让运维通过日志感知到失效规则。

前端

  • 新增 FailoverErrorTypeMultiSelect 组件,基于 shadcn 的 Popover + Checkbox + Badge 拼装(项目当前没有 multi-select,shadcn 官方也不提供)。支持全选/清空,未知遗留值用红色 Badge + tooltip 显式提示删除。
  • 列表展示同样区分已知 label(走 requestLogs.retryErrorType.* i18n)与未知字符串,未知值用红色 Badge。
  • upstream-failure-rules-editor.tsx 把 CSV <Input> 替换为新组件,errorTypes state 从 string 改为 string[]

i18n

英中两份 messages 同步新增 failureRuleErrorTypesSelectAll / ClearAll / RemoveAria / failureRuleErrorTypeUnknownTooltip,placeholder 文案改为选择式。

测试

  • 服务层新增两条:拒绝未知 errorType 创建/更新;含未知值的历史规则仍可匹配合法 evidence(向后兼容)。
  • editor 测试将新 multi-select 组件 mock 为按钮列表,断言数据契约;同时把全局规则空 error_types 用例的期望从 [] 改为 null(语义更清晰)。

兼容性

  • 数据库里已存在的合法值不受影响。
  • 含历史未知值的规则在编辑界面会以红色 Badge + tooltip 提示,并在 evidence 匹配时打出去重的 warn 日志,但不会自动删除,由用户主动清理。
  • 编辑保存时未知值会被前端 filter(isKnownFailoverErrorType) 剔除,符合后端 enum 校验。

验证

  • pnpm lint 通过
  • pnpm exec tsc --noEmit 通过
  • pnpm test:run:147 文件 / 2489 通过 / 1 skipped

Test Plan

  • 在 Admin UI 打开"上游失败规则",确认 error_types 输入由文本变成多选下拉,12 个枚举值都出现且带 i18n 标签。
  • 通过 UI 创建一条规则后,用 curl 直接对 admin API POST {"error_types": ["xxxxx"]},确认返回 400。
  • 手工往 DB 写入一条含 "http_500" 这种历史未知值的规则,编辑界面应显示红色 Badge + tooltip。
  • 触发一次能命中该规则的请求,确认日志输出 upstream failure rule contains unknown error types ... 警告一次(同 rule 反复触发只 warn 一次)。

把上游失败规则的 error_types 字段从自由 CSV 升级为封闭枚举:

- 新增 src/lib/constants/failover-error-types.ts 作为 FailoverErrorType 的运行时单一来源(数组 + zod enum + 类型守卫),让前后端共享。
- 三处 admin route schema(全局 POST、单条 PUT、scoped POST)改用 z.array(failoverErrorTypeSchema),拼错会直接 400。
- 服务层 assertValidRuleMatch 增加 enum 校验作为第二防线;ruleMatchesEvidence 命中含未知值的历史规则时以 per-rule 去重的方式 log.warn 一次,让运维通过日志感知到沉默失效。
- 前端 Editor 把 CSV Input 替换为 FailoverErrorTypeMultiSelect(基于 shadcn Popover + Checkbox + Badge 拼装),全选/清空、未知遗留值用红色 Badge + tooltip 提示删除;列表展示同样区分已知 label 与未知字符串。
- i18n 同步:英中文新增 SelectAll / ClearAll / RemoveAria / UnknownTooltip,Placeholder 改为选择式文案。
- 测试:服务层新增"拒绝未知 errorType / 历史含未知值的规则仍可匹配合法 evidence"两条;editor 测试将 MultiSelect mock 为按钮列表,断言数据契约。
@codecov
Copy link
Copy Markdown

codecov Bot commented May 24, 2026

Codecov Report

❌ Patch coverage is 56.94444% with 31 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.05%. Comparing base (c203793) to head (edab153).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #187      +/-   ##
==========================================
- Coverage   74.14%   74.05%   -0.09%     
==========================================
  Files         145      147       +2     
  Lines       11048    11114      +66     
  Branches     3832     3846      +14     
==========================================
+ Hits         8192     8231      +39     
- Misses       1657     1682      +25     
- Partials     1199     1201       +2     
Flag Coverage Δ
verify 74.05% <56.94%> (-0.09%) ⬇️
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@g1331 g1331 merged commit f143db7 into master May 24, 2026
12 checks passed
@g1331 g1331 deleted the feat/failure-rule-error-type-enum branch May 24, 2026 13:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant