icebear icebear0828 commited on
Commit
edb874f
·
unverified ·
1 Parent(s): 7594b26

fix: add Cache-Control headers to prevent stale frontend after update (#79)

Browse files

index.html served with no-cache so browsers always revalidate after
rebuild. /assets/* served with immutable (Vite content-hashed filenames).
Fixes #78 where users saw old model list after updating.

Co-authored-by: icebear0828 <icebear0828@users.noreply.github.com>

Files changed (2) hide show
  1. CHANGELOG.md +4 -0
  2. src/routes/web.ts +11 -3
CHANGELOG.md CHANGED
@@ -17,6 +17,10 @@
17
  - 模型列表自动同步:后端动态 fetch 成功后自动回写 `config/models.yaml`,静态配置不再滞后;前端每 60s 轮询模型列表,新模型无需刷新页面即可选择
18
  - Tuple Schema 支持:`prefixItems`(JSON Schema 2020-12 tuple)自动转换为等价 object schema 发给上游,响应侧还原为数组;OpenAI / Gemini / Responses 三端点统一支持
19
 
 
 
 
 
20
  ### Changed
21
 
22
  - Light mode 背景色从 `#f6f8f6` 改为纯白 `#ffffff`,增大亮/暗主题视觉差异
 
17
  - 模型列表自动同步:后端动态 fetch 成功后自动回写 `config/models.yaml`,静态配置不再滞后;前端每 60s 轮询模型列表,新模型无需刷新页面即可选择
18
  - Tuple Schema 支持:`prefixItems`(JSON Schema 2020-12 tuple)自动转换为等价 object schema 发给上游,响应侧还原为数组;OpenAI / Gemini / Responses 三端点统一支持
19
 
20
+ ### Fixed
21
+
22
+ - 前端缓存问题:`index.html` 设置 `Cache-Control: no-cache` 防止浏览器缓存旧页面,`/assets/*` 设置 immutable 长缓存(Vite content hash)
23
+
24
  ### Changed
25
 
26
  - Light mode 背景色从 `#f6f8f6` 改为纯白 `#ffffff`,增大亮/暗主题视觉差异
src/routes/web.ts CHANGED
@@ -28,12 +28,16 @@ export function createWebRoutes(accountPool: AccountPool): Hono {
28
  console.log(`[Web] publicDir: ${publicDir} (exists: ${hasWebUI})`);
29
  console.log(`[Web] desktopPublicDir: ${desktopPublicDir} (exists: ${hasDesktopUI})`);
30
 
31
- // Serve Vite build assets (web)
32
- app.use("/assets/*", serveStatic({ root: publicDir }));
 
 
 
33
 
34
  app.get("/", (c) => {
35
  try {
36
  const html = readFileSync(webIndexPath, "utf-8");
 
37
  return c.html(html);
38
  } catch (err) {
39
  const msg = err instanceof Error ? err.message : String(err);
@@ -44,13 +48,17 @@ export function createWebRoutes(accountPool: AccountPool): Hono {
44
 
45
  // Desktop UI — served at /desktop for Electron
46
  if (hasDesktopUI) {
47
- app.use("/desktop/assets/*", serveStatic({
 
 
 
48
  root: desktopPublicDir,
49
  rewriteRequestPath: (path) => path.replace(/^\/desktop/, ""),
50
  }));
51
 
52
  app.get("/desktop", (c) => {
53
  const html = readFileSync(desktopIndexPath, "utf-8");
 
54
  return c.html(html);
55
  });
56
  } else {
 
28
  console.log(`[Web] publicDir: ${publicDir} (exists: ${hasWebUI})`);
29
  console.log(`[Web] desktopPublicDir: ${desktopPublicDir} (exists: ${hasDesktopUI})`);
30
 
31
+ // Serve Vite build assets (web) — immutable cache (filenames contain content hash)
32
+ app.use("/assets/*", async (c, next) => {
33
+ c.header("Cache-Control", "public, max-age=31536000, immutable");
34
+ await next();
35
+ }, serveStatic({ root: publicDir }));
36
 
37
  app.get("/", (c) => {
38
  try {
39
  const html = readFileSync(webIndexPath, "utf-8");
40
+ c.header("Cache-Control", "no-cache");
41
  return c.html(html);
42
  } catch (err) {
43
  const msg = err instanceof Error ? err.message : String(err);
 
48
 
49
  // Desktop UI — served at /desktop for Electron
50
  if (hasDesktopUI) {
51
+ app.use("/desktop/assets/*", async (c, next) => {
52
+ c.header("Cache-Control", "public, max-age=31536000, immutable");
53
+ await next();
54
+ }, serveStatic({
55
  root: desktopPublicDir,
56
  rewriteRequestPath: (path) => path.replace(/^\/desktop/, ""),
57
  }));
58
 
59
  app.get("/desktop", (c) => {
60
  const html = readFileSync(desktopIndexPath, "utf-8");
61
+ c.header("Cache-Control", "no-cache");
62
  return c.html(html);
63
  });
64
  } else {