Files

4.9 KiB
Raw Permalink 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 个

    • 扩展为 6 位字母数字 (30^6 ≈ 7.3 亿)createRoom 用 P2002 重试
  • #16 盲盒想法编辑/删除有 TOCTOU

    • PUT/DELETE 改用 updateMany/deleteMany 原子操作
  • #17 lat/lng 未校验为合法坐标

    • Number.isFinite + 范围校验 (-9090, -180180)
  • #18 swipe action 未校验

    • 校验 action 必须为 'like' 或 'pass'
  • #19 JSON.parse(preferences) 可能崩溃

    • GET 和 PUT 响应均加 try/catchfallback {}
  • #20 ShareCardModal data 每次新引用触发 useEffect

    • 依赖改为 imageSrc 字符串而非整个 data 对象
  • #21 BlindboxRoomPage 多处 setTimeout 未清理

    • timersRef 统一收集所有 setTimeoutunmount 时批量清理;confetti rAF 用 alive ref 控制
  • #22 外部 API (高德) 失败返回泛化 500

    • 三个高德 API 路由 fetch 加 try/catch → 503
  • #23 navigation.ts location.split(",") 不校验格式

    • 校验 split 结果长度为 2 且两部分非空
  • #24 handleReset / handleNarrow 吞掉 fetch 错误

    • 检查 res.ok,失败时 toast 提示
  • #25 confettiCanvasRef 未使用(死代码)

    • 删除 ref 和 canvas 元素
  • #26 requireString 接受纯空格字符串

    • 加 .trim() 校验

Low — 可以改进

  • #27 icon-only 按钮缺少 aria-label

    • panic/blindbox/ShareCardModal/AuthModal 中所有 icon-only 按钮补 aria-label
  • #28 AudioContext 每次 playChime() 新建

    • 缓存复用单个 AudioContextstate === "closed" 时才重建
  • #29 ApiError.name 是 "Error" 而非 "ApiError"

    • 已在 High 批次修复
  • #30 blindbox lobby 加载房间失败静默无提示

    • 新增 loadError state,失败时显示"加载失败 / 点击重试"
  • #31 theme localStorage 读取不校验合法值

    • 已在 Critical 批次修复(VALID_THEMES 白名单校验)