mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-07 19:22:14 +09:00
fix(app): make web update detection survive flaky networks (#3135)
This commit is contained in:
@@ -6,6 +6,8 @@ import {
|
||||
shouldShowNativeDesktopUpdateDownloadProgress,
|
||||
shouldShowNativeDesktopUpdateInApp,
|
||||
} from '@app/features/app/utils/UpdaterPlatformUtils';
|
||||
import {WORKER_NAVIGATION_CACHE_PREFIX} from '@app/features/platform/service_worker/WorkerCacheCleanup';
|
||||
import {getProtectedCacheStorage} from '@app/features/platform/state/ProtectedWebStorage';
|
||||
import type {UpdaterContext, UpdaterDownloadOption, UpdaterEvent} from '@app/features/platform/types/Electron';
|
||||
import {Logger} from '@app/features/platform/utils/AppLogger';
|
||||
import {getClientInfo} from '@app/features/platform/utils/ClientInfo';
|
||||
@@ -49,9 +51,10 @@ export const DOWNLOADING_UPDATE_DESCRIPTOR = msg({
|
||||
});
|
||||
|
||||
const logger = new Logger('Updater');
|
||||
const CHECK_INTERVAL_MS = 30 * 60 * 1000;
|
||||
const CHECK_INTERVAL_MS = 5 * 60 * 1000;
|
||||
const MIN_CHECK_INTERVAL_MS = 60 * 1000;
|
||||
const MANUAL_DOWNLOAD_REFRESH_TIMEOUT_MS = 5 * 1000;
|
||||
const WEB_CHECK_TIMEOUT_MS = 15 * 1000;
|
||||
const VERSION_ENDPOINT = '/version.json';
|
||||
const CURRENT_BUILD_VERSION = Config.PUBLIC_BUILD_VERSION ?? null;
|
||||
const ALLOWED_WEB_UPDATE_HOSTS = new Set([
|
||||
@@ -61,6 +64,21 @@ const ALLOWED_WEB_UPDATE_HOSTS = new Set([
|
||||
'canary.fluxer.com',
|
||||
]);
|
||||
|
||||
async function dropCachedAppShell(): Promise<void> {
|
||||
const browserCaches = getProtectedCacheStorage();
|
||||
if (!browserCaches) return;
|
||||
try {
|
||||
const cacheNames = await browserCaches.keys();
|
||||
await Promise.all(
|
||||
cacheNames
|
||||
.filter((cacheName) => cacheName.startsWith(WORKER_NAVIGATION_CACHE_PREFIX))
|
||||
.map((cacheName) => browserCaches.delete(cacheName)),
|
||||
);
|
||||
} catch (error) {
|
||||
logger.warn('Failed to drop the cached app shell before reloading', error);
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeUpdaterContext(context: NativeUpdaterEvent['context']): UpdaterContext {
|
||||
switch (context) {
|
||||
case 'user':
|
||||
@@ -487,11 +505,9 @@ class Updater {
|
||||
shouldCheckNative ? this.checkNativeUpdate(checkContext) : Promise.resolve(null),
|
||||
this.checkWebUpdate(),
|
||||
]);
|
||||
this.transition({
|
||||
type: 'web.checked',
|
||||
available: webResult?.available ?? false,
|
||||
version: webResult?.version ?? null,
|
||||
});
|
||||
if (webResult) {
|
||||
this.transition({type: 'web.checked', available: webResult.available, version: webResult.version});
|
||||
}
|
||||
if (userInitiated && (!shouldCheckNative || (!this.isChecking && !this.nativeCheckFailed))) {
|
||||
this.showCurrentUpdateState();
|
||||
}
|
||||
@@ -521,7 +537,7 @@ class Updater {
|
||||
private async checkWebUpdate(): Promise<{
|
||||
available: boolean;
|
||||
version: string | null;
|
||||
}> {
|
||||
} | null> {
|
||||
if (!ALLOWED_WEB_UPDATE_HOSTS.has(window.location.host)) {
|
||||
return {available: false, version: null};
|
||||
}
|
||||
@@ -529,24 +545,28 @@ class Updater {
|
||||
const response = await fetch(VERSION_ENDPOINT, {
|
||||
cache: 'no-store',
|
||||
headers: {'Cache-Control': 'no-cache'},
|
||||
signal: AbortSignal.timeout(WEB_CHECK_TIMEOUT_MS),
|
||||
});
|
||||
if (!response.ok) {
|
||||
logger.debug('Version endpoint not available');
|
||||
return {available: false, version: null};
|
||||
return null;
|
||||
}
|
||||
const payload = (await response.json()) as {
|
||||
version?: string;
|
||||
buildVersion?: string;
|
||||
};
|
||||
const version = payload.version ?? payload.buildVersion ?? null;
|
||||
const updateAvailable = Boolean(version && CURRENT_BUILD_VERSION && version !== CURRENT_BUILD_VERSION);
|
||||
if (!version) {
|
||||
return null;
|
||||
}
|
||||
const updateAvailable = Boolean(CURRENT_BUILD_VERSION && version !== CURRENT_BUILD_VERSION);
|
||||
return {
|
||||
available: updateAvailable,
|
||||
version,
|
||||
};
|
||||
} catch (error) {
|
||||
logger.debug('Failed to fetch version info silently:', error);
|
||||
return {available: false, version: null};
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -586,6 +606,7 @@ class Updater {
|
||||
}
|
||||
if (this.updateInfo.web.available) {
|
||||
logger.info('Applying web update, reloading...');
|
||||
await dropCachedAppShell();
|
||||
window.location.reload();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,11 @@ import {
|
||||
precacheAssets,
|
||||
seedAppShell,
|
||||
} from '@app/features/platform/service_worker/WorkerAppShell';
|
||||
import {shouldDeleteWorkerCache, WORKER_CACHE_PREFIX} from '@app/features/platform/service_worker/WorkerCacheCleanup';
|
||||
import {
|
||||
shouldDeleteWorkerCache,
|
||||
WORKER_CACHE_PREFIX,
|
||||
WORKER_NAVIGATION_CACHE_PREFIX,
|
||||
} from '@app/features/platform/service_worker/WorkerCacheCleanup';
|
||||
import {getWorkerFetchRoute} from '@app/features/platform/service_worker/WorkerFetchRouting';
|
||||
import {
|
||||
getBadgeCount,
|
||||
@@ -45,7 +49,7 @@ const ensureServiceWorkerReady: Promise<void> = Promise.resolve();
|
||||
const SERVICE_WORKER_VERSION = typeof __FLUXER_SW_VERSION__ === 'string' ? __FLUXER_SW_VERSION__ : 'dev';
|
||||
const PRECACHE_MANIFEST = typeof __FLUXER_PRECACHE_MANIFEST__ === 'undefined' ? [] : __FLUXER_PRECACHE_MANIFEST__;
|
||||
const PRECACHE_CACHE = `${WORKER_CACHE_PREFIX}-precache-${SERVICE_WORKER_VERSION}`;
|
||||
const NAVIGATION_CACHE = `${WORKER_CACHE_PREFIX}-navigation-${SERVICE_WORKER_VERSION}`;
|
||||
const NAVIGATION_CACHE = `${WORKER_NAVIGATION_CACHE_PREFIX}${SERVICE_WORKER_VERSION}`;
|
||||
const EXPECTED_CACHES = new Set([PRECACHE_CACHE, NAVIGATION_CACHE]);
|
||||
const NAVIGATION_NETWORK_TIMEOUT_MS = 650;
|
||||
const serviceWorkerCaches = self.caches;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
export const WORKER_CACHE_PREFIX = 'fluxer';
|
||||
export const WORKER_NAVIGATION_CACHE_PREFIX = `${WORKER_CACHE_PREFIX}-navigation-`;
|
||||
|
||||
export function shouldDeleteWorkerCache(cacheName: string, expectedCaches: ReadonlySet<string>): boolean {
|
||||
return cacheName.startsWith(`${WORKER_CACHE_PREFIX}-`) && !expectedCaches.has(cacheName);
|
||||
|
||||
Reference in New Issue
Block a user