Files
no-whatever/BUGFIX.md
T

126 lines
4.9 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 — 建议修复
- [x] **#15 房间 ID 空间仅 9000 个** ✅
- 扩展为 6 位字母数字 (30^6 ≈ 7.3 亿)createRoom 用 P2002 重试
- [x] **#16 盲盒想法编辑/删除有 TOCTOU** ✅
- PUT/DELETE 改用 updateMany/deleteMany 原子操作
- [x] **#17 lat/lng 未校验为合法坐标** ✅
- Number.isFinite + 范围校验 (-90~90, -180~180)
- [x] **#18 swipe action 未校验** ✅
- 校验 action 必须为 'like' 或 'pass'
- [x] **#19 JSON.parse(preferences) 可能崩溃** ✅
- GET 和 PUT 响应均加 try/catchfallback {}
- [x] **#20 ShareCardModal data 每次新引用触发 useEffect** ✅
- 依赖改为 imageSrc 字符串而非整个 data 对象
- [x] **#21 BlindboxRoomPage 多处 setTimeout 未清理** ✅
- timersRef 统一收集所有 setTimeoutunmount 时批量清理;confetti rAF 用 alive ref 控制
- [x] **#22 外部 API (高德) 失败返回泛化 500** ✅
- 三个高德 API 路由 fetch 加 try/catch → 503
- [x] **#23 navigation.ts location.split(",") 不校验格式** ✅
- 校验 split 结果长度为 2 且两部分非空
- [x] **#24 handleReset / handleNarrow 吞掉 fetch 错误** ✅
- 检查 res.ok,失败时 toast 提示
- [x] **#25 confettiCanvasRef 未使用(死代码)** ✅
- 删除 ref 和 canvas 元素
- [x] **#26 requireString 接受纯空格字符串** ✅
- 加 .trim() 校验
---
## Low — 可以改进
- [x] **#27 icon-only 按钮缺少 aria-label** ✅
- panic/blindbox/ShareCardModal/AuthModal 中所有 icon-only 按钮补 aria-label
- [x] **#28 AudioContext 每次 playChime() 新建** ✅
- 缓存复用单个 AudioContextstate === "closed" 时才重建
- [x] **#29 ApiError.name 是 "Error" 而非 "ApiError"** ✅
- 已在 High 批次修复
- [x] **#30 blindbox lobby 加载房间失败静默无提示** ✅
- 新增 loadError state,失败时显示"加载失败 / 点击重试"
- [x] **#31 theme localStorage 读取不校验合法值** ✅
- 已在 Critical 批次修复(VALID_THEMES 白名单校验)