Disable static asset caching
This commit is contained in:
parent
4ee09976ee
commit
c898ff863d
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
> 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9`。
|
||||
|
||||
最后更新:2026-06-08(前端模块化 Step 2 完成:抽出 chat.js;main 75 行,路径 1 收官)
|
||||
最后更新:2026-06-08(task_progress 进度工具 + 前端固定进度区 + 静态资源 no-cache)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -21,6 +21,10 @@
|
|||
|
||||
## 已完成关键能力
|
||||
|
||||
### 2026-06-08
|
||||
|
||||
- **新增 Codex 式 `task_progress` 进度工具 + Web 固定进度区**:`TaskProgressTool` 默认注册到 agent,支持 `set_plan/update_step/clear`,返回极短 UI-only 结果;上下文压缩对旧 `task_progress` tool_call/result 做专门折叠,避免进度历史长期占 prompt。前端新增 `progress.js` 做 task 级进度状态合并,修复 `update_step` 只带 `{id,status}` 时因缺标题不显示的问题;当前进度显示从助手消息内提升到 `#task-progress-dock`(对话流下方、输入框上方),历史消息内仍保留进度块作记录。system prompt + coding/ppt/proposal/analyze skill 加轻量使用约定,要求只在多步骤关键阶段少量更新。**部署侧补静态资源 no-cache**:`NoCacheStaticFiles` 替换默认 `StaticFiles`,让浏览器重新校验 `/static/*.js` 等资源,避免前端修复已部署但旧 `chat.js` 仍被缓存导致看不到进度区。校验:`pytest tests/test_context_compaction.py tests/test_task_progress_tool.py tests/test_executor_docker.py tests/test_static_vendor.py -v` 相关集通过;`node --test tests/frontend_task_progress.test.mjs` 2 过;`node --check web/static/js/chat.js web/static/js/progress.js` 过。
|
||||
|
||||
### 2026-06-06
|
||||
|
||||
- **前端模块化 Step 2 收官:抽出 `chat.js`(对话视图)+ main.js 缩成 75 行入口**:最后也是最缠的一块——任务列表(浏览/筛选/滚动)+ selectTask 切换 + renderChatMeta/模型下拉 + renderMessages + live-run 助手 + sendMessage/cancel + fetchSse/handleSseEvent + 润色/粘贴文件 + 完成/废弃/删除/导出/清空(原 main.js 连续区 64–1132)→ `chat.js`(1086 行)。**决策:合一个 chat.js 而非强拆 tasks.js+stream.js**——读完依赖图确认二者共享 `state.liveRuns` + `chat-stream` DOM + run 生命周期,且 live-run 助手(renderLiveRunIfVisible/ensureRunningTaskSubscribed 等)被 selectTask 和 SSE 机器两边调用、骑墙;强拆会制造双向各 ~4-5 个 import 且边界不自然(用户已确认选合一)。导出 `loadTaskList`/`loadModels`/`selectTask`,embed/files/newtask 对这三个的 import 从 `./main.js` 改指 `./chat.js`;`formatUploadProgress` 加 export(粘贴上传进度用)。**chat 不调 enterApp → 与 main 无环**。`main.js` 仅留 `enterApp`(编排)+ `loadStorage` + Esc 关栈 + boot = **75 行入口**,import 精简到 11 行(layout/markdown/media 不再被 main 直接引用,但经 chat 仍在依赖图、副作用照常)。**校验升级**:除 node 全检 + import/export 一致性,新增**从 main BFS 的模块可达性检查**(14/14 可达,确保副作用模块不掉出图)。dev.html 4087 行单文件 → 14 个零构建 ES module + 纯 HTML;main 2719→75。**路径 1(拆文件)完成**,后续可按需进路径 2(给 chat/files 等局部引 Alpine/petite-vue 响应式)。
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
from pathlib import Path
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from web.static_files import NoCacheStaticFiles
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
STATIC_DIR = ROOT / "web" / "static"
|
||||
|
|
@ -43,6 +49,19 @@ class StaticVendorTests(unittest.TestCase):
|
|||
self.assertIn("context_original_chars", src)
|
||||
self.assertIn("cache_hit_tokens", src)
|
||||
|
||||
def test_static_js_is_served_with_revalidation_header(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
static_dir = Path(tmp)
|
||||
(static_dir / "app.js").write_text("export const ok = true;\n", encoding="utf-8")
|
||||
app = FastAPI()
|
||||
app.mount("/static", NoCacheStaticFiles(directory=str(static_dir)), name="static")
|
||||
client = TestClient(app)
|
||||
|
||||
resp = client.get("/static/app.js")
|
||||
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertEqual(resp.headers.get("cache-control"), "no-cache")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ from uuid import UUID, uuid4
|
|||
from fastapi import Depends, FastAPI, File, Form, HTTPException, UploadFile
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import FileResponse, RedirectResponse, StreamingResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy import BigInteger, cast, func, select, update
|
||||
from starlette.background import BackgroundTask
|
||||
|
|
@ -51,6 +50,7 @@ from .auth import (
|
|||
)
|
||||
from .broker import broker
|
||||
from .sinks import WebEventSink
|
||||
from .static_files import NoCacheStaticFiles
|
||||
|
||||
|
||||
STATUS_FILTERS = ("active", "completed", "abandoned")
|
||||
|
|
@ -690,7 +690,7 @@ def create_app() -> FastAPI:
|
|||
# Windows 上 mimetypes 偶尔把 .js 判成 text/plain,会令 <script type="module"> 被浏览器拒执行;
|
||||
# 显式兜底,保证静态 ES module 以正确 MIME 下发。
|
||||
mimetypes.add_type("text/javascript", ".js")
|
||||
app.mount("/static", StaticFiles(directory=str(_STATIC_DIR)), name="static")
|
||||
app.mount("/static", NoCacheStaticFiles(directory=str(_STATIC_DIR)), name="static")
|
||||
|
||||
# ───────────── Misc ─────────────
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
"""Static file helpers for the Web SPA."""
|
||||
from __future__ import annotations
|
||||
|
||||
from starlette.responses import Response
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
|
||||
class NoCacheStaticFiles(StaticFiles):
|
||||
"""StaticFiles variant that asks browsers to revalidate SPA assets.
|
||||
|
||||
The Web UI is a module graph of small JS files. Without revalidation, users
|
||||
can keep an old chat.js after a deploy and miss frontend-only fixes.
|
||||
"""
|
||||
|
||||
def file_response(self, *args, **kwargs) -> Response:
|
||||
response = super().file_response(*args, **kwargs)
|
||||
response.headers["Cache-Control"] = "no-cache"
|
||||
return response
|
||||
Loading…
Reference in New Issue