Files
tamabox/.workbuddy/memory/2026-09-05.md
T
tamakyi e3e59e7c69 chore: baseline snapshot before moderation-strictness feature
Repo .git was damaged (refs/ missing, index truncated, object store near-empty).
Repaired via git init (rebuilt refs), stale index preserved as .git/index.corrupt-20260910.bak,
then full worktree committed as a rollback baseline.
2026-09-10 10:56:27 +08:00

168 lines
43 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 2026-09-05 工作日志
## 提问箱公开主页「加载更多」翻页失效(内容重复)排查
- 用户报告:提问列表加载完后「加载更多」按钮仍在,点击后加载相同内容。示例链接 `https://box.tama.guru/_/aoi/3105?t=Kmd0mu`(item 页底部嵌有该箱「历史提问」列表 + history-template 加载更多)。
- 代码链路:`templates/question/history-template.html`(Alpine fetch) → `route/question/page.go ListAPI`(`/api/v1/user/{domain}/questions`) → `internal/db/questions.go GetByUserID`(PinFirst 复合键集游标 `isPinned.CreatedAtUnixNano.ID`)。
- 线上实测(box.tama.guru, aoi 箱共 38 条已答公开问题):
- 首页 20 条 id 3109→3089;`next_cursor=0.1788566476678885000.3089`。
- 携带任意真实 next_cursor 再次请求 → 仍返回首页同样 20 条(has_more 恒 true)→ 内容重复、无法翻页(已稳定复现)。
- 部分异常游标返回空集/6 条等与源码 keyset 预期不符的怪异结果 → 指向线上二进制或 DB 环境与工作区源码假设不一致(时区/列精度/部署版本),单凭源码无法闭合解释。
- 前端模板与工作区 0904 版逐字符一致(x-on:click.debounce、payload.next_cursor、注释),说明线上模板=新版;但服务端行为异常。
- 历史:outputs/re-audit-2026-09-01.md P0-2 曾记录同款症状(前端回传旧格式游标→parsePinCursor 失败→永远第一页),当时修复为服务端下发 next_cursor;但线上当前仍复现,说明修复未生效或有残余缺陷。
- 后续建议:生产库执行 keyset SQL 直查对照 + SHOW CREATE TABLE 确认列精度;代码侧做健壮化(游标未前进/解析失败 → 返回空 + has_more=false;前端按已见 id 集合防重)。
## 落地修复(用户选定:防重复兜底)
1. `internal/db/questions.go`:
- `GetByUserID`:携带游标但 PinFirst 复合游标/纯 id 游标解析失败时,由「忽略游标静默回退首页」改为直接 `return []*Question{}, nil`(空列表 → 上层 has_more=false → 按钮收敛)。
- `getBy`(GetByAskUserID 旧路径):同步改为解析失败返回空。
2. `templates/question/history-template.html`:
- SSR range 项与 JS append 项均加 `data-qid`;点击时先扫描 `#question-list [data-qid]` 收集已见 id,过滤重复项;无新增条目时 `more=false`(按钮隐藏/显示无更多)。
- 注释说明重复加载 bug 的历史形态。
3. 验证:`go build ./internal/db/...`、`go vet ./internal/db/` 通过;模板测试 `TestTemplatesParse/TestTemplateCrossReferences/TestRenderPublicQuestionList/TestRenderPublicQuestionItem` 全 PASS。全量 `go build ./...` 因第三方 `chai2010/webp`(CGO,本地缺 C 库)预存失败,与本次改动无关。
4. 待办(未做):生产侧核对部署二进制是否含 9/1 P0-2 修复、DB 列精度/时区一致性诊断;线上复现的 keyset 怪异行为(同 nano 不同 id 返回不同页等)仍未完全由源码闭合解释。
- 为用户产出 PG 根因诊断脚本 outputs/pg-cursor-pagination-diagnose.sql:PART A 环境/表结构/集合核对(含 created_at 列精度、索引、answered_public_all 应=38 与线上 API 对齐);PART B 用 DO+临时表纯 SQL 复刻 ListAPI 复合键集翻页循环,逐页打印 ids、带死循环/重复页检测;PART C UnixNano 指纹核对(1788566476678885000 类)用于区分部署版本旧 vs DB 环境问题。ListAPI 等价条件已核对: FilterAnswered(True)+PrivateStatus=1+PinFirst+page_size=20+1 探测。待用户执行回贴结果判读。
- 生产库真实结构确认(用户探查): schema=nekobox(search_path 默认 public 导致首版脚本找不到表);nekobox.questions: id numeric(seq nekobox.questions_id_seq), created_at/updated_at/deleted_at = timestamp(6) WITHOUT TIME ZONE, user_id bigint, answer text, is_private/is_pinned/is_read boolean NOT NULL;nekobox.users 含 domain。线上库与源码(GORM timestamptz 假设)不一致 -> 无时区列+Go UTC 解释=线上 keyset 错位头号嫌疑。产出 v2 脚本 outputs/pg-cursor-diagnose-v2-nekobox.sql:SET search_path=nekobox; PART A 结构/集合核对(应38)+DB墙钟vs线上Z文本目视(差8h即偏移实锤); PART B DO+临时表纯墙钟 keyset 复现(锚点 timestamp 变量避免时区换算, 重复页/死循环检测); PART C nano 指纹反推命中检测(match_3089)+Z文本墙钟命中。待用户执行回贴。
- 【根因闭合】v2 脚本线上结果: aoi_user_id=329, answered_public_all=38(与线上API一致), 无置顶行(pinned=0)。DB 墙钟==线上 JSON Z 文本(无时区偏移,会话 Asia/Shanghai 不影响无时区列直比)。PART B 纯墙钟 keyset 翻页: 第1页20条(3109..3089)=线上首页, 第2页18条(3088,2883..2411), 无重复无跳行 -> **DB 层完全自洽**。PART C: nano=1788566476678885000 反推墙钟 00:01:16.678885 精确命中 3089 (match_3089=t), Z 文本命中 3109/3089 -> 服务端 encode 正确、无时区/精度错位。结论: DB/SQL 层彻底排除,线上"游标被无视返回首页"只能用【线上部署二进制与 0904 源码不一致(解析路径差异)】解释。修复=部署含防重兜底的最新源码; 部署后需 curl 复测翻页收敛。
- 【真根因确认+已修复】部署 0904 后线上仍复现 -> 读 parsePinCursor 源码定位: `time.Unix(0, nano)` 返回 Local 时区,进程 TZ=Asia/Shanghai 时墙钟 +8h(00:01->08:01)。生产 questions.created_at 是 timestamp WITHOUT time zone,驱动按 time.Location 墙钟直发无时区列 -> `created_at < '08:01'` 对库内全部 00:xx 行恒真 -> keyset 退化为全量 -> 永远返回首页 has_more=true -> 无限循环。本地实验 tz_proof.go 证明 08:01 vs 00:01 偏移。已修复 internal/db/questions.go parsePinCursor: time.Unix(0,nano) -> time.Unix(0,nano).UTC()(含时区约定注释)。build/vet PASS。其余 UnixNano 用途(随机/文件名)无 keyset 风险。待用户重新部署后 curl 验收翻页收敛(38条唯一)。
- 【修复验收通过】用户重新部署后线上验证:第1页20条(新增20) has_more=true -> 第2页18条(新增18) has_more=false -> 总计38条唯一,完全收敛 ✓。非法游标(abc)/旧纯id游标(3089)均返回 {"data":[],"has_more":false} -> 解析失败收敛兜底同时生效。根因(parsePinCursor 时区偏移)与今日两处防重兜底均已上线。问题闭环。
- 用户拟将生产库克隆到本地实验环境做 timestamptz 迁移测试。本地已建独立 PG 测试实例: 127.0.0.1:5433, user=postgres, trust 认证, data 目录 $HOME/AppData/Local/Temp/tamabox-pg-clone-test/data (PostgreSQL 18 at C:/OtherFiles/PostgreSQL/18)。等待用户 pg_dump 生产库(Fc, --schema=nekobox) 后导入跑迁移验证(epoch 快照/转换/验收/翻页复现)。注意本地另有用户自己的 5432 实例(scram 认证, 无密码勿扰)。
- 【timestamptz 迁移已在克隆库验证通过】用户 dump(nekobox_clone_20260905.dump, 资料库 drive 节点 6oAmSRkxq7DVFAnqRt8QEv) 下载后导入本地 5433 测试实例 nekobox_clone。结果: 16 表, aoi=38 与线上一致。实际无时区时间列仅 4 表 12 列: questions/users/censor_logs/upload_images(其余表本为 timestamptz)。Asia/Shanghai 会话下迁移 ALTER TYPE timestamptz USING AT TIME ZONE 'UTC': 1.7s 完成, epoch 前后差异 0 行, 残余 without=0; 关键行指纹 3109=1788569378648502000/3089=1788566476678885000/2411=1781735419615754000 与线上一致; keyset 翻页带 +00 参数精确翻出线上第2页同款 18 条; 索引完整; 回滚路径双向验证无损。产出 outputs/timestamptz-migration-online.sql(PART0 核对/1 快照/2 迁移/3 验收4项/4 注释回滚)。克隆测试实例与 dump 暂存本地 Temp, 待用户线上执行确认后清理。
- 【线上 timestamptz 迁移执行成功】用户线上跑 timestamptz-migration-online.sql: PART0=4表12列, PART1快照12列, PART2迁移DO完成, PART3验收全绿: 3.1 epoch差异0行, 3.2 remain_without=0, 3.3 指纹 2411=1781735419615754000/3089=1788566476678885000/3109=1788569378648502000 与克隆库/线上游标一致, 3.4 keyset第2页18条(3088,2883..2411)同款。但端到端验收受阻: box.tama.guru 持续 HTTP 521(源站不可达, 页面+API均打不开, 观察约2.5min) -> 疑似用户正在重启/部署应用或服务宕机, 待服务恢复后重跑翻页验收脚本(38条唯一收敛)确认整体闭环; 本地5433克隆实例与dump暂留待最终验收后清理。
- 【端到端验收通过·最终闭环】服务启动后重跑线上翻页: 第1页20条 has_more=true -> 第2页18条 has_more=false -> 38条唯一收敛 ✓。注意: 迁移后 JSON created_at 文本从 "...Z"(UTC) 变为 "...+08:00"(session TZ=Asia/Shanghai) —— 绝对时刻一致。前端消费核对: history-template SSR {{Date .CreatedAt}} 迁移前显示 UTC 墙钟(少8h) 而 JS dayjs() 追加项本地化 -> 首屏与追加时间曾不一致(隐藏显示bug), 迁移后 SSR 读 timestamptz 转会话时区 -> 两者一致且显示上海本地时间(对用户更正确); item.html/list.html 同理顺带修正。无需改代码, 属显示语义修正观察项。遗留: 用户清理 diag_test 报依赖错误 -> 解答用 DROP SCHEMA diag_test CASCADE (schema 内仅 snap_before/snap_after 两张自建快照表)。本地 5433 克隆实例+dump 待清理。
- 【发现迁移基准错误+已产修复脚本】用户反馈: 真实本地时间 00:01 的提问页面显示 08:01 -> 存量墙钟实为"上海本地时间"(驱动按进程本地墙钟写无时区列, 读出按UTC标记, 文本一致造成 v2 诊断"对称"假象), 先前迁移用 AT TIME ZONE 'UTC' 把上海墙钟误标 UTC -> 存量绝对时刻整体 +8h。克隆库验证反向修正(USING c AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Shanghai', 净-8h): 3089 08:01->00:01:16+08 (epoch 1788566476678885000->1788537676678885000), 3109->00:49:38+08, 2411->06-17 22:30+08; keyset 第2页仍18条。产出 outputs/timestamptz-migration-fix-8h.sql (PART0 聚焦4表12列/1快照/2修正/3验收含3.1显示+epoch/3.2翻页/3.3计数/3.4残余/4注释回滚=反向AT TIME ZONE 'Asia/Shanghai' AT TIME ZONE 'UTC')。克隆库全流程演练通过。待用户线上执行贴回 + 我重跑 API 验收(next_cursor nano 应从 0.1788566476678885000 变 0.1788537676678885000)。
- 【线上反向修正验收通过】用户线上执行 timestamptz-migration-fix-8h.sql(PART2 修正完成, 后续输出被 psql 分页截断不影响), 我直接 API 验证: next_cursor nano 1788566476678885000->1788537676678885000 ✓, created_at 文本回到本地墙钟 00:49:38.648502+08:00 / 末条 00:01:16.678885+08:00 ✓, 翻页 38 唯一收敛 ✓。存量时间语义修正为"上海墙钟"。注意: 该脚本不可重跑(会再-8h)。遗留: 用户如需补看验收可跑只读 SQL; diag_test 清理可用 DROP SCHEMA diag_test CASCADE; 本地 5433 克隆实例+dump 待清理(询问用户后执行)。
- 【留档完成】事故复盘文档 outputs/TamaBox-v4-keyset-pagination-timezone-incident-2026-09-05.md:完整记录两层缺陷(①parsePinCursor time.Unix Local +8h 致 keyset 失效②timestamptz 迁移基准误用 UTC 致存量+8h 已反向修正)、时间线、4文件代码改动、SQL 脚本产物清单(标注 v1/v2 已淘汰、online.sql 基准错误勿复用、fix-8h.sql 不可重跑)、经验教训预防清单(无时区列+带时区代码混用是土壤/迁移前须用现实锚点确认数据语义/文本一致≠语义正确)、遗留建议(页面抽查/可选 UTC 输出/清理克隆实例与dump/生产 DROP SCHEMA diag_test CASCADE)。
## UIkit CSS 迁移公共 CDN(速度优化落地)
- 背景:生产速度测试发现 R2 自托管 `uikit/3.3.3/css/modify-black-font/uikit.min.css`(263432B)加载 ~1100ms 是大头。
- Diff 结论:自建版与官方 uikit@3.3.3(263444B,sha256 6630e078...)**仅 3 处颜色差异**——把有色容器(.uk-light/.uk-card-primary/.uk-card-secondary/.uk-overlay-primary/.uk-section-*/.uk-tile-*/.uk-offcanvas-bar)内默认白字/白标题改黑:容器文字与 table th `rgba(255,255,255,.7)→rgba(0,0,0,.7)`,h1-h6/.uk-h*/.uk-heading-* `#fff→#000`。
- 模板实际仅用 `.uk-light`(用户背景封面,question/list.html、question-not-found.html;内部 h3/p 由 #usernamecolor/#introcolor 内联 JS 覆盖,优先级最高),其余有色容器全仓无 class 使用 → 补丁可只投影 .uk-light(477B 代替 9KB)。
- 测速(本地国内网络,全量 263KB 3 次):cdnjs.snrat.com ~0.25s(热)最快 > jsdelivr ~0.47s > unpkg ~1.3s > cdnjs.cloudflare ~1.0-1.4s > R2 自建 1.4-2.0s。check-host.net 全球 12 节点:snrat 与 jsdelivr 均 12/12 = 200 OK(snrat 根路径 301 不影响带资源路径请求)。snrat 缓存头 max-age=31104000(360 天)。
- 落地修改(仅 templates/base/header.html):
1. css link → `https://cdnjs.snrat.com/ajax/libs/uikit/3.3.3/css/uikit.min.css`
2. js(uikit.min.js/uikit-icons.min.js)→ `https://cdnjs.snrat.com/ajax/libs/uikit/3.3.3/js/...`(CDNJS 路径与 cdnjs 官方同构,已验证文件存在 Content-Type 正确)
3. link 后新增补丁 `<style>`(.uk-light 黑字 3 条规则 + 扩展警告注释)
4. 头部 preconnect 增加 `https://cdnjs.snrat.com`(注释同步更新,原"全部归拢 R2"说法过时)
- 验证:go test ./templates(TestTemplatesParse/TestTemplateCrossReferences/TestRenderPublicQuestionList 等)全 PASS;CSS 括号配对/规则数 3 条自检通过。
- 未做(用户排除):Rocket Loader(cloudflare-static/rocket-loader.min.js 1100ms)需在 Cloudflare 后台关闭 Speed→Rocket Loader。
- 注意:原 R2 路径 `.../expire-1-y/uikit/3.3.3/css/modify-black-font/uikit.min.css` 仍保留可访问(回滚用)。若日后引入 .uk-card-primary 等容器需黑字,须扩展补丁(参考此节差异清单)。
## UIkit 迁移生产/测试环境验收(对比通过)
- 部署后对比(真实浏览器 computed style 实测,agent-browser eval,注意本会话模型不可读图、勿再用"看截图"断言):
- 生产 box.tama.guru/_/tamakyi(旧代码):uikitSheet=r2 modify-black-font;.uk-light 容器色 rgba(0,0,0,.7);navbar_bg rgb(248,248,248);username 内联色 rgb(0,123,255)(小狼账号自设蓝)、intro rgb(205,76,108)(自设粉)。
- 测试 box-dev-api.337845818.best/_/admin6(新部署):uikitSheet=cdnjs.snrat.com 官方;.uk-light 容器色 rgba(0,0,0,.7)(补丁生效=modify-black-font 等价);navbar_bg rgb(248,248,248) 一致;username/intro 内联色 rgb(0,0,0)(admin6 账号默认黑,非 CSS 差异)。
- 结论:UIkit 相关 computed style 两环境逐值一致 → 迁移无回归。用户名/简介颜色差异仅来自各账号服务端颜色字段。
- 环境坑记录:agent-browser 冷启动需 `agent-browser install`(Chrome 152 下载 193MB,卡住原因=缺浏览器);open+eval 组合命令易超时被 auto-background,应 open 与 eval 分两条命令执行;本环境 curl 落盘/-o /dev/null 偶报 error 23 但不影响数据(可改 stdout+管道)。Chrome 位置 C:\Users\Tamakyi Gekka\.agent-browser\browsers\chrome-152.0.7977.82。
## uikit R2 vs snrat 国内外测速(5 次全量下载中位,2026-09-05)
- 国内本机:OLD-css 0.864s→NEW 0.200s(4.3x);OLD-js 0.796→NEW 0.163(4.9x);OLD-icons 0.824→NEW 0.177(4.7x)。三资源合计 2.48s→0.54s,净省 ~1.9s。
- 国外 check-host.net 多节点(两轮):CSS p50 R2 240-358ms vs snrat 233-420ms ≈ 打平(R2 偶发长尾 max 3.3-4.8s,snrat 平稳 ≤896ms);JS p50 R2 372ms vs snrat 647ms,R2 快 ~40%。
- 结论:snrat 国内完胜;海外 R2(Cloudflare 全球边缘)略优/打平。站点访客以国内为主 → 切 snrat 净收益大。若未来在意海外可 geo 双源(国内 snrat / 海外 R2),当前不做(过度设计)。
- 测速脚本已归档 dev/bench-cdn.sh(cn=国内 5 次全量取中位 / global=check-host.net 海外 p50/p90),cn 模式冒烟通过(复测数据与基线一致)。
## 部署架构澄清(用户补充 + 实测确认)
- 用户架构:正式 box.tama.guru 与测试 box-dev-api.337845818.best 均经 Cloudflare Tunnel 开放;box.tama.guru 是国内走腾讯 EdgeOne 的双入口。
- 实测(国内本机/出口视角):box.tama.guru → 43.174.247.43 Server: TencentEdgeOne(HTML 无 rocket-loader 注入,干净);box-dev-api.337845818.best & r2.337845818.best → 172.67.144.141 Server: cloudflare CF-RAY AMS(HTML 含 rocket-loader);cdnjs.snrat.com → 101.227.21.24(国内节点)。
- 推论:Rocket Loader 只在 Cloudflare 边缘对走 CF 的请求注入 → 仅影响海外路径与测试域名;国内 EdgeOne 路径 HTML 干净(EdgeOne 回源直连源站不经 CF HTML 重写)。本机出口连 CF 落在 AMS(阿姆斯特丹)→ 本机测 R2 慢不代表真实国内用户(其可能经 CF 亚洲/香港节点),但 snrat 国内节点快是实打实。
- 待用户确认:① 之前 1100ms rocket-loader 截图是否来自海外/测试域名或未接 EdgeOne 前 ② r2.337845818.best 是否也接 EdgeOne(未接 → 静态资源国内仍可能慢,是后续优化重点)③ CF 侧 Rocket Loader zone 开关位置。
- 【用户澄清+盘点完成】①截图=PageSpeed(海外探测),非国内 ②r2.337845818.best 未接 EdgeOne ③DNS 分流架构:dnspod 按境内/境外解析 box.tama.guru,境内 EdgeOne/境外 Cloudflare。据此修正认知:国内不受 Rocket Loader 影响;国内真实瓶颈=R2 的 CF 国际链路。全量盘点 r2 静态资源(outputs/r2-assets-cdn-audit-2026-09-05.md):sha256 校验 6 个 JS 库(jquery/dayjs/alpine/cropperjs/vConsole/waline.js)=纯官方原版;实测 R2 每资源 0.72-1.10s vs snrat 0.15-0.42s(2-7.4×);关键认知=公共 CDN(cdnjs.cloudflare/jsdelivr 0.6-1.8s)≠快,要选 snrat 国内节点。waline-no-commits.css 为刻意定制(39 条 .wl-card display:none 隐藏评论列表,与官方选择器全同)不可直换官方。easy.qrcode=remake 定制、neko-pixel 0.0.21 npm 查无、time.box.js/图片图标=专属 → 无公共源。建议:方案 A(R2 接 EdgeOne 根治定制文件)+方案 B(A 组 5 库迁 snrat) 组合,待用户批准。
- 【重大认知修正 + 方案落地】itdog 大陆多节点测速(用户截图):box.tama.guru(EO新加坡,43.174.247.43)大陆均值 3.2-3.6s、三网骨干峰值 9-11s(四川电信 11.1s);box-dev-api(纯 CF,172.67.144.141/147.52/80.1)大陆均值 5.7-6.5s、峰值 12s。**反向分布**:港澳台/新疆/内陆部分省份反而 300-500ms。结论:① CF 与 EO 单走大陆都慢 3-12s,CF tunnel 不是备案前解药,删 DNS 分流的方案被证伪 ② R2 大陆实测 ~250ms 正常,静态资源非瓶颈 ③ 真正瓶颈=大陆→境外源站(EO新加坡/CF边缘)动态页链路。Rocket Loader 用户已手动关闭,HTTP/3 已开启。后端审计初步:box-dev-api 的 /favicon.ico→302 首页、robots.txt 1.1-2.4s(本机 AMS 视角)、cf-cache-status: BYPASS → 静态未缓存且每次回源,存疑点待大陆视角复核。根本解决路径只剩备案+大陆 CDN(腾讯云/阿里云,或 CF 中国伙伴京东云/世纪互联),备案前可做项=静态库迁 snrat。方案 B 已落地:jquery3.7.1/dayjs1.10.8/alpine3.9.0/cropper1.6.2(js+css 用 min 版)/vConsole3.12.1/waline2.15.8 共 7 文件全部 sha256 校验一致后 R2→cdnjs.snrat.com(4 文件:header/list/question-not-found/profile),备份在 .workbuddy/backup/cdn-migration-20260905/,go test templates 通过。R2 保留:easy.qrcode remake/waline-no-commits.css/time.box.js/pickr(1.10.1 snrat 404 未收录)/neko-pixel 0.0.21/logo/社交图标。
- 【snrat 部署验收通过】用户部署 box-dev-api 后用浏览器实测 admin6 主页:HTML 全部 snrat 引用已生效(uikit/css/js/icons、dayjs/alpinejs、waline.js/jquery/min),无 Rocket Loader 注入。document.readyState=complete,UIkit/Dayjs/jQuery/Waline 全局对象全部存在,UIkit 组件(navbar、tab、表单、按钮)渲染完整、.uk-light 补丁正确生效、定制文件(waline-no-commits/easy.qrcode remake/time.box.js/社交图标)继续走 R2 无回归。截图已存 outputs/admin6-snrat-check.png。验收小波折:open+eval 组合命令易卡顿(已分步解决),首次 eval 在 defer 脚本未执行完时探测 typeof UIkit=false 属时序问题,等 3s 后 UIkit=true。
- 【生产部署验收通过】box.tama.guru 主页/用户页 HTML 引用全部确认(uikit/dayjs/alpinejs/jquery/waline.js → cdnjs.snrat.com,旧 R2 库残留=0,定制文件 waline-no-commits/time.box.js/tamabox_low.png 继续 R2 200 可达)。浏览器实测 /_/tamakyi:UIkit/dayjs/jQuery/Waline 全局对象全部就位(UIkit=function)、navbar rgb(248,248,248)、.uk-light rgba(0,0,0,.7) 补丁生效、用户名 rgb(0,123,255) 账号自设色、28 UIkit 按钮,无回归。截图 outputs/prod-tamakyi-snrat-check.png。⚠️ 踩坑记录:agent-browser 探测 defer JS 库有显著时序窗口——本机(AMS 出口)拉 snrat 国内节点 133KB uikit.min.js 需数秒,过早 eval typeof UIkit=false 属假阴性;且 CDN 无 Timing-Allow-Origin 时 resource timing 全 0 属正常。正确姿势:open 后 sleep≥8 再 eval,或先 fetch 触发等待;判断库是否成功以全局对象(typeof)为准,勿信 resource timing。
- 【后端审计补完】澄清两个疑点+确认一个真问题:① /favicon.ico、/robots.txt、/ 的 302 是 curl -sSI(HEAD) 假象——flamego f.Get 路由对 HEAD 不匹配返回 302,GET 全部 200,浏览器无影响,撤稿之前"favicon 302"结论。② 真问题:route.go 的 favicon/robots/sitemap/llms/llms-full 5 个 embed handler 只设 Content-Type 无 Cache-Control → CF cf-cache-status: BYPASS、EdgeOne Age:0 每次回源 tunnel。全项目仅 share.go:501 share-card 设了 public max-age=300;/static/* 由 flamego.Static CacheControl 统一 86400。③ 动态页 HTML 无缓存头是预期(个性化),edge 每次回源属免备案架构使然。待批修复:5 个 handler 各加 Cache-Control: public, max-age=86400(约 5 行,纯增益)。源站不在本机(无 cloudflared/tamabox 进程),localhost 直测不可行。
- 【修复落地】route.go 5 个 embed 静态 handler(/favicon.ico /robots.txt /sitemap.xml /llms.txt /llms-full.txt)各加 Cache-Control: public, max-age=86400。验证:gofmt 干净 + go build -overlay 编译通过 + go test ./internal/route/ 通过。⚠️ 环境坑记录:Windows 本机全量 go build/go test 会因 chai2010/webp@v1.4.0(cgo 依赖 libwebp) 报 undefined webpGetInfo 等——需用 dev/webpstub 机制:dev/overlay.json 已失效(指向旧仓库路径 TamaBox-v4-time),正确姿势=自建 overlay 把 internal/storage/s3.go 替换为 stub(s3.go 导出符号仅 MaxUploadBytes/UploadAvatarToS3/UploadBackgroundToS3/UploadPictureToS3/ConvertStaticImageToWebP + oss.go 引用的私有 isSupportedImage),本次 stub 存 %TEMP%/s3_stub_tamabox.go、overlay 存 %TEMP%/ov-tamabox.json,命令:go build/test -overlay=...。部署链:Dockerfile CGO_ENABLED=0 + go build ./cmd/(生产无碍)。
- 【测试环境缓存修复验收】box-dev-api 5 路径(favicon/robots/sitemap/llms/llms-full)响应均已带 Cache-Control: public, max-age=86400(HTTP 200)。⚠️ CF 缓存机制要点:无 cookie 首访 → session 中间件 Set-Cookie(flamego_session) → cf-cache-status: BYPASS 永不缓存;带有效 cookie 请求 → 不 Set-Cookie → MISS→HIT(Age 递增) 正常缓存。真实浏览器=先访问 HTML 已种 cookie→后续 favicon 等请求均带 cookie→缓存命中;无 cookie 场景仅 curl 首访/搜索引擎爬虫(低频,可接受)。测试环境通过,用户将部署生产。若日后想让爬虫也命中缓存:把静态路由挪到 session 中间件之前或让 session 跳过静态路径(本次未做)。
- 【答疑】/.well-known/pki-validation/fileauth.txt 删除后仍被爬:① 线上实测两环境均 302→/(Go http.Redirect 标准输出),文件内容确已删除生效;② 请求持续=全网证书/安全扫描器将 .well-known/pki-validation 与 acme-challenge 同列标准探测目标,按字典批量扫所有域名,与文件是否存在无关;③ 302 来源=route.go:396 f.NotFound handler(先 ab.Record 404 计数再 ctx.Redirect("/"),注释说明 flamego NotFound 先写 404 再 302);④ 噪音放大:探测 302→扫描器跟随→再请求 / 首页,日志翻倍且 CDN 不缓存 302(EO-Cache-Status: MISS/DYNAMIC)每次回源。治理可选:CDN 规则边缘拦 .well-known/*(推荐,零回源)/f.NotFound 对探测路径直接 404 不 302/autoban 敏感路径加速封禁。等待用户选方向。
- 【架构澄清+瓶颈定位实验设计】用户澄清架构:海外=CF+tunnel+优选网络;国内=EdgeOne 直连源站 IP(回源不走 tunnel)。质疑"大陆→境外源站"归因。回应要点:大陆用户连的是 EO 节点非源站(此前表述简写失准),但 EO 国际站无大陆 PoP(43.174.247.43=新加坡),"境外段"=大陆→EO 新加坡节点(itdog 3-11s 证据即此段)。分离实验:生产 robots.txt 已确认 EO HIT 缓存(MISS→HIT Age 2/3,证明 cache-control 5 行修复已部署生产生效)→ 用户 itdog 对比 robots.txt(HIT 零回源) vs /_/tamakyi(每次回源),差异=EO→源站回源段+源站处理;若 robots 也 3s+ 则瓶颈纯在大陆→EO 段与回源方式无关。需用户补测+告知源站位置(决定回源段性质:源站大陆则 EO 新加坡→大陆回源也属国际链路)。
- 【链路分解实测完成】用户确认服务器在境外。三组实测:① 本机 curl(AMS 出口):robots.txt(EO HIT 零回源) 0.6-2.2s 波动 vs 动态页 /_/tamakyi ttfb 0.87-1.46s——EO HIT 也波动大,非回源差异;② check-host.net 全球 25 节点:robots.txt(EO HIT) p50=1258ms min505(us2) vs 动态页(回源) p50=1217ms min735(jp1)——**两者几乎相同**→ 证明 EO→境外源站回源段快(服务器境外),动态页不比零回源静态慢;③ agent-browser 页面级(AMS):TTFB 348ms、HTML download 555ms、load_total 1.1s、最慢资源 log beacon 1944ms。结论:动态页慢的根因=用户→EO 新加坡节点段(大陆 itdog 3-11s 因国际出口拥塞;连欧美节点也 0.5-2.7s 因 EO 国际站无大陆 PoP 且节点网络质量差),与回源方式(tunnel/优选/直连IP)无关。对照:snrat(国内 cdnjs 镜像) 全球 p50 仅 257ms ≪ EO p50 1258ms,EO 国际站全球表现差。根治=备案+大陆 CDN。
- 【HTML 压缩评估】生产 /_/tamakyi:原文 61,125B(内联 style 11 块 19,972B + 内联 script 10 块 14,048B + 外链 9js/2css + 空白 4KB)。gzip 已在源站开启(internal/route/gzip.go GzipHandler 挂 http.Server 最外层,处理 204/304/嗅探/Flusher,不主动 flush 单块输出;EO 透传带 Vary),gzip-9 后仅 14,966B(24%)。无显式 Flush 调用点 → 无多次 RTT 问题。结论:压缩已最优;555ms download 主因是高 RTT 非体积;内联 CSS 20KB 是设计(高 RTT 下内联优于外链+缓存),不建议外链化;剩余微优化(minify 内联块/去空白)收益 <1KB gzip,无意义。真正优化=降 RTT(大陆 CDN)。
- 【EO 优选调研】结论:①有,社区"EO 优选 IP"玩法与 CF 优选同原理:EO 官方 CNAME 把大陆调度到新加坡(43.174.247.43 AS139341 ACE)慢,社区做法=DNS 改 A 记录直指香港等优质 EO 边缘 IP(43.174.150.x 香港同 AS)。数据源 wetest.vip(page/edgeone/address_v4 需登录,15min 更新)/cf2dns 面板/archlinuxcn forum 教程。②实测:xhh 教程香港 IP 43.174.150.199/119 已失效(000,时效性强需活源+月度更新);EO 大陆可达节点列表(EdgeOne-ips-Checker available_eofreecn_ips 167 个大陆 IP)接口 2026-07-31 停服 8-31 下线,实测全 000 → B 类已死。③EO 官方内建"自研协议优化/回源链路探测"(类 Argo)但无法解决无大陆 PoP;优选只能把边缘从新加坡换香港(大陆 3-11s→预计 50-200ms,待 itdog 验证)。④落地=dnspod 境内线路解析值改 A 记录指向活香港 EO IP(多 IP+TTL60-300s+月度更新),境外保持 CF。风险:灰色玩法/证书 SNI 需实测/EO 不承诺。结论已回复用户,未动手改 DNS。
## EO 优选方案 B 免费实测(用户排除付费 wetest.vip 后执行)
- 【用户确认】方案 B=自扫或公开 CNAME 均可,不限香港,周边区域比新加坡好即可。
- 【免费公开源全灭实测】wetest.vip 需会员(公开页0 IP);*.v1/v4.eo.fastlydns.cn、eo.072103.xyz、eo.088086.xyz 全部 NXD;蓝点网/雾创岛 43.174.150.x 香港 IP 证书已退回 *.cdn.myqcloud.com(CDN 通用池) 全失效。博客佐证(2026-02):"EO 现在不能使用优选节点,非适用节点返回 418"。
- 【方法论修正】TLS 握手成功≠可用(腾讯 CDN 泛播池也握手);正确判据=schannel 证书校验过(CN=box.tama.guru)+HTTP 200+Server: TencentEdgeOne。
- 【扫描事实】43.174/43.175 全 /16 抽样(3072)+官方段/HK/JP 细扫(56 /24≈14000 IP):能服务 box.tama.guru 的 EO 池 9 段全新加坡(192-195/218/240-241/246-247);EO 泛播确实同步全球——HK(43.175.115/119/43.174.126 共8 IP)、东京(43.175.16/17/18/113 共6)、大阪 107.217、AMS/DXB 零星可服务。
- 【南宁电信视角(真实大陆)】官方新加坡 TCP 180ms/TTFB 0.8-1.0s 反而最优;香港候选 351-377ms/TTFB 1.3-1.8s、东京 276-299ms/TTFB 1.1-4.0s(不稳) 均更慢 → 官方 CNAME 即 EO 对大陆优化调度,自扫候选跨境路由绕。单点结论≠全国,华南可能反转,已列 15 个候选 IP 待用户 itdog 多节点验证(报告 outputs/eo-free-probe-2026-09-05.md)。
- 【工具沉淀】dev/eo-probe-ips.py(TLS快速预筛)、dev/eo-probe-v2.py(schannel 严格判定,可复用任意域名)、dev/eo-scan-all.py(TCP+openssl 两段式大范围扫描)。
- 【下一步】用户 itdog 验证候选 vs 新加坡;若均不优→方案 B 到此为止(免费优选时代窗口 2025中-2026初已关),剩余=备案+大陆CDN 或维持现状。
## 头像+二维码加载慢诊断(个人主页 /_/tamakyi,南宁电信实测)
- 【根因1 P0】R2( r2.337845818.best ) 图片对象全部无 Cache-Control(仅 ETag+Last-Modified,cf-cache-status: DYNAMIC);头像 Last-Modified=当天→启发式缓存≈0→每次页面加载重下头像。304 也 1.05s(RTT 省不掉)。上传代码 s3.go PutObjectInput 从未设 CacheControl(已确认)。
- 【根因2 P0】R2 纯 CF 国际链路:南宁实测每资源 0.78-1.4s(头像 0.9-1.05s/背景 297KB 1.39s/logo js 0.96s/1.6KB time.box.js 也 0.9s=RTT 主导);snrat 大陆镜像 0.3-0.5s 作对照。
- 【根因3 P1】easy.qrcode.min.js(52KB,R2) logo 分支:头像 onload 才绘制,onerror 仅 console.error 无降级→头像挂/失败=二维码永久空白。
- 【根因4 P1】头像缓存键分裂:header.html icon/favicon 用 ?og-image=1(646-648),list.html+页面头像+QR logo 用 ?v=2 → 一页下载 2 份。
- 【根因5 P1】背景 box_background.png 303KB 未压缩 PNG(2024-08 上传未变)。
- 【根因6 P2】defer 统计脚本拖 DOMContentLoaded:vercount 1.24s(波动2.7s)/mami 波动3.7s → 二维码初始化(DCL)被拖。
- 【修复方向】P0① s3.go 加 CacheControl public,max-age=31536000,immutable(图片 key 随机不可变安全);P0② easy.qrcode onerror 降级无 logo 绘制+超时;P1③ 统一头像 query 键(og-image=1 无 Go 处理疑似遗留);P1④ 背景转 WebP≤60KB;P1⑤ 统计脚本 async;P2⑥⑦⑧(R2 前接 EO 回源=治本)。
- 报告 outputs/avatar-qrcode-slow-2026-09-05.md;抓包快照 outputs/tb-page-snapshot.html;库源码已存 dev/easy.qrcode.min.js、dev/time.box.js。
## P0 批落地:s3.go immutable 缓存头 + 二维码降级渲染(用户批准后执行)
- 【用户授权】"先备份当前源码,然后尝试吧"→ 执行诊断报告的 P0-① 与 P0-②。
- 【备份】.workbuddy/backup/p0-avatar-qrcode-20260905/(s3.go + header.html + list.html + question-not-found.html + profile.html,含修改前版本;header.html 修改前版本用脚本从当前版精确移除新增块生成,已与 cdn-migration 基线比对仅差上午 10 行 CDN 迁移改动,验证一致)。
- 【改动1 s3.go】uploadPictureToS3 的 PutObjectInput 增加 CacheControl: public, max-age=31536000, immutable(key=picture/日期/随机hex 内容不可变,安全)。SDK v1.47.5 PutObjectInput.CacheControl 字段验证存在;gofmt 通过。
- 【改动2 header.html】<body> 后新增全局 window.tbRenderQR(el, getOptions):核心修复 easy.qrcode logo/背景图加载失败仅 console.error 无降级导致二维码永久空白。策略=已知可用层立即绘制(logo 记忆命中零等待一次成型)→ 未知层先省略画基础码(立即可扫)→ Image 探针判定后重绘升级版;失败记忆 _tbQrGood/_tbQrBad 按 URL 同会话缓存;3s 超时兜底;迟到 onload 仍能补 logo。实测 8 个行为用例全过(node stub QRCode/Image)。
- 【改动3 三个调用点】list.html(公开主页)/question-not-found.html:new QRCode→tbRenderQR;profile.html updatePreview 同改(debounce 高频重绘场景靠 URL 记忆避免闪烁)。背景图分支(库内无 onerror 同风险)一并纳入降级。
- 【验证】go test ./templates/ 全绿(TestTemplatesParse/CrossReferences 等);8 行为用例 PASS;改动 diff 已复核为纯增量。
- 【部署注意】缓存头只对新上传对象生效;历史对象需手动(可用 CF 批量 purge 或 R2 重新上传);模板改动无后端逻辑变化。待用户部署后浏览器验证:二次访问头像应 0ms 级、头像坏链时二维码仍可扫。
## CORS/画布污染核查(P0-② 二维码降级改动安全性)
- 【结论】当前部署不会引发 CORS 失败或 canvas 污染。三个前提实测:① R2 已配 CORS 白名单,box.tama.guru 与 box-dev-api.337845818.best 均返回 ACAO(localhost:3000/example.com/裸域 无 ACAO);② list/profile options 显式 crossOrigin:'anonymous'(注释注明"直连 R2 跨域图须 CORS 模式"),tbRenderQR 默认同值,与库内 new Image 的 CORS 模式一致,探针与库绘制走同一缓存键;③ easy.qrcode 内部 makeImage 后调 this._elCanvas.toDataURL("image/png")(catch 后 console.error + 提示 "Configured crossOrigin:'anonymous'")——库开发者已内置该诊断。
- 【污染免疫原理】canvas 污染只发生在"no-cors 隐式加载成功但无 ACAO"的中间态;tbRenderQR 探针失败(onerror)→ 根本不 drawImage → 无污染;成功 → 响应必带 ACAO → 干净。不存在绘制了却污染的路径。
- 【边界提醒】R2 CORS 白名单只有 2 个源;新增页面域名/本地开发需先加白名单,否则 anonymous 探针 onerror → 降级基础码(可扫但永不带 logo,不报错)。favicon/og-image/meta 均非 canvas 上下文,无需 CORS 无污染风险(header.html 640-653 直指 R2)。
## P0 批测试环境验证(agent-browser 实测 box-dev-api.337845818.best/_/admin6)
- 【静态部署检查】HTML 含 tbRenderQR 2 次(helper 定义+1 调用)/ crossOrigin:'anonymous' 1 次 / logoBackgroundTransparent 2 次 / new QRCode(document.getElementById 为 0(已替换)→ 三处调用点全部到位。
- 【正常路径】#boxqrimg canvas 200x200、toDataURL 成功(base64 长 28578 无污染)、中心 20x20 像素全为非白非黑"other"且色相分布为深色照片调(4,4,3 棕色系 96 像素、7,6,1 等)→ 头像 logo 已绘制升级版。
- 【降级路径】临时 div 用坏头像 URL(不存在 picture path)调 tbRenderQR:立即态 canvas 已生成(len 4226 紧凑基础码、exportable true);3s 后状态保持(childCount 1、exportable true);_tbQrBad 写入坏 URL;_tbQrGood 同时记录真实页面头像 9af3e38?v=2 与背景图 25e2d84 → 双向印证降级免疫污染。
- 【加载中排查】页面 2 个 uk-spinner:"正在生成分享图…"(分享卡区正常态)+ captcha modal 内的"加载中..."(rect 全 0 不可见);"加载更多 加载中..."按钮 display:none → 均非主诉"全屏加载中"覆盖层。用户主诉已消除。
- 【截图】outputs/tb-dev-test-qr.png 全页、tb-dev-test-qr-crop.png 头部裁切:头像、用户名、提问按钮、二维码均已显示,无加载中残留。
- 【代理坑提醒】agent-browser open <url> 因测试站境外+defer 脚本多会卡 load 等待;可靠做法:先 agent-browser open (about:blank 启动 daemon) → 再 open <url> 导航 → 然后 sleep ≥10s 让 defer 脚本就绪再 eval。
- 【结论】P0 批(s3.go immutable 缓存头 + 二维码降级)测试环境验证全部通过,可部署生产。
## P0 批正式环境验证(agent-browser 实测 box.tama.guru/_/tamakyi)
- 【静态部署】HTML 60.1KB;tbRenderQR 2 次/调用点 1 处替换/crossOrigin 'anonymous' 1 次/logoBackgroundTransparent 2 次/new QRCode(document.getElementById 0;头像引用 r2.337845818.best/.../2e571390...(2026/09/05 新传)→ 与测试环境一致部署完整。
- 【正常路径】#boxqrimg canvas 200x200 exportable len 31726;中心 20x20 像素全为深色照片调(7,7,7 灰+蓝黑系)→ 头像 logo 已绘制。
- 【降级路径】坏 URL 测试:立即态 len 4226 基础码、3s 后保持 stillCanvas exportable len 4226 不空白;badMem=1、goodMem 含 2 个真实 URL(头像 2026/09/05 新头像 + 背景图 2024/09/22)→ 双向印证探针与降级免疫污染均正常。
- 【加载中残留】visibleLoading=[](精确定位 rect>0 且文本含"加载中"的可见元素);之前的 uk-spinner/captcha/loadmore 在生产页同样不可见。
- 【截图】outputs/tb-prod-test-qr.png:头像、二维码码眼完整、无加载中遮挡 → 与测试环境一致通过。
- 【坑补充】第一次 `agent-browser open | open URL` 链式启动 3+ 分钟无输出卡死(即使之前测试环境用同样命令成功),最终拆成单条独立命令即恢复。原因可能是 Chromium 冷启动资源紧张或 chain 顺序导致首条 stdin 未及时关闭。经验:永远单条命令、第一条单独跑。
- 【结论】P0 批正式环境全部通过;图片缓存头对老对象(2024/09/22 背景图)暂不生效,新上传对象生效,需要时间自然过渡。
## autoban 误伤诊断(用户 app.ini: window=10s, sensitive=10, 404=20, duration=15min, trust_proxy=true)
- 【机制】全站仅 2 个计数入口:中间件敏感路径 defer Record(autoban.go:39)+ f.NotFound 404 Record(route.go:403)。业务路由/模板 77 URL × 39 敏感词零碰撞 → 正常页面不误计。
- 【误伤根因】正常用户 10s 内凑不出 10 敏感/20 404 → 误伤必是"他人探测记到自己键上":P0-A IP 键折叠(EO 未透传 EO-Client-IP 时全大陆回退到 43.174.247.x 新加坡边缘共享键;CF tunnel 异常时回退 127.0.0.1);P0-B CGNAT 共享出口连坐(国内 NAT444 常态,管理员自伤主因);P1-C IPv6 /64 聚合连坐;P1-D 双 CDN 未删对端权威头可被伪造头定向嫁祸。
- 【止血配置】window 60s + sensitive 30 + 404 100 + security_alert_email 开(先看封禁 IP 判断折叠)。治本=EO 开回源 IP 透传 + 源站防火墙锁 CDN 网段 + 双 CDN 删对端头。
- 【代码增强候选】登录 session 豁免计数、封禁 logrus 日志、UA 二次确认。报告 outputs/autoban-false-positive-2026-09-05.md。
## autoban 策略修正实施:ban_track_notfound(用户选定"配置开关版")
- 【背景】生产新误伤案例 106.7.154.160(真实国内电信 IP,非 CDN 边缘):样本 /_/Liarcs×4 + /×2 全是计数事件;但 domain/题目不存在走 302(Pager/Questioner 均 ctx.Redirect)不计数 → 能进 f.NotFound 的多为非 GET 探测或未注册路径;告警 paths 仅 6 条且受 2min cleanup 影响可跨窗口累积 ≠ 触发原因;触发=10s 内 ≥20 次 f.NotFound 或 ≥10 敏感。疑似共享出口(CGNAT)连坐或目标性探测。
- 【评估结论】敏感通道 35 项扫描特征与业务路由全表零重叠→零误伤;404 通道是唯一误伤源但价值低(主流字典必碰敏感项;/_/{domain} 枚举靠 200/302 区分本来就抓不到)→ 砍 404 计数收益>损失。
- 【改动 5 文件,备份 .workbuddy/backup/autoban-track-notfound-20260905/】
1. internal/conf/static.go:Security 加 BanTrackNotFound bool `ini:"ban_track_notfound"`(默认 false)
2. internal/security/autoban/autoban.go:Options.TrackNotFound、Banner.trackNotFound、New 赋值;Record 对 KindNotFound && !trackNotFound 直接 return
3. internal/route/route.go:Options 传 TrackNotFound: conf.Security.BanTrackNotFound;f.NotFound 注释同步
4. internal/security/autoban/autoban_test.go:newTestBanner 默认 TrackNotFound:true;新增 TestNotFoundTrackDisabledByDefault;TestDefaults 断言 trackNotFound==false
5. conf/app.sample.ini:security 段加 ban_track_notfound = false + 注释(真实 app.ini 在各部署环境,需用户同步补键或留空=默认关)
- 【验证】gofmt 干净;go vet OK;go test 三包全绿(route/conf/autoban,带 -overlay=tb_overlay.json 规避 cgo webp);新用例 PASS。
- 【部署提醒】测试/生产 app.ini 无需强制加键(缺省 false 即关闭 404 计数);若想恢复 404 计数置 true。conf/app.ini 不在本地副本(conf/ 仅 app.sample.ini)。
## ban_track_notfound 测试环境实测(box-dev-api,出口=本机南宁电信)
- 【正常路径】/ + /_/admin3..admin9 全 200 无 403;不存在 domain ×8 → 302 不封(业务 302 通道确认);admin6 页 SSR title「admin6 - 狼的提问箱」正常。
- 【404 洪水】未注册路径 ×30 → 全 302(location:/) 不封(TrackNotFound=false 预期,但受 enable_auto_ban 前提约束)。
- 【敏感×37 异常】/.env /admin /wp-login.php 等共 37 个敏感路径(默认阈值 10 的 3.7 倍)→ 全 302 无 403。302 均为服务端 f.NotFound Redirect(location:/ + securityHeaders + CF NEL),确认到达应用层 → 疑似测试环境 enable_auto_ban=false(或阈值极高/实例未跑新二进制),需用户核对 app.ini + 日志后才能定论。若 enable=false,本测无法验证 TrackNotFound 语义,建议测试环境 enable_auto_ban=true 后重测。
- 【教训】agent-browser open 境外站再次卡死(>3.5min),TaskStop 清理;渲染验证降级 curl 抽查 HTML 即可。
## ban_track_notfound 测试环境复测通过(22:17-22:35,出口 222.218.124.37)
- 【上轮误判根因】串行 curl 每个请求间隔 1-2s(南宁→CF→源 RTT),13 个敏感请求跨 18s > 10s 窗口 → 永不达阈值;日志时间戳证实。非配置问题。
- 【复测方法】xargs -P 25 并发打,窗口内密集命中。
- 【结果】① 正常路径 / + admin3-9 全 200;② 纯 404 并发 x25 → 全 302 无 403(TrackNotFound=false 生效);③ 敏感路径并发 x25 → 前~10 个 302 后 15 个全 403(阈值10触发封禁);④ 封禁后 / 与 /_/admin6 也 403(IsBanned 全局生效)。
- 【副作用】本机出口 IP 222.218.124.37 被测试环境临时封禁 15min(约至 22:35 自动解除);测试环境日志可见该批 403 + 若有 security_alert_email 会收到告警(含样本路径)。
- 【结论】修复语义完整闭环:正常业务放行、纯404豁免、敏感扫描封禁。部署生产即可。