diff --git a/apps/server/AliasVault.Client/nginx.conf b/apps/server/AliasVault.Client/nginx.conf
index bc4c4ca10..f240c5fb9 100644
--- a/apps/server/AliasVault.Client/nginx.conf
+++ b/apps/server/AliasVault.Client/nginx.conf
@@ -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;
diff --git a/apps/server/AliasVault.Client/wwwroot/index.template.html b/apps/server/AliasVault.Client/wwwroot/index.template.html
index 0f3e61ef1..95bee17b4 100644
--- a/apps/server/AliasVault.Client/wwwroot/index.template.html
+++ b/apps/server/AliasVault.Client/wwwroot/index.template.html
@@ -31,7 +31,7 @@
-
+
@@ -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 = {