- 2026-10-29.md:第三批 7 项改动与验证数据 - MEMORY.md:三条长期结论 —— 「用户主动删除 = 真删」(模型全带 DeletedAt,默认 Delete 是软删)、 「配额必须与内容同事务」(incrementQuotaTx 是唯一实现,且投票口径是票数不是个数)、 「错误页不套模板」(f.NotFound 不经过组级中间件,注入 template.Data 会 panic) - topics/quality.md:标注第三批范围 新踩到的三个坑: 1. gofmt 会把注释列表 `// 1) ...` 改写成 `// 1. ...`,被标为未格式化时先排除这个 2. Select(...) 漏列 = 字段静默为空(摘要永远是空串,只有一项测试红) 3. CDP 验证纯前端:chrome 必须作为独立后台进程启动,跟在别的命令后面 `&` 会随 shell 退出被回收;headless=new 的空标签页 type 常是 other
73 lines
5.5 KiB
Markdown
73 lines
5.5 KiB
Markdown
# 业务正确性 / 运维 / 前端体验(2026-10-29 第二轮复查)
|
||
|
||
安全向的结论在 `sec.md`。这里是非安全向的四类,完整清单见 `outputs/review-2026-10-29.md`。
|
||
|
||
> **状态**:下列「已实测确认的缺陷」「处置覆盖面」「Redis」三块已于同日修复并端到端验证。
|
||
> 紧接着做了第二批(P2 + 高价值 P3,共 9 项):投票结果 stale-while-revalidate、
|
||
> 三处幂等、私聊首条事务、`/healthz` `/readyz`、验证码失败保正文、`tbBeginSubmit`、
|
||
> waline 按页加载 / lazy / safe-area / i18n、5 处裸断言。
|
||
> 第三批(同日):删除投票/提问改事务 + 真删、配额与内容同事务、追问改回答不重复发信、
|
||
> token 常数时间比较 + CSV 注入补齐、写路径 fail-closed、真 404/HTML 500、
|
||
> 提问者找回自己的提问(localStorage + `/api/v1/ask-status`)。
|
||
> 条目保留是为了记住**为什么**会踩、以及验证该怎么做。
|
||
|
||
## 已修:两个 P0(都是实测出来的)
|
||
|
||
- **私聊 50 条截断(P0,已修)**:`internal/db/message.go` 的 `GetByConversationID` 是
|
||
`Order("created_at ASC") + Limit(50)`,而唯一调用方 `chat_handler.go:97` 永远传 `nil` cursor。
|
||
会话上限是 200 → 第 51 条起写库成功、`message_count` 在涨、页面显示 51/200,但列表永远只有最早 50 条。
|
||
实测造 55 条,页面只渲染 #1–#50。**取最新 N 条要 `Order("id DESC")` 再内存反转**;
|
||
游标 `id <` 也必须配 DESC(配升序会反复返回同一页)。
|
||
- **访问日志泄露 token(P0,已修)**:`flamego.Classic()` = `Use(Logger(), Recovery(), Static())`,
|
||
`Logger()` 记的是 `ctx.Request().RequestURI`(含 query)。实测出现
|
||
`path=/_/tblocal/4?t=SECRETTOKEN123`。`?t=` 是私密题钥匙,`?code=` 是改密凭证。
|
||
**tracing 中间件早就为同一风险避开 RequestURI 并写了注释,框架默认 Logger 漏了。**
|
||
改用 `flamego.New()` + 自写访问日志,只记 `URL.Path` 与白名单 query key。
|
||
|
||
## 处置(restrict)的覆盖面(投票已补齐)
|
||
|
||
- 提问链路 7 处 `ActiveLevel`(page L4 / question L3 / setPublic L2 / followUpAnswer L2 / shareCard 全级 / ListAPI L4 / moderation 锁定)。
|
||
- **投票链路 0 处**:`vote.Page`、`vote.Submit`、`VotePublish` 都不查。L4 处置后 `/_/domain` 已 302 到
|
||
`/restricted`,但 `/vote/{token}` 照常可投、可发新投票、探索页照常曝光。
|
||
- **教训:新增任何「用户发起」的写入口,都要问一句「这里要不要查 ActiveLevel」。**
|
||
|
||
## 运维面
|
||
|
||
- **无优雅关闭**(已修):全仓 `signal.Notify` / `server.Shutdown` 零命中,SIGTERM 直接杀进程。
|
||
`web.go` 里 `ListenAndServe` 一把梭。每次部署都掐断在途 POST。
|
||
- **Redis 两种故障**(已修):session 走 Redis 时 load 失败会 `panic`(flamego/session),被 Recovery 兜成 **全站 500**;
|
||
`cache.Cacher` 没写 `if conf.Redis.Addr != ""`,空 Addr 被 go-redis 回退 `localhost:6379` →
|
||
「不配 Redis」的部署里找回密码静默坏掉、私信邮件节流失效。启动时该 `PING` 一次并 fail fast。
|
||
- `ReadTimeout: 30s` 与 8MB 单图上限冲突(手机 4G 上行 1–2 Mbps → 8MB 必超时),正文也一起丢。
|
||
- 无 `/healthz`:`route.Home` 不碰 DB,DB 挂了 `/` 仍 200。
|
||
- `Dockerfile` 用 `golang:1.20` 而 `go.mod` 是 1.27.3 → **构建必失败**;`-X` 路径还是旧仓库名
|
||
`github.com/NekoWheel/NekoBox`,`BuildCommit` 永远是 `dev`。
|
||
- AutoMigrate 无版本化:GORM 只加列不删列,**改列名会静默留下旧列 + 新列全 NULL**,比迁移失败更难发现。
|
||
- 备份只有 `docs/cli.md` 一句 `pg_dump`;用户图片全在对象存储,**只恢复 DB = 全站裂图**。
|
||
|
||
## 前端
|
||
|
||
- `templates/question/history-template.html` 有硬编码中文(查看回答 / 加载更多 / 无更多留言),en/ja 用户直接看到。
|
||
`vote/page.html:140` 中英混排。**字典本身 917 条 × 4 语言完全对齐(有测试守着),问题只在模板层。**
|
||
- 提问页验证码失败走 `SetErrorFlash + Redirect` → **整段提问内容丢弃**;其他分支都是
|
||
`SetError + Success` 保留内容。最常见的失败反而最不该丢内容。
|
||
- `form.submit()` 不触发 submit 事件 → 验证码流(默认 captcha_type)完全没有防重复点击。
|
||
- `waline.js` 全站加载但只有 `home.html` / `item.html` 用;UIkit CSS 走第三方 CDN 且阻塞首屏。
|
||
- 32 个 `<label>` 无 `for=`;语义标签白字对比度约 1.9:1;全仓零 `loading="lazy"`。
|
||
- 移动端 `body{padding-bottom:56px}` 与 `env(safe-area-inset-bottom)` 冲突,iPhone 上遮住约 32px。
|
||
|
||
## 产品缺口(按 ROI)
|
||
|
||
1. **提问者找不回自己的提问**:不填邮箱时唯一手段是成功页那条链接,关页即失联。
|
||
全仓 `localStorage` 零命中。最小解:前端记 `{domain, id, token}`,箱主页显示「你有 N 条提问已被回答」。
|
||
2. **箱主没有自己的数据**:`SiteAnalytics` 是全站聚合(只有 `COUNT(DISTINCT user_id)`),
|
||
**全站没有 page view 表**。箱主不知道自己被看了多少次、回答率多少。
|
||
3. `NotifyType` 只有 `email` / `none`,无 Telegram / Webhook。
|
||
4. 模板零 `application/ld+json`(SEO 富摘要),无 RSS。
|
||
5. 图片原图直传,无前端压缩。
|
||
|
||
## 这一轮确认「不用动」的
|
||
|
||
四语言字典对齐、深色模式正文对比度 4.8–6.8:1、gzip 实现(204/304/Content-Length/Vary/Flusher 都处理对了)、
|
||
后台移动端短标签方案。这几块质量高于平均,别为了统一风格去改。
|