Files
no-whatever/BUGFIX.md
T

6.6 KiB
Raw Blame History

待修复问题清单

全面代码审查后整理,按严重程度排列。 修复后在对应条目前打 [x]


Critical — 必须修复

  • #1 非成员可操作任意房间

    • 文件:api/room/[id]/swipe/route.tsundo/route.tsreset/route.ts
    • swipe / undo 没有校验 userId 是否在 data.users 中,非成员的 like 会被计入匹配
    • reset 无需任何 userId,任何人知道 4 位房间号即可清空所有投票
    • 修复:在 atomicUpdateRoom 回调开头加成员校验;reset 要求 userId 并验证 creatorId
  • #2 盲盒抽取竞态条件

    • 文件:api/blindbox/draw/route.ts
    • findMany({ status: "in_pool" })update 不在事务中,两个并发请求可抽到同一个想法
    • 修复:用 prisma.$transaction + updateMany({ where: { id, status: "in_pool" } }) 保证原子性,count=0 时返回 409
  • #3 SwipeDeck 不响应他人重置

    • 文件:src/components/SwipeDeck.tsx
    • 其他用户重置房间后,当前用户的 currentIndex / localMatchId 不清零,卡片位置错乱
    • 修复:加 useEffect 检测 server 端 swipeCount 归零时调用 clearLocalState()
  • #4 API 错误响应被当数组用导致崩溃

    • 文件:src/app/panic/page.tsxsuggestions)、src/app/profile/page.tsxhistory/favorites
    • fetch 后没检查 res.ok,非 200 返回的 { error: "..." } 被当作数组,.map() 崩溃
    • 修复:所有 fetch 后先检查 res.ok,失败时设为空数组
  • #5 theme.ts SSR 崩溃

    • 文件:src/lib/theme.ts
    • setStoredTheme / applyTheme 直接访问 localStorage / document,无 typeof window 守卫
    • 修复:函数开头加 if (typeof window === "undefined") return;

High — 应该修复

  • #6 用户名唯一性 TOCTOU 竞态

    • register 去掉 findUnique 直接 create + catch P2002user PUT 同理
    • apiHandler 全局兜底 P2002 → 409
  • #7 SSE events 接口无认证

    • 校验 userId query param + 房间成员身份;start() 加 try/catch
  • #8 GET /api/user 暴露任意用户邮箱 ⚠️

    • 需要真实服务端 auth 才能彻底修复,暂保留;已加 JSON.parse 安全防护
  • #9 收藏去重用 JSON contains 匹配

    • Favorite 模型新增 restaurantId 字段 + @@unique([userId, restaurantId])
  • #10 缺少数据库索引

    • 补齐所有缺失索引并 db push
  • #11 无 onDelete 级联

    • Decision/Favorite/BlindBoxMember 加 onDelete: CascadeBlindBoxIdea.drawnBy 加 onDelete: SetNull
  • #12 密码无最大长度限制

    • validatePassword 加 128 字符上限
  • #13 AuthModal 关闭后重开不重置表单

    • useEffect 监听 open 变 true 时重置全部表单状态
  • #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) + 范围校验 (-9090, -180180)
  • #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/createapi/location/suggestapi/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.tsxblindbox/[code]/page.tsxShareCardModal.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"] 白名单