From b3af85d3a0d8543b66529b5ef626b3f4345273a7 Mon Sep 17 00:00:00 2001 From: NicolasCantu Date: Wed, 29 Oct 2025 15:21:19 +0100 Subject: [PATCH] fix: add prerequisite checks and real block sync to block-sync page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Motivations :** - La page block-sync ne vérifie pas les prérequis comme les autres pages - La synchronisation est simulée au lieu d'être réelle - Il manque les vérifications de PBKDF2, wallet et birthday **Modifications :** - Ajout des vérifications de prérequis (PBKDF2 key, wallet, birthday) dans block-sync.ts - Remplacement de la synchronisation simulée par une synchronisation réelle via updateDeviceBlockHeight() - Ajout de la connexion aux relais si chain_tip n'est pas disponible - Ajout de vérifications de wallet avec retry pour gérer les problèmes de synchronisation - Ajout de la gestion d'erreur avec redirection vers les pages appropriées selon le type d'erreur - Amélioration de la gestion d'erreur avec messages clairs et redirections automatiques - Déplacement de l'écouteur du bouton continuer avant le try pour être toujours disponible - Ajout de vérifications de nullité pour les éléments DOM **Pages affectées :** - src/pages/block-sync/block-sync.ts (ajout des vérifications de prérequis et synchronisation réelle) - src/pages/home/home.ts (ajout des vérifications de prérequis pour la page de pairing) --- src/pages/birthday-setup/birthday-setup.ts | 4 +- src/pages/block-sync/block-sync.ts | 284 +++++++++++++++------ src/pages/home/home.ts | 221 +++++++++++----- 3 files changed, 367 insertions(+), 142 deletions(-) diff --git a/src/pages/birthday-setup/birthday-setup.ts b/src/pages/birthday-setup/birthday-setup.ts index 089db5e..0874bc6 100644 --- a/src/pages/birthday-setup/birthday-setup.ts +++ b/src/pages/birthday-setup/birthday-setup.ts @@ -110,7 +110,7 @@ document.addEventListener('DOMContentLoaded', async () => { // Connexion aux relais await services.connectAllRelays(); - + // Attendre que la hauteur de bloc soit définie via le handshake updateStatus('⏳ Attente de la synchronisation avec le réseau...', 'loading'); updateProgress(40); @@ -150,7 +150,7 @@ document.addEventListener('DOMContentLoaded', async () => { console.log('🔄 Calling updateDeviceBlockHeight()...'); await services.updateDeviceBlockHeight(); console.log('✅ updateDeviceBlockHeight() completed successfully'); - + // Vérifier que le birthday a bien été mis à jour en récupérant le wallet depuis la base updateStatus('🔍 Vérification de la mise à jour...', 'loading'); updateProgress(70); diff --git a/src/pages/block-sync/block-sync.ts b/src/pages/block-sync/block-sync.ts index 5b0b6d4..5bdae8f 100644 --- a/src/pages/block-sync/block-sync.ts +++ b/src/pages/block-sync/block-sync.ts @@ -1,35 +1,82 @@ document.addEventListener("DOMContentLoaded", async () => { console.log("🔄 Block sync page loaded"); - const status = document.getElementById("status"); - const progressBar = document.getElementById("progressBar"); - const continueBtn = document.getElementById("continueBtn"); - const currentBlockEl = document.getElementById("currentBlock"); - const birthdayEl = document.getElementById("birthday"); - const blocksToScanEl = document.getElementById("blocksToScan"); - const blocksScannedEl = document.getElementById("blocksScanned"); - const transactionsFoundEl = document.getElementById("transactionsFound"); + const status = document.getElementById("status") as HTMLElement; + const progressBar = document.getElementById("progressBar") as HTMLElement; + const continueBtn = document.getElementById("continueBtn") as HTMLButtonElement; function updateStatus(message: string, type: 'loading' | 'success' | 'error') { - status.textContent = message; - status.className = `status ${type}`; + if (status) { + status.textContent = message; + status.className = `status ${type}`; + } } function updateProgress(percentage: number) { - progressBar.style.width = `${percentage}%`; + if (progressBar) { + progressBar.style.width = `${percentage}%`; + } } function updateSyncItem(elementId: string, value: string, status: 'pending' | 'completed' | 'error' = 'pending') { const element = document.getElementById(elementId); - element.textContent = value; - element.className = `sync-status ${status}`; + if (element) { + element.textContent = value; + element.className = `sync-status ${status}`; + } + } + + // Gestion du bouton continuer (définie avant le try pour être toujours disponible) + if (continueBtn) { + continueBtn.addEventListener('click', async () => { + console.log('🏠 Redirecting to main application...'); + // Rediriger vers l'application principale + console.log('🔄 Block sync completed, checking storage state...'); + const { checkStorageStateAndNavigate } = await import('../../router'); + await checkStorageStateAndNavigate(); + }); } try { - // Étape 1: Initialisation des services - updateStatus('🔄 Initialisation des services...', 'loading'); + // Étape 1: Vérification des prérequis + updateStatus('🔍 Vérification des prérequis...', 'loading'); updateProgress(10); + // Vérifier que le PBKDF2 key existe d'abord (prérequis le plus basique) dans le store pbkdf2keys + const { SecureCredentialsService } = await import('../../services/secure-credentials.service'); + const secureCredentials = SecureCredentialsService.getInstance(); + + let pbkdf2KeyFound = false; + const securityModes = ['none', 'otp', 'password', 'os', 'proton-pass']; + for (const mode of securityModes) { + try { + const hasKey = await secureCredentials.hasPBKDF2Key(mode as any); + if (hasKey) { + const key = await secureCredentials.retrievePBKDF2Key(mode as any); + if (key) { + pbkdf2KeyFound = true; + console.log(`✅ PBKDF2 key found in pbkdf2keys store for security mode: ${mode}`); + break; + } + } + } catch (error) { + console.log(`⚠️ No PBKDF2 key found in pbkdf2keys store for mode ${mode}`); + } + } + + if (!pbkdf2KeyFound) { + console.log('⚠️ PBKDF2 key not found in pbkdf2keys store, redirecting to security-setup...'); + updateStatus('⚠️ Redirection vers la configuration de sécurité...', 'loading'); + setTimeout(() => { + window.location.href = '/src/pages/security-setup/security-setup.html'; + }, 1000); + return; + } + + // Étape 2: Initialisation des services + updateStatus('🔄 Initialisation des services...', 'loading'); + updateProgress(20); + const { default: Services } = await import('../../services/service'); if (!Services) { throw new Error('Services class not found in default export'); @@ -61,84 +108,173 @@ document.addEventListener("DOMContentLoaded", async () => { throw new Error('Services not initialized'); } - // Étape 2: Récupération des informations de synchronisation - updateStatus('📊 Récupération des informations de synchronisation...', 'loading'); - updateProgress(20); - - const currentBlockHeight = services.getCurrentBlockHeight(); - if (currentBlockHeight === -1) { - throw new Error('Block height not available'); - } - - // Récupérer le wallet pour obtenir la date anniversaire - const wallet = await services.getDeviceFromDatabase(); + // Vérifier que le wallet existe en base (avec plusieurs tentatives pour gérer les problèmes de synchronisation) + let wallet = await services.getDeviceFromDatabase(); if (!wallet) { - throw new Error('Wallet not found'); + console.log('⚠️ Wallet not found, waiting for database synchronization...'); + for (let attempt = 0; attempt < 5; attempt++) { + await new Promise(resolve => setTimeout(resolve, 500)); + wallet = await services.getDeviceFromDatabase(); + if (wallet) { + console.log(`✅ Wallet found after ${attempt + 1} attempts`); + break; + } + } + + if (!wallet) { + console.log('⚠️ Wallet still not found after retries, redirecting to wallet-setup...'); + updateStatus('⚠️ Redirection vers la configuration du wallet...', 'loading'); + setTimeout(() => { + window.location.href = '/src/pages/wallet-setup/wallet-setup.html'; + }, 1000); + return; + } } - const birthday = wallet.sp_wallet?.birthday || 0; - const blocksToScan = currentBlockHeight - birthday; + // Vérifier que le wallet contient bien les données attendues + if (wallet.sp_wallet && wallet.sp_wallet.birthday !== undefined) { + console.log('✅ Wallet found in database with birthday:', wallet.sp_wallet.birthday); + } else { + throw new Error('Wallet found but missing required data (sp_wallet or birthday)'); + } + + // Vérifier que le birthday est configuré (> 0) + if (!wallet.sp_wallet.birthday || wallet.sp_wallet.birthday === 0) { + console.log('⚠️ Birthday not configured (birthday = 0), redirecting to birthday-setup...'); + updateStatus('⚠️ Redirection vers la configuration de la date anniversaire...', 'loading'); + setTimeout(() => { + window.location.href = '/src/pages/birthday-setup/birthday-setup.html'; + }, 1000); + return; + } + + console.log('✅ All prerequisites verified for block sync'); + + // Étape 3: Connexion aux relais si nécessaire + updateStatus('🔗 Connexion aux relais...', 'loading'); + updateProgress(40); + + let currentBlockHeight = services.getCurrentBlockHeight(); + if (currentBlockHeight === -1 || currentBlockHeight === 0) { + console.log('⚠️ Block height not available, connecting to relays...'); + await services.connectAllRelays(); + + // Attendre que le handshake arrive et que chain_tip soit défini + await new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + reject(new Error('Timeout waiting for block height from handshake')); + }, 15000); // 15 secondes de timeout + + const checkBlockHeight = () => { + const blockHeight = services.getCurrentBlockHeight(); + if (blockHeight !== -1 && blockHeight > 0) { + console.log(`✅ Block height set from handshake: ${blockHeight}`); + currentBlockHeight = blockHeight; + clearTimeout(timeout); + resolve(); + } else { + setTimeout(checkBlockHeight, 100); + } + }; + + checkBlockHeight(); + }); + } + + // Étape 4: Récupération des informations de synchronisation + updateStatus('📊 Récupération des informations de synchronisation...', 'loading'); + updateProgress(50); + + const birthday = wallet.sp_wallet.birthday; + const lastScan = wallet.sp_wallet.last_scan || birthday; + const blocksToScan = currentBlockHeight - lastScan; // Mettre à jour l'interface avec les informations updateSyncItem('currentBlock', currentBlockHeight.toString(), 'completed'); updateSyncItem('birthday', birthday.toString(), 'completed'); updateSyncItem('blocksToScan', blocksToScan.toString(), 'pending'); - console.log(`📊 Sync info: current=${currentBlockHeight}, birthday=${birthday}, toScan=${blocksToScan}`); + console.log(`📊 Sync info: current=${currentBlockHeight}, birthday=${birthday}, lastScan=${lastScan}, toScan=${blocksToScan}`); - // Étape 3: Début de la synchronisation - updateStatus('🔍 Début de la synchronisation des blocs...', 'loading'); - updateProgress(30); + // Vérifier si une synchronisation est nécessaire + if (blocksToScan <= 0) { + console.log('✅ Wallet already synchronized'); + updateStatus('✅ Wallet déjà synchronisé!', 'success'); + updateProgress(100); + updateSyncItem('blocksScanned', lastScan.toString(), 'completed'); + updateSyncItem('blocksToScan', '0', 'completed'); + continueBtn.disabled = false; + return; + } - let blocksScanned = 0; - let transactionsFound = 0; + // Étape 5: Synchronisation réelle des blocs + updateStatus('🔍 Synchronisation des blocs en cours...', 'loading'); + updateProgress(60); - // Simuler la synchronisation des blocs - const scanInterval = setInterval(() => { - if (blocksScanned < blocksToScan) { - blocksScanned += Math.min(10, blocksToScan - blocksScanned); // Scanner par lots de 10 - const progress = 30 + (blocksScanned / blocksToScan) * 60; // 30% à 90% + console.log(`🔄 Starting real block scan from ${lastScan} to ${currentBlockHeight}...`); + + // Utiliser updateDeviceBlockHeight qui gère la synchronisation si nécessaire + // Cette méthode vérifie si last_scan < currentBlockHeight et synchronise si nécessaire + try { + await services.updateDeviceBlockHeight(); + console.log('✅ Block scan completed successfully'); + + // Vérifier que la mise à jour a été sauvegardée + const finalWallet = await services.getDeviceFromDatabase(); + if (finalWallet?.sp_wallet?.last_scan) { + const finalLastScan = finalWallet.sp_wallet.last_scan; + console.log('✅ Wallet updated with last_scan:', finalLastScan); - updateProgress(progress); - updateSyncItem('blocksScanned', blocksScanned.toString(), 'pending'); + // Finalisation + updateStatus('✅ Synchronisation terminée avec succès!', 'success'); + updateProgress(100); + updateSyncItem('blocksScanned', finalLastScan.toString(), 'completed'); + updateSyncItem('blocksToScan', blocksToScan.toString(), 'completed'); - // Simuler des transactions trouvées occasionnellement - if (Math.random() < 0.1) { // 10% de chance par bloc - transactionsFound += Math.floor(Math.random() * 3) + 1; - updateSyncItem('transactionsFound', transactionsFound.toString(), 'completed'); - } - - if (blocksScanned >= blocksToScan) { - clearInterval(scanInterval); - - // Finalisation - updateStatus('✅ Synchronisation terminée avec succès!', 'success'); - updateProgress(100); - updateSyncItem('blocksScanned', blocksScanned.toString(), 'completed'); - updateSyncItem('blocksToScan', blocksToScan.toString(), 'completed'); - - // Activer le bouton continuer + // Activer le bouton continuer + if (continueBtn) { continueBtn.disabled = false; - - console.log('🎉 Block sync completed successfully'); } + + console.log('🎉 Block sync completed successfully'); + } else { + throw new Error('Failed to verify wallet update - last_scan not found'); } - }, 100); // Mise à jour toutes les 100ms - - // Gestion du bouton continuer - continueBtn.addEventListener('click', async () => { - console.log('🏠 Redirecting to main application...'); - // Rediriger vers l'application principale - console.log('🔄 Block sync completed, checking storage state...'); - const { checkStorageStateAndNavigate } = await import('../../router'); - await checkStorageStateAndNavigate(); - }); + } catch (error) { + console.error('❌ Error during block scan:', error); + throw error; + } } catch (error) { console.error('❌ Error during block sync:', error); - updateStatus('❌ Erreur lors de la synchronisation des blocs', 'error'); - updateSyncItem('currentBlock', 'Erreur', 'error'); - updateSyncItem('birthday', 'Erreur', 'error'); - updateSyncItem('blocksToScan', 'Erreur', 'error'); + const errorMessage = (error as Error).message; + + updateStatus(`❌ Erreur: ${errorMessage}`, 'error'); + + // Si l'erreur est liée aux prérequis, rediriger vers la page appropriée + if (errorMessage.includes('PBKDF2') || errorMessage.includes('security')) { + console.log('⚠️ Security error detected, redirecting to security-setup...'); + updateStatus('⚠️ Redirection vers la configuration de sécurité...', 'loading'); + setTimeout(() => { + window.location.href = '/src/pages/security-setup/security-setup.html'; + }, 2000); + } else if (errorMessage.includes('wallet') || errorMessage.includes('device')) { + console.log('⚠️ Wallet error detected, redirecting to wallet-setup...'); + updateStatus('⚠️ Redirection vers la configuration du wallet...', 'loading'); + setTimeout(() => { + window.location.href = '/src/pages/wallet-setup/wallet-setup.html'; + }, 2000); + } else if (errorMessage.includes('birthday')) { + console.log('⚠️ Birthday error detected, redirecting to birthday-setup...'); + updateStatus('⚠️ Redirection vers la configuration de la date anniversaire...', 'loading'); + setTimeout(() => { + window.location.href = '/src/pages/birthday-setup/birthday-setup.html'; + }, 2000); + } else { + // Erreur générale, afficher et permettre de réessayer + updateSyncItem('currentBlock', 'Erreur', 'error'); + updateSyncItem('birthday', 'Erreur', 'error'); + updateSyncItem('blocksToScan', 'Erreur', 'error'); + } } }); diff --git a/src/pages/home/home.ts b/src/pages/home/home.ts index 1355ff0..825575e 100755 --- a/src/pages/home/home.ts +++ b/src/pages/home/home.ts @@ -24,87 +24,149 @@ export async function initHomePage(): Promise { } isInitializing = true; - console.log('INIT-HOME'); - - // No loading spinner - let the interface load naturally - - // Initialize iframe pairing, content menu, and communication only if in iframe - if (window.parent !== window) { - initIframePairing(); - initContentMenu(); - initIframeCommunication(); - } - - // Set up iframe pairing button listeners - setupIframePairingButtons(); - - // Set up main pairing interface (avec protection contre les appels multiples) - if (!isMainPairingSetup) { - setupMainPairing(); - } - - // Set up account actions - setupAccountActions(); - - const container = getCorrectDOM('login-4nk-component') as HTMLElement; - container.querySelectorAll('.tab').forEach(tab => { - addSubscription(tab, 'click', () => { - container.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); - tab.classList.add('active'); - - container - .querySelectorAll('.tab-content') - .forEach(content => content.classList.remove('active')); - container - .querySelector(`#${tab.getAttribute('data-tab') as string}`) - ?.classList.add('active'); - }); - }); + console.log('🏠 Home/Pairing page loaded'); + // Vérifier les prérequis en base de données + console.log('🔍 Verifying prerequisites...'); + try { console.log('🔧 Getting services instance...'); const service = await Services.getInstance(); + + // Vérifier que le PBKDF2 key existe d'abord (prérequis le plus basique) dans le store pbkdf2keys + const { SecureCredentialsService } = await import('../../services/secure-credentials.service'); + const secureCredentials = SecureCredentialsService.getInstance(); - // D'abord vérifier la sécurité avant de créer le wallet - console.log('🔐 Checking security configuration...'); + let pbkdf2KeyFound = false; + const securityModes = ['none', 'otp', 'password', 'os', 'proton-pass']; + for (const mode of securityModes) { + try { + // Vérifier d'abord silencieusement si une clé existe dans le store pbkdf2keys + const hasKey = await secureCredentials.hasPBKDF2Key(mode as any); + if (hasKey) { + // Si une clé existe, essayer de la récupérer + const key = await secureCredentials.retrievePBKDF2Key(mode as any); + if (key) { + pbkdf2KeyFound = true; + console.log(`✅ PBKDF2 key found in pbkdf2keys store for security mode: ${mode}`); + break; + } + } + } catch (error) { + // Continue to next mode + console.log(`⚠️ No PBKDF2 key found in pbkdf2keys store for mode ${mode}`); + } + } + + if (!pbkdf2KeyFound) { + console.log('⚠️ PBKDF2 key not found in pbkdf2keys store, redirecting to security-setup...'); + setTimeout(() => { + window.location.href = '/src/pages/security-setup/security-setup.html'; + }, 1000); + return; + } + + // Vérifier que le wallet existe en base (avec plusieurs tentatives pour gérer les problèmes de synchronisation) + let wallet = await service.getDeviceFromDatabase(); + if (!wallet) { + console.log('⚠️ Wallet not found, waiting for database synchronization...'); + // Attendre un peu pour la synchronisation de la base de données + for (let attempt = 0; attempt < 5; attempt++) { + await new Promise(resolve => setTimeout(resolve, 500)); + wallet = await service.getDeviceFromDatabase(); + if (wallet) { + console.log(`✅ Wallet found after ${attempt + 1} attempts`); + break; + } + } + + if (!wallet) { + console.log('⚠️ Wallet still not found after retries, redirecting to wallet-setup...'); + setTimeout(() => { + window.location.href = '/src/pages/wallet-setup/wallet-setup.html'; + }, 1000); + return; + } + } + + // Vérifier que le wallet contient bien les données attendues + if (wallet.sp_wallet && wallet.sp_wallet.birthday !== undefined) { + console.log('✅ Wallet found in database with birthday:', wallet.sp_wallet.birthday); + } else { + throw new Error('Wallet found but missing required data (sp_wallet or birthday)'); + } + + // Vérifier que le birthday est configuré (> 0) + if (!wallet.sp_wallet.birthday || wallet.sp_wallet.birthday === 0) { + console.log('⚠️ Birthday not configured (birthday = 0), redirecting to birthday-setup...'); + setTimeout(() => { + window.location.href = '/src/pages/birthday-setup/birthday-setup.html'; + }, 1000); + return; + } + + console.log('✅ All prerequisites verified for pairing page'); + + // No loading spinner - let the interface load naturally + + // Initialize iframe pairing, content menu, and communication only if in iframe + if (window.parent !== window) { + initIframePairing(); + initContentMenu(); + initIframeCommunication(); + } + + // Set up iframe pairing button listeners + setupIframePairingButtons(); + + // Set up main pairing interface (avec protection contre les appels multiples) + if (!isMainPairingSetup) { + setupMainPairing(); + } + + // Set up account actions + setupAccountActions(); + + const container = getCorrectDOM('login-4nk-component') as HTMLElement; + container.querySelectorAll('.tab').forEach(tab => { + addSubscription(tab, 'click', () => { + container.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); + tab.classList.add('active'); + + container + .querySelectorAll('.tab-content') + .forEach(content => content.classList.remove('active')); + container + .querySelector(`#${tab.getAttribute('data-tab') as string}`) + ?.classList.add('active'); + }); + }); + + // Les prérequis sont vérifiés, le wallet existe et le birthday est configuré + // Maintenant vérifier si les credentials de pairing existent + console.log('🔐 Checking pairing credentials...'); const { SecureCredentialsService } = await import('../../services/secure-credentials.service'); const secureCredentialsService = SecureCredentialsService.getInstance(); const hasCredentials = await secureCredentialsService.hasCredentials(); if (!hasCredentials) { - console.log('🔐 No security credentials found, user must configure security first...'); - // Afficher le sélecteur de mode de sécurité + console.log('🔐 No pairing credentials found, user must authenticate...'); + // Afficher le sélecteur de mode de sécurité si nécessaire ou déclencher l'authentification await handleMainPairing(); return; } - // Check if wallet exists, create if not - console.log('🔍 Checking for existing wallet...'); - const existingDevice = await service.getDeviceFromDatabase(); + // Wallet existe et credentials existent, procéder avec le pairing + console.log('✅ Wallet and credentials verified, proceeding with pairing...'); + console.log('🔍 Wallet details:', { + hasSpendKey: !!wallet.sp_wallet?.spend_key, + hasScanKey: !!wallet.sp_wallet?.scan_key, + birthday: wallet.sp_wallet?.birthday + }); - if (!existingDevice) { - console.log('📱 No wallet found, creating new device...'); - const spAddress = await service.createNewDevice(); - console.log('✅ New device created with address:', spAddress); - - // Verify wallet was created successfully - const verifyDevice = await service.getDeviceFromDatabase(); - if (!verifyDevice) { - throw new Error('Failed to create wallet - device not found after creation'); - } - console.log('✅ Wallet creation verified'); - } else { - console.log('📱 Existing wallet found'); - console.log('🔍 Wallet details:', { - hasSpendKey: !!existingDevice.sp_wallet?.spend_key, - hasScanKey: !!existingDevice.sp_wallet?.scan_key, - birthday: existingDevice.sp_wallet?.birthday - }); - } - - // Trigger WebAuthn authentication - console.log('🔐 Triggering WebAuthn authentication...'); + // Trigger WebAuthn authentication pour déchiffrer les credentials existants + console.log('🔐 Triggering WebAuthn authentication to decrypt credentials...'); await handleMainPairing(); // Attendre que les credentials soient réellement disponibles avant de continuer @@ -132,7 +194,34 @@ export async function initHomePage(): Promise { console.log('✅ Home page initialization completed'); } catch (error) { - console.error('❌ Error initializing home page:', error); + console.error('❌ Error initializing home/pairing page:', error); + + // Afficher un message d'erreur à l'utilisateur + const container = getCorrectDOM('login-4nk-component') as HTMLElement; + const mainStatus = container.querySelector('#main-status') as HTMLElement; + if (mainStatus) { + mainStatus.innerHTML = `❌ Error: ${(error as Error).message}`; + } + + // Si l'erreur est liée aux prérequis, rediriger vers la page appropriée + const errorMessage = (error as Error).message; + if (errorMessage.includes('PBKDF2') || errorMessage.includes('security')) { + console.log('⚠️ Security error detected, redirecting to security-setup...'); + setTimeout(() => { + window.location.href = '/src/pages/security-setup/security-setup.html'; + }, 2000); + } else if (errorMessage.includes('wallet') || errorMessage.includes('device')) { + console.log('⚠️ Wallet error detected, redirecting to wallet-setup...'); + setTimeout(() => { + window.location.href = '/src/pages/wallet-setup/wallet-setup.html'; + }, 2000); + } else if (errorMessage.includes('birthday')) { + console.log('⚠️ Birthday error detected, redirecting to birthday-setup...'); + setTimeout(() => { + window.location.href = '/src/pages/birthday-setup/birthday-setup.html'; + }, 2000); + } + throw error; } finally { isInitializing = false;