Add automatic reload to web app when detecting outdated local client side files (#1681)

This commit is contained in:
Leendert de Borst
2026-02-11 23:16:02 +01:00
committed by GitHub
parent 5589ca66e6
commit f4d3f5a6f3
2 changed files with 71 additions and 7 deletions

View File

@@ -33,8 +33,10 @@ http {
try_files $uri =404;
}
# Never cache index.html and appsettings.json
# Never cache index.html, appsettings.json, and Blazor framework bootstrap files
# These files must always be fresh to ensure users get the latest version
# The _framework/*.js and *.json files are critical for Blazor initialization and must not be cached
# to prevent issues when upgrading .NET versions (e.g., .NET 9 -> .NET 10)
location ~* ^/(index\.html|appsettings\.json)$ {
root /usr/share/nginx/html;
expires -1;
@@ -43,6 +45,16 @@ http {
try_files $uri =404;
}
# Never cache Blazor framework JavaScript and JSON files
# These files change between .NET versions and caching them causes boot failures
location ~* ^/_framework/.*\.(js|json)$ {
root /usr/share/nginx/html;
expires -1;
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
add_header Pragma "no-cache";
try_files $uri =404;
}
# Default location for all other files
location / {
root /usr/share/nginx/html;

View File

@@ -31,7 +31,7 @@
<link rel="manifest" href="manifest.json" />
<link rel="apple-touch-icon" sizes="500x500" href="img/icon-500.png" />
<link rel="apple-touch-icon" sizes="192x192" href="img/icon-192.png" />
<link rel="preload" href="/wasm/aliasvault_core_bg.wasm?v=@CacheBuster" as="fetch" type="application/wasm" crossorigin />
<link rel="prefetch" href="/wasm/aliasvault_core_bg.wasm?v=@CacheBuster" as="fetch" type="application/wasm" crossorigin />
<link rel="modulepreload" href="/wasm/aliasvault_core.js?v=@CacheBuster" />
</head>
@@ -284,20 +284,72 @@
const errorMessageElement = document.getElementById('error-message');
const showError = (message) => {
errorMessageElement.textContent = message;
errorMessageElement.classList.remove('hidden');
document.querySelector('.loading-progress-text').classList.add('hidden');
document.querySelector('svg.animate-spin').classList.add('hidden');
if (errorMessageElement) {
errorMessageElement.textContent = message;
errorMessageElement.classList.remove('hidden');
}
const progressText = document.querySelector('.loading-progress-text');
if (progressText) progressText.classList.add('hidden');
};
// Detect Blazor boot failures (e.g., after .NET version upgrades)
// This handles transitions between major .NET versions where boot format changes
let bootFailureHandled = false;
async function handleBlazorBootFailure() {
if (bootFailureHandled) return;
bootFailureHandled = true;
console.info('AliasVault: Blazor boot failure detected. Refreshing cached files...');
try {
// Clear service worker caches
if ('caches' in window) {
const cacheKeys = await caches.keys();
await Promise.all(cacheKeys.map(key => caches.delete(key)));
}
// Unregister service workers
if ('serviceWorker' in navigator) {
const registrations = await navigator.serviceWorker.getRegistrations();
await Promise.all(registrations.map(r => r.unregister()));
}
// Force browser to fetch fresh versions of critical files
// cache: 'reload' bypasses HTTP cache and updates it with fresh content
const criticalFiles = [
'/',
'/_framework/blazor.webassembly.js',
'/_framework/dotnet.js'
];
await Promise.all(criticalFiles.map(url =>
fetch(url, { cache: 'reload' }).catch(() => {})
));
} catch (e) {
console.error('AliasVault: Cache refresh failed:', e);
}
// Navigate to cache-busted URL to force complete reload with fresh resources
const url = new URL(window.location.href);
url.searchParams.set('_r', Date.now().toString());
window.location.replace(url.toString());
}
window.addEventListener('error', function(event) {
if (event.error && event.error.message && event.error.message.includes('WebAssembly')) {
showError(getTranslation('errors.webAssemblyError'));
}
});
window.addEventListener('unhandledrejection', function(event) {
if (event.reason && event.reason.message && event.reason.message.includes('WebAssembly')) {
const errorMessage = event.reason?.message || String(event.reason) || '';
if (errorMessage.includes('WebAssembly')) {
showError(getTranslation('errors.webAssemblyError'));
}
// Detect Blazor boot failures
else if (errorMessage.includes('blazor.boot.json') || errorMessage.includes('Failed to load config file') || errorMessage.includes('Failed to start platform')) {
event.preventDefault();
handleBlazorBootFailure();
}
});
window.addEventListener('load', manageLoadingScreen);
window.blazorCulture = {