- 新增 topics/quality.md:业务正确性 / 运维 / 前端体验四类结论,含 「私聊取最早 50 条」「Classic() 的 Logger 记 RequestURI」这两个坑的成因, 以及处置覆盖面、Redis 两种故障模式、产品缺口(无 page view / 提问者找不回自己的提问) - 2026-10-29.md:本轮六项修复的改动与验证结果,另记两个实现坑 (limit 兜底不该抬大用户配置;Python bytes 字面量不支持 \uXXXX) - topics/sec.md / 2026-10-27.md:补上一轮遗漏的字体自动发现与 CSP enforce 结论 - MEMORY.md:索引加 quality,处置覆盖面一条标注投票链路已补齐
67 lines
4.8 KiB
Markdown
67 lines
4.8 KiB
Markdown
# 2026-10-29
|
||
|
||
## 第二轮综合复查(非安全向)
|
||
|
||
用户要求「再确认还存在什么问题与风险点」+「从用户角度有什么值得改进和增加的功能」。
|
||
前一轮是安全专项(P1/P2/P3 全修完),这轮换到**业务正确性 / 运维 / 前端体验 / 产品能力**四个面。
|
||
|
||
产出:`outputs/review-2026-10-29.md`(分级清单 + 复现方式 + 产品建议)。
|
||
|
||
### 实测复现的两条 P0
|
||
|
||
1. **私聊 50 条截断**:`GetByConversationID` 是 `Order("created_at ASC") + Limit(50)`,
|
||
唯一调用方永远传 nil cursor;会话上限 200 → 第 51 条起**写库成功但永久不可见**。
|
||
本地造 55 条实测:页面只渲染 #1–#50。这不是性能问题,是「数据在库里但用户看不到」。
|
||
2. **访问日志泄露 token**:`flamego.Classic()` = `Use(Logger(), Recovery(), Static())`,
|
||
Logger 记 `RequestURI`(含 query)。实测日志出现 `path=/_/tblocal/4?t=SECRETTOKEN123`。
|
||
`?t=` 是私密题钥匙、`?code=` 是改密凭证。**tracing 中间件早就为同一风险避开了 RequestURI
|
||
并写了注释,框架默认 Logger 漏了。**
|
||
|
||
### 代码确认的 P1
|
||
|
||
- **无优雅关闭**:全仓 `signal.Notify`/`Shutdown` 零命中,SIGTERM 直接杀进程 → 每次重启掐断在途 POST。
|
||
- **提问入库后图片失败不回滚**(`page.go:660` → `:680`),叠加 DuplicateGuard 5 分钟 → 用户以为没发出、5 分钟内不能重提。
|
||
- **Redis 两种故障**:session 在 Redis 上宕机即全站 500(flamego session load 直接 panic);
|
||
`cache.Cacher` **没有** `if conf.Redis.Addr != ""`,空 Addr 被 go-redis 回退到 localhost:6379 → 没配 Redis 时找回密码静默坏掉。
|
||
- **处置不覆盖投票**:提问链路 7 处 `ActiveLevel`,投票链路 0 处。L4 处置后 `/_/domain` 已 302 到 /restricted,
|
||
但 `/vote/{token}` 照常可投、可发新投票、探索页照常曝光。
|
||
|
||
### 产品缺口(重要,别忘)
|
||
|
||
- **完全没有访问量数据**:`SiteAnalytics` 是全站聚合,无 per-user 维度,也没有 page view 表。
|
||
箱主无法知道自己被看了多少次。
|
||
- **提问者找不回自己的提问**:不填邮箱时唯一手段是成功页那条链接,关页即失联;全仓 `localStorage` 零命中。
|
||
→ 建议前端用 localStorage 记 `{domain, id, token}`,箱主页显示「你有 N 条提问已被回答」。
|
||
- `NotifyType` 只有 `email` / `none`,无 Telegram / Webhook。
|
||
- 模板零 `application/ld+json`(SEO 富摘要缺失),无 RSS。
|
||
- 图片原图直传(无前端压缩),配合 ReadTimeout 30s → 手机端 8MB 图必超时。
|
||
|
||
### 顺带确认的「不用动」
|
||
|
||
四语言字典 917 条完全对齐(`TestDictionaryComplete` PASS)、深色模式正文对比度 4.8–6.8:1 达标、
|
||
gzip 实现质量高、后台移动端短标签方案做得细。
|
||
|
||
### 已修(同日,六项全部端到端验证)
|
||
|
||
| # | 改动 | 验证 |
|
||
|---|---|---|
|
||
| 1 | `GetByConversationID` 改 `id DESC` 取最新 + 内存反转,limit 取 `conf.Limits.ChatMaxMessages`;游标配 DESC | 55 条会话实测渲染 55 条(改前 50);单测 3 项 + 变异(改回 ASC 变红) |
|
||
| 2 | 弃 `flamego.Classic()`,`flamego.New()` + 自写 `accessLogMiddleware`(只记 `URL.Path`;静态成功不记、4xx/5xx 记) | 日志 `msg=access path=/_/tblocal/4`,token 计数 0 |
|
||
| 3 | `signal.NotifyContext` + `srv.Shutdown(15s)` + 关连接池 | 8 个并发分享图请求中 SIGTERM → 8/8 成功 |
|
||
| 4 | 图片失败分支 `Questions.DeleteByID` 回滚 | 6MB 图 → 提问增量 0 且 `deleted_at` 非空;无图对照增量 1 |
|
||
| 5 | `cache.Cacher` 加 `if conf.Redis.Addr != ""`(否则 MemoryIniter);启动 `PingRedis` fail-fast | 停 Redis 后启动立即失败并指向 Redis |
|
||
| 6 | 投票 Page/Submit 加 L4、VotePublish 加 L2、探索页用 `ActiveLevels` 批量过滤 | L4 → `/vote` 302 到 /restricted;探索页该投票 0→2(撤销后);L2 下发布停 draft,撤销后 published |
|
||
|
||
新增 `vote.err.restrictedPublish` 四语言 key。回归:4/4、19/19、6/6、3/3,全量单测 + `go build ./...` 通过。
|
||
|
||
**踩到的两个坑**(值得记):
|
||
- 消息加载的兜底写成 `limit < 50 → 50`,会把箱主显式配置的小上限抬大,测试直接测出这个语义错误。
|
||
兜底只该处理**零值**(未初始化),不该替用户改配置。
|
||
- Python 的 **bytes 字面量不支持 `\uXXXX`**(只有 str 支持),写字面量 `b'...\uff08'` 会原样写入
|
||
`\uff08` 六个字符。CRLF 文件的字节级替换要用 `\xef\xbc\x88` 或 `.encode()`。
|
||
|
||
### 待办
|
||
|
||
六项改动仍未 commit;P2/P3 清单在 `outputs/review-2026-10-29.md` 第四、五节。
|
||
建议下一批:投票结果聚合 O(N²)、三处幂等缺陷、ReadTimeout 30s 与 8MB 上传冲突、Dockerfile 构建不出来。
|