Add mobile Safari reload loop prevention for auth pages
All checks were successful
Build And Push Image / docker (push) Successful in 3m2s

- Implement comprehensive reload loop prevention utility
- Add initialization checks to setup-password, verify, and signup pages
- Include timeout protection and error handling for config loading
- Add fallback defaults to prevent page failures on mobile devices
- Document mobile reload loop prevention system
This commit is contained in:
2025-08-10 16:21:54 +02:00
parent 86977ca92a
commit 8b05fdd3d7
6 changed files with 846 additions and 24 deletions

View File

@@ -1,20 +1,29 @@
/**
* Config Cache Initialization Plugin
* Ensures config cache is properly initialized and prevents reload loops
* Specifically designed to fix Safari iOS issues
* Config Cache Initialization Plugin with Advanced Reload Loop Prevention
* Comprehensive mobile Safari reload loop prevention system
* Integrates with reload-loop-prevention utility for maximum protection
*/
import { initReloadLoopPrevention, applyMobileSafariReloadLoopFixes } from '~/utils/reload-loop-prevention';
export default defineNuxtPlugin({
name: 'config-cache-init',
enforce: 'pre', // Run before other plugins
async setup() {
console.log('[config-cache-init] Initializing config cache plugin');
console.log('[config-cache-init] Initializing comprehensive config cache and reload prevention plugin');
// Only run on client side
if (typeof window === 'undefined') {
return;
}
// Emergency reload loop prevention - check immediately
const canLoad = initReloadLoopPrevention('config-cache-init');
if (!canLoad) {
console.error('[config-cache-init] Plugin load blocked by reload loop prevention');
return;
}
// Initialize a flag to prevent multiple initializations
if ((window as any).__configCacheInitialized) {
console.log('[config-cache-init] Config cache already initialized');
@@ -24,6 +33,9 @@ export default defineNuxtPlugin({
// Mark as initialized
(window as any).__configCacheInitialized = true;
// Apply mobile Safari specific fixes first
applyMobileSafariReloadLoopFixes();
// Initialize the config cache structure if not already present
if (!(window as any).__configCache) {
(window as any).__configCache = {
@@ -43,18 +55,41 @@ export default defineNuxtPlugin({
console.log('[config-cache-init] Call history initialized');
}
// Add a global error handler to catch and prevent reload loops
// Enhanced error handler with reload loop detection
const originalError = window.onerror;
window.onerror = function(msg, url, lineNo, columnNo, error) {
// Check for common reload loop patterns
if (typeof msg === 'string' && (
msg.includes('Maximum call stack') ||
msg.includes('too much recursion') ||
msg.includes('RangeError')
)) {
console.error('[config-cache-init] Potential reload loop detected:', msg);
// Prevent default error handling which might cause reload
return true;
if (typeof msg === 'string') {
const isReloadLoop = (
msg.includes('Maximum call stack') ||
msg.includes('too much recursion') ||
msg.includes('RangeError') ||
msg.includes('Script error') ||
msg.includes('ResizeObserver loop limit exceeded') ||
msg.includes('Non-Error promise rejection captured')
);
if (isReloadLoop) {
console.error('[config-cache-init] Potential reload loop detected:', {
message: msg,
url,
lineNo,
columnNo,
userAgent: navigator.userAgent,
timestamp: new Date().toISOString()
});
// Record this as a potential reload trigger
initReloadLoopPrevention('error-handler');
// Prevent default error handling which might cause reload
return true;
}
// Check for config-related errors
if (msg.includes('config') || msg.includes('fetch') || msg.includes('load')) {
console.warn('[config-cache-init] Config-related error detected:', msg);
}
}
// Call original error handler if it exists
@@ -64,16 +99,97 @@ export default defineNuxtPlugin({
return false;
};
// Add unhandled rejection handler
// Enhanced unhandled rejection handler
window.addEventListener('unhandledrejection', (event) => {
if (event.reason?.message?.includes('config') ||
event.reason?.message?.includes('reload')) {
console.error('[config-cache-init] Unhandled config-related rejection:', event.reason);
const reason = event.reason;
const isConfigRelated = (
reason?.message?.includes('config') ||
reason?.message?.includes('reload') ||
reason?.message?.includes('fetch') ||
reason?.message?.includes('/api/')
);
if (isConfigRelated) {
console.error('[config-cache-init] Unhandled config-related rejection:', {
reason,
stack: reason?.stack,
timestamp: new Date().toISOString()
});
// Record potential reload trigger
initReloadLoopPrevention('promise-rejection');
// Prevent default which might cause reload
event.preventDefault();
}
});
console.log('[config-cache-init] Config cache plugin initialized successfully');
// Add performance monitoring for mobile Safari
if ('performance' in window && 'navigation' in window.performance) {
const navTiming = performance.navigation;
if (navTiming.type === 1) { // TYPE_RELOAD
console.warn('[config-cache-init] Page was reloaded - checking for reload loops');
const canContinue = initReloadLoopPrevention('page-reload');
if (!canContinue) {
return;
}
}
}
// Monitor for rapid API calls that could indicate loops
const originalFetch = window.fetch;
let apiCallCount = 0;
let apiCallWindow = Date.now();
window.fetch = function(input: RequestInfo | URL, init?: RequestInit) {
const now = Date.now();
// Reset counter every 5 seconds
if (now - apiCallWindow > 5000) {
apiCallCount = 0;
apiCallWindow = now;
}
apiCallCount++;
// Check for excessive API calls
if (apiCallCount > 10) {
const url = typeof input === 'string' ? input : input.toString();
console.warn(`[config-cache-init] Excessive API calls detected: ${apiCallCount} calls in 5s`, url);
// Check if this could be a config-related loop
if (url.includes('/api/recaptcha-config') || url.includes('/api/registration-config')) {
console.error('[config-cache-init] Config API loop detected!', url);
initReloadLoopPrevention('config-api-loop');
}
}
return originalFetch.call(this, input, init);
};
// Add visibility change handler for mobile Safari
document.addEventListener('visibilitychange', () => {
if (!document.hidden) {
// Page became visible - might be returning from background
console.log('[config-cache-init] Page visibility restored');
setTimeout(() => {
// Verify cache is still intact after visibility change
const cache = (window as any).__configCache;
if (!cache) {
console.warn('[config-cache-init] Config cache lost after visibility change - reinitializing');
(window as any).__configCache = {
recaptcha: null,
registration: null,
recaptchaLoading: false,
registrationLoading: false,
recaptchaError: null,
registrationError: null
};
}
}, 100);
}
});
console.log('[config-cache-init] Comprehensive config cache and reload prevention plugin initialized successfully');
}
});