Skip to content

修复静态资源缓存顽疾:URL 加内容哈希版本指纹 + no-cache 响应头 - #25

Merged
zj1123581321 merged 1 commit into
mainfrom
fix/static-asset-versioning
Jul 21, 2026
Merged

zj1123581321 merged 1 commit into
mainfrom
fix/static-asset-versioning

Conversation

@zj1123581321

Copy link
Copy Markdown
Collaborator

范围

生产事故修复:部署新版后用户端 floating-toc.js 被中间代理/浏览器缓存旧版本(强刷+无痕均拿不到新文件),页面 HTML 是新结构但目录面板跑旧逻辑。

  • 启动时对 src/web/static 全部文件计算内容合并 sha256 短哈希,注入模板全局 asset_v;模板中 floating-toc.js/css、history.html 引用改为 ?v={{ asset_v }}——内容变 URL 必变,任何缓存都不敢用旧的
  • /static/ 响应统一附加 Cache-Control: no-cache(每次回源验证,ETag 命中仍 304 省带宽)
  • index.html/history.html 为纯静态页不经模板,其子资源由 no-cache 头覆盖(最小侵入,见 PR 说明)

验证证据

  • 新增 12 个测试(令牌稳定性/变化性、模板渲染带版本参数、no-cache 头与 304 行为)
  • uv run pytest tests/unit:2476 passed 全绿

@github-actions

Copy link
Copy Markdown

Reviewed commit: e392f6b4a21b7293643b6fe4c474f730d88669d5

🤖 Review 门禁 — 判定: pass(主审: claude-glm)

该 PR 实现正确、内部自洽,且与"修复静态资源缓存顽疾"的设计意图一致。我逐项核对:

  1. compute_asset_version(context.py:1895-1912):对 static_dir 下全部文件按"相对路径+\0+内容+\0"做合并 sha256、排序保证确定性、截 8 位十六进制。缺失目录时返回稳定哈希,不崩溃。token 为纯十六进制,注入 URL 无 XSS 风险。
  2. get_templates(context.py:1915-1920):在 templates.env.globals 注入 asset_v。views.py:30 在模块加载时调用一次 get_templates(),进程内只算一次哈希,无性能问题;所有 templates.TemplateResponse 渲染路径(transcript/error/processing/cleaned)都共用同一实例,asset_v 作为全局变量在 base.html 及其子模板中均可用。
  3. NoCacheStaticFiles(app.py:35-47):覆写 get_response 对所有 /static/ 响应(含 200/304/404)附加 Cache-Control: no-cache,签名与父类一致;fastapi>=0.115 捆绑的 Starlette 支持 ETag/304,304 测试成立。
  4. 模板改动:floating-toc.css/js、base.html 的 history.html 链接均加 ?v={{ asset_v }};transcript.html 内仅有这两个外部静态引用,已全覆盖。无重复 import(hashlib 在 context.py:12 正确新增)。
  5. 测试:12 个新增用例覆盖令牌稳定/可变/缺失目录、模板渲染含版本参数、no-cache 头与 304 行为,并更新了既有 test_frontend_nav.py。无 blocker/major 缺陷。

下面两条为 nit,不影响合门。

覆盖:single;1 个审查单元;307 行 diff。

  • nit · testing · testing.navenv-empty-asset-v tests/unit/web/test_frontend_nav.py:28 — 这条结构断言只验证了 URL 前缀结构,无法保证 asset_v 是真实令牌——即便生产侧 {{ asset_v }} 被误删或恒为空,该测试仍会通过。该薄弱断言对模板渲染 asset_v 的实际生效不构成回归保护(虽然 test_static_asset_versioning.py 的 test_transcript_page_versions_floating_toc_assets 用真实令牌做了强校验,覆盖存在于别处)。
    • 触发:test_frontend_nav.py 的 _jinja_env() 构建的是一个独立 Jinja2 环境(loader + autoescape=True),并未注入 asset_v 全局。
    • 证据:第 28-33 行 _jinja_env 只 new 出 jinja2.Environment(loader=FileSystemLoader, autoescape=True),未设置 env.globals['asset_v']。base.html:1047 的 /static/history.html?v={{ asset_v }} 在该环境下 asset_v 为 Undefined,渲染为空串,实际输出为 history.html?v=。第 96-99 行 test_contains_history_page_link 断言 'href="/static/history.html?v=' in html,该子串在令牌为空时仍然命中。
    • 验收:在 test_frontend_nav.py 的 _jinja_env() 中注入 env.globals['asset_v'](例如取自 video_transcript_api.api.context.get_templates 或一个固定常量),使该套件渲染出的 URL 带有非空令牌;保持现有断言即可验证真实结构。
  • nit · design · design.entrypage-unversioned-history src/video_transcript_api/api/routes/views.py:166 — 与引发本事故的同类失败模式(中间代理忽略缓存指令)相比,这些入口页对 history.html 缺少内容指纹这道二保险。PR 正文已将其明确为'最小侵入'的权衡,history.html 变更频率低、实际风险有限,故仅作 nit。floating-toc.js/css 这一致命路径已用版本令牌根治,符合本 PR 的核心目标。
    • 触发:PR 正文声明 index.html/history.html 为纯静态页、其子资源由 no-cache 头覆盖;但首页 _HOME_HTML(views.py:166)与 add_task 页 index.html(static/index.html:27)对 /static/history.html 的引用均未带版本令牌。
    • 证据:views.py:166 <a class="cta" href="/static/history.html" ...>任务历史</a>;static/index.html:27 <a href="/static/history.html" class="site-nav-link">📋 任务历史</a>。二者经 HTMLResponse 提供不经模板,故无法注入 asset_v。这些入口页指向的 history.html 仅靠 /static/ 挂载的 no-cache 头回源验证。
    • 验收:如需彻底闭环,可让 _HOME_HTML/history 链接与 add_task 页的 history 链接也带上 ?v=(例如在视图层渲染时注入已算好的 asset_v,或把首页/提交页改为轻量模板)。若接受当前 no-cache 覆盖方案,则在 PR 说明中保留该已知限制即可,无需改动。

@github-actions

Copy link
Copy Markdown

📒 Review ledger state

  • Commit: e392f6b4a21b7293643b6fe4c474f730d88669d5
  • Round: 1
  • Status / findings: pass / 2
  • Comparison: first_review

完整数据保存在 codex-review-ledger artifact;此 sticky comment 仅保存跨 rerun 的连续游标。

@zj1123581321
zj1123581321 merged commit ddb4ce6 into main Jul 21, 2026
2 checks passed
@zj1123581321
zj1123581321 deleted the fix/static-asset-versioning branch July 21, 2026 04:25
zj1123581321 added a commit that referenced this pull request Jul 23, 2026
- 260723-0214 HANDOFF 头部标注已被 260723-0246 取代(范围锁定 Stage 1)
- 章节 plan §12:接线批 T1/T6-T10 与计划外 T11 实际已于 2026-07-19
  全部完成并部署生产(stacked PR #15-#25),checkbox 此前未同步

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
zj1123581321 added a commit that referenced this pull request Oct 15, 2026
修复静态资源缓存顽疾:URL 加内容哈希版本指纹 + no-cache 响应头
zj1123581321 added a commit that referenced this pull request Oct 15, 2026
- 260723-0214 HANDOFF 头部标注已被 260723-0246 取代(范围锁定 Stage 1)
- 章节 plan §12:接线批 T1/T6-T10 与计划外 T11 实际已于 2026-07-19
  全部完成并部署生产(stacked PR #15-#25),checkbox 此前未同步

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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