Files
no-whatever/BUGFIX.md
T

158 lines
6.6 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.
# 待修复问题清单
> 全面代码审查后整理,按严重程度排列。
> 修复后在对应条目前打 `[x]`。
---
## Critical — 必须修复
- [x] **#1 非成员可操作任意房间**
- 文件:`api/room/[id]/swipe/route.ts``undo/route.ts``reset/route.ts`
- swipe / undo 没有校验 userId 是否在 `data.users` 中,非成员的 like 会被计入匹配
- reset 无需任何 userId,任何人知道 4 位房间号即可清空所有投票
- 修复:在 `atomicUpdateRoom` 回调开头加成员校验;reset 要求 userId 并验证 creatorId
- [x] **#2 盲盒抽取竞态条件**
- 文件:`api/blindbox/draw/route.ts`
- `findMany({ status: "in_pool" })``update` 不在事务中,两个并发请求可抽到同一个想法
- 修复:用 `prisma.$transaction` + `updateMany({ where: { id, status: "in_pool" } })` 保证原子性,count=0 时返回 409
- [x] **#3 SwipeDeck 不响应他人重置**
- 文件:`src/components/SwipeDeck.tsx`
- 其他用户重置房间后,当前用户的 `currentIndex` / `localMatchId` 不清零,卡片位置错乱
- 修复:加 useEffect 检测 server 端 swipeCount 归零时调用 `clearLocalState()`
- [x] **#4 API 错误响应被当数组用导致崩溃**
- 文件:`src/app/panic/page.tsx`suggestions)、`src/app/profile/page.tsx`history/favorites
- fetch 后没检查 `res.ok`,非 200 返回的 `{ error: "..." }` 被当作数组,`.map()` 崩溃
- 修复:所有 fetch 后先检查 `res.ok`,失败时设为空数组
- [x] **#5 theme.ts SSR 崩溃**
- 文件:`src/lib/theme.ts`
- `setStoredTheme` / `applyTheme` 直接访问 `localStorage` / `document`,无 `typeof window` 守卫
- 修复:函数开头加 `if (typeof window === "undefined") return;`
---
## High — 应该修复
- [x] **#6 用户名唯一性 TOCTOU 竞态** ✅
- register 去掉 findUnique 直接 create + catch P2002user PUT 同理
- apiHandler 全局兜底 P2002 → 409
- [x] **#7 SSE events 接口无认证** ✅
- 校验 userId query param + 房间成员身份;start() 加 try/catch
- [x] **#8 GET /api/user 暴露任意用户邮箱** ⚠️
- 需要真实服务端 auth 才能彻底修复,暂保留;已加 JSON.parse 安全防护
- [x] **#9 收藏去重用 JSON contains 匹配** ✅
- Favorite 模型新增 `restaurantId` 字段 + `@@unique([userId, restaurantId])`
- [x] **#10 缺少数据库索引** ✅
- 补齐所有缺失索引并 db push
- [x] **#11 无 onDelete 级联** ✅
- Decision/Favorite/BlindBoxMember 加 `onDelete: Cascade`BlindBoxIdea.drawnBy 加 `onDelete: SetNull`
- [x] **#12 密码无最大长度限制** ✅
- `validatePassword` 加 128 字符上限
- [x] **#13 AuthModal 关闭后重开不重置表单** ✅
- useEffect 监听 open 变 true 时重置全部表单状态
- [x] **#14 邀请页加入失败无错误提示** ✅
- 新增 joinError statecatch 中捕获并渲染错误消息
---
## Medium — 建议修复
- [ ] **#15 房间 ID 空间仅 9000 个**
- 文件:`src/lib/store.ts`
- 4 位数字 (1000-9999) 碰撞概率高,fallback 也没兜住 P2002
- 修复:扩展到 6 位字母数字,或 catch P2002 后重试
- [ ] **#16 盲盒想法编辑/删除有 TOCTOU**
- 文件:`api/blindbox/route.ts` PUT / DELETE
- 状态检查和修改之间可被并发抽取,drawn 的想法可能被编辑/删除
- 修复:用 `updateMany({ where: { id, status: "in_pool" } })` 替代先查后改
- [ ] **#17 lat/lng 未校验为合法坐标**
- 文件:`api/room/create/route.ts`
- `lat=0`(赤道有效坐标)被当 falsy 拒绝;非数值字符串也能通过
- 修复:`Number.isFinite(lat)` + 范围校验 (-90~90, -180~180)
- [ ] **#18 swipe action 未校验**
- 文件:`api/room/[id]/swipe/route.ts`
-`like`/`pass` 值静默通过,只增 count 不记偏好
- 修复:校验 `action` 是否为允许的值
- [ ] **#19 JSON.parse(preferences) 可能崩溃**
- 文件:`api/user/route.ts` GET
- 残损 JSON 导致 500,用户无法加载 profile
- 修复:使用 safe parse helperfallback 为 `{}`
- [ ] **#20 ShareCardModal data 每次新引用触发 useEffect**
- 文件:`src/components/ShareCardModal.tsx`
- 内联对象导致 data 引用每次变化,图片重复加载
- 修复:改用 `imageSrc` 字符串作为 useEffect 依赖,或在调用侧 useMemo
- [ ] **#21 BlindboxRoomPage 多处 setTimeout 未清理**
- 文件:`src/app/blindbox/[code]/page.tsx`
- confetti rAF 循环、确认退出 3s 定时器、focus 定时器均未在 unmount 时清理
- 修复:用 ref 存储 timer IDuseEffect return 中 clearTimeout / cancelAnimationFrame
- [ ] **#22 外部 API (高德) 失败返回泛化 500**
- 文件:`api/room/create``api/location/suggest``api/location/regeo`
- 用户不知道是定位服务不可用
- 修复:wrap fetch 加 try/catchthrow `ApiError("位置服务暂时不可用", 503)`
- [ ] **#23 navigation.ts location.split(",") 不校验格式**
- 文件:`src/lib/navigation.ts`
- 无逗号的字符串产生 `undefined` 坐标
- 修复:校验 split 结果长度为 2 且两部分非空
- [ ] **#24 handleReset / handleNarrow 吞掉 fetch 错误**
- 文件:`src/app/room/[id]/page.tsx`
- 无 res.ok 检查,失败时用户无反馈
- 修复:检查 res.ok,失败时 throw
- [ ] **#25 confettiCanvasRef 未使用(死代码)**
- 文件:`src/app/blindbox/[code]/page.tsx`
- 渲染了 canvas 但 confetti() 使用全局实例
- 修复:删除 ref 和 `<canvas>` 元素
- [ ] **#26 requireString 接受纯空格字符串**
- 文件:`src/lib/validation.ts`
- `" "` 是 truthy 但没有实质内容
- 修复:加 `.trim()` 校验
---
## Low — 可以改进
- [ ] **#27 icon-only 按钮缺少 aria-label**
- 文件:`panic/page.tsx``blindbox/[code]/page.tsx``ShareCardModal.tsx` 等多处
- 屏幕阅读器只报 "按钮" 无描述
- [ ] **#28 AudioContext 每次 playChime() 新建**
- 文件:`src/lib/celebrate.ts`
- 浏览器限制并发 AudioContext 数量 (~6),快速重复触发可能静默失败
- 修复:缓存复用单个 AudioContext
- [ ] **#29 ApiError.name 是 "Error" 而非 "ApiError"**
- 文件:`src/lib/api.ts`
- 堆栈和日志中显示 `Error:` 而非 `ApiError:`,调试不便
- 修复:构造函数加 `this.name = "ApiError"`
- [ ] **#30 blindbox lobby 加载房间失败静默无提示**
- 文件:`src/app/blindbox/page.tsx`
- catch 空处理,用户看到空列表以为没房间
- [ ] **#31 theme localStorage 读取不校验合法值**
- 文件:`src/lib/theme.ts`
- 任意字符串被当作 Theme 使用
- 修复:对比 `["light", "dark", "system"]` 白名单