Skip to content

[修复] 资源ID冲突/场景浏览丢失/投票统计等若干数据与UI问题(已本地验证) - #3

Closed
rthdfd wants to merge 1 commit into
Intelvor:mainfrom
rthdfd:fix/data-ui-security-bugs
Closed

[修复] 资源ID冲突/场景浏览丢失/投票统计等若干数据与UI问题(已本地验证)#3
rthdfd wants to merge 1 commit into
Intelvor:mainfrom
rthdfd:fix/data-ui-security-bugs

Conversation

@rthdfd

@rthdfd rthdfd commented Aug 8, 2026

Copy link
Copy Markdown

本PR由deepseek提交

说明

您好!我们对 OpenBox 做了一次全面代码审查,发现了一些小问题并已在本分支修复。所有改动均已本地验证通过(tsc 类型检查、生产构建、250 条资源数据一致性校验),不改变任何现有功能设计——匿名投票(评价)、评论区(用户反馈)、匿名投稿、失效反馈等社区能力全部保留,仅修复缺陷本身。如果某处修复与您的预期不符,欢迎直接指出,我们随时调整。


1. [数据] 资源 ID 冲突导致详情页打开错误内容

原因src/data/seed.tsmk()cur-${subType}-${name} 生成 id,中文名会被正则替换成纯连字符,导致多个资源共用同一 id:

  • 通义千问 / 文心一言 / 智谱清言 / 讯飞星火 的 id 都是 cur-ai-apps-----
  • 即梦 AI / 可灵 AI 的 id 都是 cur-ai-apps----ai
  • curated.ts阿里云百炼 / 百度千帆 的 id 都是手写的 ob-api-

由于 getResource()find() 只返回第一个匹配,文心一言、智谱清言、讯飞星火、可灵 AI、百度千帆的详情页会打开错误的内容;同时 React key={r.id} 冲突、收藏/投票/评论按 id 关联时数据互相串。

修复

  • mk() 内维护已用 id 集合,冲突项追加稳定的内容 hash(djb2)后缀
  • seedResources 导出时统一去重兜底,重复 id 追加序号
  • 现有非冲突资源的 id 完全不变,不影响已收藏/已投票数据

验证:修复后 250 条资源 id 全部唯一,5 个受影响的资源均可正确打开详情页。

2. [数据] 场景浏览页丢失 114 条资源,首页计数与场景页严重不符

原因curated.ts 中 118 条资源显式声明 scenarios: [];而 src/lib/data.ts 的场景过滤直接用 r.scenarios.includes(sc) 判断(空数组即被排除),首页场景树却用 resolveScenarios()(空数组会回退到子类型默认映射)。两者判定不一致导致:

  • 首页场景树显示(newbie 171 / developer 210 / researcher 153 / creator 113)
  • 场景页实际只有(77 / 96 / 115 / 95)——近一半资源在"按场景浏览"下不可见

修复:场景过滤改用与首页相同的 resolveScenarios() 判定。验证后各场景页与首页计数完全一致,恢复全部资源。

3. [数据] 验证投票统计双计

原因submitVerification() 同时写 localStorage 和 Supabase 表;getVerificationStats() 先把本地票计入基数,再遍历云端行(含自己刚投的那条)又计一次 → 自己投 1 票显示 2 票。

修复:本地投票记录增加 synced 标记——云端写入成功则统计以云端为准(不再重复计本地);云端写入失败时本地票仍作兜底计入。既消除双计,又保留离线/降级能力。

4. [后端] 匿名写接口温和加固(保留匿名能力)

原因0001-0003 迁移中 verifications / comments / submissions / reports 的 insert 策略均为 with check(true),且 created_at 可由客户端任意指定,存在伪造验证时间、超长昵称撑库等滥用空间。

修复(新增 0004_harden_anon_writes.sql,幂等可重复执行):

  • 服务端 trigger 强制 created_at = now()(客户端传的时间一律覆盖)
  • 评论昵称加 30 字符上限、内容必须非空
  • 不改变匿名可投票/可留言/可投稿/可反馈的设计

