Files
user-system/docs/review-fix-closure-2026-05-28.md

159 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# user-system review 修复收口2026-05-28
## 结论
本轮已完成 review 报告相关最高优先级前端/E2E blocker 修复并完成后端、前端、E2E 三层验证。
当前状态:
- 最高优先级 blocker已修复
- Go 全量测试:通过
- 前端全量测试通过82 files, 522 tests
- Playwright CDP 全链路 E2E通过
## 本轮修复项
### 1. 会话恢复 / refresh 竞态
- 问题:`AuthProvider` 初始恢复会话与 HTTP client 401 重试路径会并发触发 `/auth/refresh`,在 refresh token 轮换模型下导致 `401`
- 修复:前端改为共享 single-flight refresh。
- 涉及文件:
- `frontend/admin/src/lib/http/client.ts`
- `frontend/admin/src/services/auth.ts`
- `frontend/admin/src/services/auth.test.ts`
### 2. 用户列表响应结构漂移
- 问题:后端 `/users` 返回 `{ users, total, limit, offset }`,前端只按 `items` 读取,导致页面空表。
- 修复:增加 users 列表 normalize兼容 `items/users``page_size/limit/offset`
- 涉及文件:
- `frontend/admin/src/services/users.ts`
- `frontend/admin/src/services/users.test.ts`
### 3. Webhooks 列表响应结构漂移
- 问题Webhooks 页加载时报 `Cannot read properties of undefined (reading 'map')`
- 修复:兼容 `data/items/webhooks` 多种列表包裹形状。
- 涉及文件:
- `frontend/admin/src/services/webhooks.ts`
- `frontend/admin/src/services/webhooks.test.ts`
### 4. Social accounts 响应结构漂移
- 问题ProfileSecurityPage 报 `socialAccounts.map is not a function`
- 修复:兼容 `array/items/accounts/social_accounts` 形状。
- 涉及文件:
- `frontend/admin/src/services/social-accounts.ts`
- `frontend/admin/src/services/social-accounts.test.ts`
### 5. Playwright CDP E2E harness 漂移
- 修复点包括:
- refresh token 断言从可读 cookie 改为 HttpOnly cookie / session presence 真相
- `创建用员` 文案 typo
- responsive 场景后 viewport 未恢复
- drawer 选择器 strict mode 冲突
- delete confirm 由 modal 漂移为 popconfirm
- 菜单分组/路由漂移设备、审计日志、Webhooks、profile/security
- 多处页面断言从宽文本改为更稳定选择器
- 涉及文件:
- `frontend/admin/scripts/run-playwright-cdp-e2e.mjs`
- `frontend/admin/scripts/run-playwright-auth-e2e.sh`
### 6. E2E 限流误伤
- 问题:测试流量触发 API rate limit导致后续场景误报。
- 修复:为 E2E backend 增加 `DISABLE_RATE_LIMIT=1` 开关,仅用于测试启动脚本。
- 涉及文件:
- `internal/api/middleware/ratelimit.go`
- `frontend/admin/scripts/run-playwright-auth-e2e.sh`
### 7. 内存限流器全局误伤与条目泄漏风险
- 问题:`internal/api/middleware/ratelimit.go` 之前按 endpoint 只创建单一 limiter导致同一接口上的所有用户共享一个桶同时缺少空闲条目清理策略无法对历史 client key 做收敛。
- 修复:改为按 `endpoint + user_id/IP` 分桶,并在访问路径上按 TTL 清理长期空闲的 limiter 条目。
- 回归测试:
- 不同 IP 的登录限流相互独立
- 共享 IP 下不同 `user_id` 的 API 限流相互独立
- 空闲 limiter 会被清理,不再无限累积
- 涉及文件:
- `internal/api/middleware/ratelimit.go`
- `internal/api/middleware/ratelimit_test.go`
### 8. handler context 类型断言补强
- 问题:`SSOHandler``WebhookHandler` 仍存在 `user_id.(int64)` / `username.(string)` 直接断言,若 middleware 注入异常类型会触发 panic。
- 修复:统一复用 `getUserIDFromContext` / `getUsernameFromContext`,类型不匹配时返回 `401 unauthorized`,避免 handler panic。
- 回归测试:
- `SSOHandler.Authorize` 非法 context 类型返回 `401`
- `SSOHandler.UserInfo` 非法 context 类型返回 `401`
- `WebhookHandler.CreateWebhook/ListWebhooks` 非法 context 类型返回 `401`
- 涉及文件:
- `internal/api/handler/auth_handler.go`
- `internal/api/handler/sso_handler.go`
- `internal/api/handler/webhook_handler.go`
- `internal/api/handler/context_guard_test.go`
### 9. 密码强度 + 静默错误补强
- 问题review 报告中指出两类尾部问题:
- 默认密码校验对刚好达到最小长度的短密码过于宽松
- TOTP / 操作日志链路存在 `_ = err``_ = json.Unmarshal(...)``_ = repo.Create(...)` 这类静默吞错
- 修复:
- `validatePasswordStrength` 改为对“刚好达到最小长度”的密码要求至少 3 种字符类型;较长密码仍保留 2 种类型可过的兼容行为
- `TOTPService` 对恢复码摘要、JSON 编解码、`UpdateTOTP` 持久化失败全部显式返回错误,不再静默忽略
- `OperationLogMiddleware` 对 nil repo fail-safe 返回;异步落库失败改为写日志,不再无声吞错
- 回归测试:
- 8 位两类字符密码被拒绝8 位三类字符密码通过,较长两类字符密码仍通过
- 损坏的恢复码 JSON 会返回解析错误
- 恢复码消费后持久化失败会显式返回更新错误
- operation log 在 nil repo 情况下不会 panic参数脱敏/非 JSON fallback 继续受测
- 涉及文件:
- `internal/service/auth.go`
- `internal/service/auth_service_test.go`
- `internal/service/auth_password_internal_test.go`
- `internal/service/totp.go`
- `internal/service/totp_internal_test.go`
- `internal/api/middleware/operation_log.go`
- `internal/api/middleware/operation_log_test.go`
### 10. review 报告真相校准 + avatar 路径硬化
- 真相校准:`PROJECT_REVIEW_REPORT.md` 中一批条目已不再代表当前仓库真相,至少包括:
- `uploadAvatar` 字段名错误:前后端当前都使用 `avatar`,该条为陈旧误报
- `StateManager` 无法停止、`L1Cache` 无容量限制、密码强度过宽松、操作日志未转义、Webhooks 客户端全量分页、`ContactBindingsSection` 未复用:均已在后续提交中关闭
- 仍值得继续跟踪、但已不构成功能 blocker 的尾项:
- `social_account_repo.go` 仍是原生 SQL 实现
- `AuthProvider` 仍保留 React state + session store 双轨状态管理
- `ApiResponse.data` 空值建模仍偏乐观(`T` 而非 `T | null`
- 本轮额外修复:
- 将头像上传目录从运行时相对路径解析改为绝对路径归一化,避免 cwd 漂移导致文件落盘位置不稳定
- 扩展名校验统一转小写,避免 `.JPG/.PNG` 这类常见文件名被误拒
- 回归测试:
- `resolveAvatarUploadDir("")` 返回绝对路径且收敛到 `/uploads/avatars`
- 自定义根目录会被保留并归一化到 `<root>/avatars`
- 涉及文件:
- `internal/api/handler/avatar_handler.go`
- `internal/api/handler/avatar_handler_path_test.go`
## 验证结果
### 后端
- 命令:`go test ./...`
- 结果:通过
### 前端
- 命令:`npm test -- --runInBand`
- 结果:通过
- 统计:`82 passed`, `522 passed`
### E2E
- 命令:`npm run e2e:full`
- 结果:通过
- 结论:`Playwright CDP E2E completed successfully`
## 闭环判断
### 实现闭环
已完成。本轮识别出的真实 blocker 均已修复。
### 证据闭环
已完成。Go 全量测试、前端全量测试、CDP E2E 全部通过。
### 文档真相闭环
已完成。本文件记录了问题、修复、验证与当前结论。
### 防复发闭环
已部分完成:
- 已为 users/webhooks/social-accounts 响应结构漂移补 service-level normalize + tests
- 已把 refresh 单飞与 E2E harness 漂移修复固化
- 后续建议:把 E2E 页面导航/断言进一步抽象为页面对象或稳定 helper减少文案/菜单变动带来的连锁断言漂移