diff --git a/electron/main/index.ts b/electron/main/index.ts index ce217580..10d04579 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -652,6 +652,15 @@ app.whenReady().then(() => { // Set User-Agent for client session.defaultSession.webRequest.onBeforeSendHeaders((details, callback) => { details.requestHeaders["User-Agent"] = getUserAgent(); + // 数据 JSON(applist / categories / priority-config / sidebar-config 等)禁用客户端缓存。 + // nginx 默认不发 Cache-Control,Chromium 会按启发式 TTL(≈(now-LastModified)/10)复用陈旧副本, + // 导致服务端上新应用后商店内仍搜不到、且重启无效(仅删 Cache 目录才生效)。 + // 注意:C2 会给 URL 追加 ?_t=... 版本戳,故需按 query 前的 pathname 判断,而非 endsWith(".json")。 + const dataUrlPath = details.url.split("?")[0]; + if (dataUrlPath.endsWith(".json")) { + details.requestHeaders["Cache-Control"] = "no-cache"; + details.requestHeaders["Pragma"] = "no-cache"; + } callback({ cancel: false, requestHeaders: details.requestHeaders }); }); createWindow(); diff --git a/src/App.vue b/src/App.vue index 6b3da917..284bcc3f 100644 --- a/src/App.vue +++ b/src/App.vue @@ -429,6 +429,21 @@ const axiosInstance = axios.create({ timeout: 5000, // 增加到 5 秒,避免网络波动导致的超时 }); +// C2:数据 JSON(applist / categories / sidebar-config 等)追加 ?_t 版本戳, +// 使每次 URL 不同,穿透 CDN 边缘缓存,确保返回最新列表。 +// 与主进程 onBeforeSendHeaders 注入的 no-cache 互为兜底(C1 已对 Chromium 磁盘缓存生效)。 +axiosInstance.interceptors.request.use((config) => { + if (config.method?.toLowerCase() === "get" && typeof config.url === "string") { + // 按去除 query 的 pathname 判断,兼容 C2 自身追加的 ?_t= 及任何既有 query + const reqPath = config.url.split("?")[0]; + if (reqPath.endsWith(".json")) { + const sep = config.url.includes("?") ? "&" : "?"; + config.url = `${config.url}${sep}_t=${Date.now()}`; + } + } + return config; +}); + const fetchWithRetry = async ( url: string, signal?: AbortSignal, diff --git a/src/global/storeConfig.ts b/src/global/storeConfig.ts index ab76a17f..4768dcde 100644 --- a/src/global/storeConfig.ts +++ b/src/global/storeConfig.ts @@ -12,6 +12,20 @@ const priorityConfigAxios = axios.create({ timeout: 10000, }); +// C2:priority-config.json 走独立 axios 实例(不经过 axiosInstance), +// 同样追加 ?_t 版本戳以穿透 CDN 边缘缓存,与 C1 的 no-cache 注入互为兜底。 +priorityConfigAxios.interceptors.request.use((config) => { + if (config.method?.toLowerCase() === "get" && typeof config.url === "string") { + // 按去除 query 的 pathname 判断,兼容 C2 自身追加的 ?_t= 及任何既有 query + const reqPath = config.url.split("?")[0]; + if (reqPath.endsWith(".json")) { + const sep = config.url.includes("?") ? "&" : "?"; + config.url = `${config.url}${sep}_t=${Date.now()}`; + } + } + return config; +}); + export const APM_STORE_STATS_BASE_URL: string = import.meta.env.VITE_APM_STORE_STATS_BASE_URL || "";