5. [UI] 全站图标尺寸失效

原因Icon.tsxsize prop 解构后没有传给 lucide 图标组件,所有图标固定 24px,调用方的 size={18} 等全部无效。

修复<Cmp size={size} {...props} /> 显式传递。顺带补充了 GitBranch 图标(此前 Footer/关于页的 GitHub 链接图标静默回退为地球图标)。

6. [UI] 详情页官方徽章文案错误

原因DetailView.tsx 中官方资源徽章误用了 t('detail.favorite')("收藏"),应为"官方"(FeaturedCard 中是正确的,此处为笔误)。

修复:改用 t('common.official')

7. [UI] 资源卡标签重复渲染 / 移动端丢失

原因ResourceCard.tsx 中两组标签 chips 的响应式类写重了——桌面端前 2 个与前 3 个标签同时渲染(重复显示),移动端则全部隐藏只剩 "+N"。

修复:移动端显示前 2 个标签 + 溢出计数,桌面端显示前 3 个,两组互斥。

8. [UI] 移动端缺少排行榜入口

原因:排行榜链接只在桌面导航(sm:flex)中,移动端底部 Tab 没有该项,手机用户无法访问完整排行榜页。

修复MobileTabBar 增加排行榜 Tab(首页/搜索/排行榜/收藏/投稿/关于)。

9. [工程] 其他小问题

  • eslint 误扫构建产物eslint.config.js 的 ignores 补充 docspublic(此前 npm run lint 会输出一堆压缩产物噪音)
  • hreflang 与实现不符index.html 声明了 hreflang="en" 指向 ?lang=en,但本站为 hash 路由 SPA、语言由 localStorage 记忆,该参数不生效,已移除避免搜索引擎把同一页面当多语言收录
  • 路由解析崩溃useHashRoute 对非法百分号编码(如手输 #/search?q=%zz)会抛异常,已加 try-catch 容错

未改动(仅建议,供参考)

  • src/data/curated.ts 存在 8 组 URL 重复收录(如 硅基流动/OpenRouter/阿里云百炼 等同一站点出现两次),考虑到数据维护可能有意保留不同描述,本次未删改数据,如需要可另行处理
  • invite-mobile 分类目前 0 条资源,分类页为空,可考虑补充内容或暂时隐藏
  • 项目暂无自动化测试,建议后续补充数据一致性检查脚本(本次修复正是靠这类校验发现的)

验证情况

检查项 结果
tsc -b --noEmit ✅ 通过
npm run build(vite 8) ✅ 成功
npm run lint ✅ 无报错
250 条资源 id 唯一性 ✅ 全部唯一
场景页与首页计数一致性 ✅ 一致

感谢维护者的付出,OpenBox 的架构设计(配置驱动分类、社区验证闭环)非常清晰,希望这份 PR 能帮上忙。

- seed.ts: mk() 生成 id 冲突时追加 hash;seedResources 导出时统一去重,保证全部资源 id 唯一(文心一言/智谱清言/讯飞星火/可灵AI/百度千帆等详情页恢复可访问)
- data.ts: 场景过滤改用 resolveScenarios(与首页场景树一致),场景页恢复 114 条丢失资源;投票统计修复本地+云端双计,保留未上云兜底
- Icon.tsx: size 显式传给图标组件(此前全站图标尺寸失效);补充 GitBranch 图标
- DetailView.tsx: 官方徽章文案 "收藏" → "官方"
- ResourceCard.tsx: 标签 chips 移动端2个+N/桌面3个,修复重复渲染与移动端丢失
- MobileTabBar.tsx: 移动端底部导航补充排行榜入口
- useHashRoute.ts: 非法百分号编码不再导致路由解析崩溃
- eslint.config.js: 忽略 docs/public 构建产物
- index.html: 移除无效的 en hreflang 声明(SPA 语言由 localStorage 记忆)
- 新增 supabase/migrations/0004_harden_anon_writes.sql: 服务端强制 created_at,评论昵称长度上限(保留匿名投票/评论/投稿/反馈能力)
@rthdfd rthdfd closed this by deleting the head repository Aug 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant