Spaces:
Paused
Paused
icebear icebear0828 commited on
fix: add Cache-Control headers to prevent stale frontend after update (#79)
Browse filesindex.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>
- CHANGELOG.md +4 -0
- 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/*",
|
|
|
|
|
|
|
|
|
|
| 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/*",
|
|
|
|
|
|
|
|
|
|
| 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 {
|