mirror of
https://github.com/LifeArchiveProject/WeChatDataAnalysis.git
synced 2026-06-18 15:54:08 +08:00
Compare commits
1 Commits
@@ -7,6 +7,7 @@ const {
|
||||
globalShortcut,
|
||||
dialog,
|
||||
shell,
|
||||
session,
|
||||
} = require("electron");
|
||||
let autoUpdater = null;
|
||||
let autoUpdaterLoadError = null;
|
||||
@@ -465,6 +466,34 @@ function getDesktopSettingsPath() {
|
||||
return path.join(dir, "desktop-settings.json");
|
||||
}
|
||||
|
||||
function getPackagedUiDir() {
|
||||
if (!app.isPackaged) return null;
|
||||
try {
|
||||
return path.join(process.resourcesPath, "ui");
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function readPackagedUiBuildId() {
|
||||
const uiDir = getPackagedUiDir();
|
||||
if (!uiDir) return "";
|
||||
|
||||
try {
|
||||
const indexPath = path.join(uiDir, "index.html");
|
||||
if (!fs.existsSync(indexPath)) return "";
|
||||
const html = fs.readFileSync(indexPath, { encoding: "utf8" });
|
||||
const match =
|
||||
html.match(/buildId:"([^"]+)"/) ||
|
||||
html.match(/\/_payload\.json\?([^"'&<>\s]+)/) ||
|
||||
html.match(/data-src="\/_payload\.json\?([^"]+)"/);
|
||||
return String(match?.[1] || "").trim();
|
||||
} catch (err) {
|
||||
logMain(`[main] failed to read packaged UI build id: ${err?.message || err}`);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function loadDesktopSettings() {
|
||||
if (desktopSettings) return desktopSettings;
|
||||
|
||||
@@ -476,6 +505,9 @@ function loadDesktopSettings() {
|
||||
ignoredUpdateVersion: "",
|
||||
// Backend (FastAPI) listens on this port. Used in packaged builds.
|
||||
backendPort: DEFAULT_BACKEND_PORT,
|
||||
// Tracks the packaged UI build so we can invalidate Chromium's HTTP cache
|
||||
// after upgrades without wiping user data/localStorage.
|
||||
lastSeenUiBuildId: "",
|
||||
};
|
||||
|
||||
const p = getDesktopSettingsPath();
|
||||
@@ -539,6 +571,33 @@ function setIgnoredUpdateVersion(version) {
|
||||
return desktopSettings.ignoredUpdateVersion;
|
||||
}
|
||||
|
||||
async function refreshRendererCacheForPackagedUi() {
|
||||
if (!app.isPackaged) return;
|
||||
|
||||
const nextBuildId = readPackagedUiBuildId();
|
||||
if (!nextBuildId) return;
|
||||
|
||||
const prevBuildId = String(loadDesktopSettings()?.lastSeenUiBuildId || "").trim();
|
||||
if (prevBuildId === nextBuildId) return;
|
||||
|
||||
try {
|
||||
const ses = session?.defaultSession;
|
||||
if (ses) {
|
||||
await ses.clearCache();
|
||||
try {
|
||||
await ses.clearStorageData({ storages: ["serviceworkers"] });
|
||||
} catch {}
|
||||
}
|
||||
logMain(`[main] cleared renderer cache for UI build change: ${prevBuildId || "(none)"} -> ${nextBuildId}`);
|
||||
} catch (err) {
|
||||
logMain(`[main] failed to clear renderer cache for UI build change: ${err?.message || err}`);
|
||||
}
|
||||
|
||||
loadDesktopSettings();
|
||||
desktopSettings.lastSeenUiBuildId = nextBuildId;
|
||||
persistDesktopSettings();
|
||||
}
|
||||
|
||||
function parseEnvBool(value) {
|
||||
if (value == null) return null;
|
||||
const v = String(value).trim().toLowerCase();
|
||||
@@ -1614,6 +1673,7 @@ function registerWindowIpc() {
|
||||
|
||||
async function main() {
|
||||
await app.whenReady();
|
||||
await refreshRendererCacheForPackagedUi();
|
||||
Menu.setApplicationMenu(null);
|
||||
registerWindowIpc();
|
||||
registerDebugShortcuts();
|
||||
|
||||
@@ -99,9 +99,36 @@ class _SPAStaticFiles(StaticFiles):
|
||||
self._fallback_200 = Path(str(self.directory)) / "200.html"
|
||||
self._fallback_index = Path(str(self.directory)) / "index.html"
|
||||
|
||||
async def get_response(self, path: str, scope): # type: ignore[override]
|
||||
@staticmethod
|
||||
def _normalize_path(path: str) -> str:
|
||||
return str(path or "").strip().lstrip("/")
|
||||
|
||||
@classmethod
|
||||
def _is_shell_path(cls, path: str) -> bool:
|
||||
normalized = cls._normalize_path(path)
|
||||
return normalized in {"", "index.html", "200.html", "_payload.json"} or normalized.startswith(
|
||||
"_payload.json/"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _apply_cache_headers(cls, path: str, response):
|
||||
normalized = cls._normalize_path(path)
|
||||
try:
|
||||
return await super().get_response(path, scope)
|
||||
if cls._is_shell_path(normalized):
|
||||
response.headers["Cache-Control"] = "no-store, no-cache, must-revalidate"
|
||||
response.headers["Pragma"] = "no-cache"
|
||||
response.headers["Expires"] = "0"
|
||||
elif normalized.startswith("_nuxt/"):
|
||||
response.headers.setdefault("Cache-Control", "public, max-age=31536000, immutable")
|
||||
except Exception:
|
||||
pass
|
||||
return response
|
||||
|
||||
async def get_response(self, path: str, scope): # type: ignore[override]
|
||||
normalized = self._normalize_path(path)
|
||||
try:
|
||||
response = await super().get_response(path, scope)
|
||||
return self._apply_cache_headers(normalized, response)
|
||||
except StarletteHTTPException as exc:
|
||||
if exc.status_code != 404:
|
||||
raise
|
||||
@@ -112,8 +139,8 @@ class _SPAStaticFiles(StaticFiles):
|
||||
raise
|
||||
|
||||
if self._fallback_200.exists():
|
||||
return FileResponse(str(self._fallback_200))
|
||||
return FileResponse(str(self._fallback_index))
|
||||
return self._apply_cache_headers("200.html", FileResponse(str(self._fallback_200)))
|
||||
return self._apply_cache_headers("index.html", FileResponse(str(self._fallback_index)))
|
||||
|
||||
|
||||
def _maybe_mount_frontend() -> None:
|
||||
|
||||
Reference in New Issue
Block a user