fix: add prerequisite checks and real block sync to block-sync page

**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)
This commit is contained in:
NicolasCantu 2025-10-29 15:21:19 +01:00
parent cd368cf667
commit b3af85d3a0
3 changed files with 367 additions and 142 deletions

View File

@ -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);

View File

@ -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<void>((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');
}
}
});

View File

@ -24,87 +24,149 @@ export async function initHomePage(): Promise<void> {
}
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<void> {
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 = `<span style="color: var(--error-color)">❌ Error: ${(error as Error).message}</span>`;
}
// 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;