UI: remove manual continue buttons for setup flows\n\nMotivations:\n- Enchaînement automatique des étapes sans interaction inutile\n- Éviter confusion pendant faucet/sync\n\nModifications:\n- Suppression des boutons dans wallet-setup.html et block-sync.html\n- Adaptation wallet-setup.ts pour redirection auto sans bouton\n\nPage affectées:\n- src/pages/wallet-setup/wallet-setup.html, src/pages/block-sync/block-sync.html, src/pages/wallet-setup/wallet-setup.ts

This commit is contained in:
NicolasCantu 2025-10-30 11:54:09 +01:00
parent bd1762ee0c
commit 44adae2d05
13 changed files with 70 additions and 81 deletions

View File

@ -4,3 +4,4 @@ alwaysApply: true
le site tourne sur le port 3004
l'url du site est https://dev3.4nkweb.com
ne déclanche jamais la CI

View File

@ -1,4 +1,5 @@
import { MessageType } from '../../models/process.model';
import { secureLogger } from '../../services/secure-logger';
export class IframePairingComponent {
private iframe: HTMLIFrameElement | null = null;
@ -19,7 +20,7 @@ export class IframePairingComponent {
switch (type) {
case 'IFRAME_READY':
console.log('🔗 Iframe pairing service is ready');
secureLogger.info('Iframe pairing service is ready', { component: 'IframePairingComponent' });
this.isReady = true;
break;
case MessageType.PAIRING_4WORDS_WORDS_GENERATED:
@ -57,7 +58,7 @@ export class IframePairingComponent {
`;
document.body.appendChild(this.iframe);
console.log('🔗 Hidden iframe created for pairing');
secureLogger.debug('Hidden iframe created for pairing', { component: 'IframePairingComponent' });
}
public async createPairing(): Promise<void> {
@ -93,7 +94,7 @@ export class IframePairingComponent {
}
private onWordsGenerated(data: any) {
console.log('✅ 4 words generated:', data.words);
secureLogger.info('4 words generated', { component: 'IframePairingComponent', hasWords: !!data.words });
// Emit custom event for the parent application
window.dispatchEvent(
new CustomEvent('pairing-words-generated', {
@ -103,7 +104,7 @@ export class IframePairingComponent {
}
private onStatusUpdate(data: any) {
console.log('📊 Pairing status update:', data.status);
secureLogger.debug('Pairing status update', { component: 'IframePairingComponent', status: data.status });
// Emit custom event for the parent application
window.dispatchEvent(
new CustomEvent('pairing-status-update', {
@ -113,7 +114,7 @@ export class IframePairingComponent {
}
private onPairingSuccess(data: any) {
console.log('✅ Pairing successful:', data.message);
secureLogger.info('Pairing successful', { component: 'IframePairingComponent', message: data.message });
// Emit custom event for the parent application
window.dispatchEvent(
new CustomEvent('pairing-success', {
@ -123,7 +124,7 @@ export class IframePairingComponent {
}
private onPairingError(data: any) {
console.error('❌ Pairing error:', data.error);
secureLogger.error('Pairing error', { component: 'IframePairingComponent', error: data.error });
// Emit custom event for the parent application
window.dispatchEvent(
new CustomEvent('pairing-error', {

View File

@ -33,12 +33,12 @@ export class SecureCredentialsComponent {
secureLogger.info('SecureCredentialsComponent initialized', {
component: 'SecureCredentialsComponent',
operation: 'init'
operation: 'init',
});
} catch (error) {
secureLogger.error('Failed to initialize SecureCredentialsComponent', error as Error, {
component: 'SecureCredentialsComponent',
operation: 'init'
operation: 'init',
});
}
}
@ -54,7 +54,7 @@ export class SecureCredentialsComponent {
} catch (error) {
secureLogger.error('Failed to load secure credentials HTML', error as Error, {
component: 'SecureCredentialsComponent',
operation: 'loadHTML'
operation: 'loadHTML',
});
}
}
@ -71,7 +71,7 @@ export class SecureCredentialsComponent {
} catch (error) {
secureLogger.error('Failed to load secure credentials CSS', error as Error, {
component: 'SecureCredentialsComponent',
operation: 'loadCSS'
operation: 'loadCSS',
});
}
}
@ -145,14 +145,13 @@ export class SecureCredentialsComponent {
// Émettre l'événement
eventBus.emit('credentials:created', { credentials });
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Erreur inconnue';
this.showMessage(`Erreur lors de la création des credentials: ${errorMessage}`, 'error');
secureLogger.error('Failed to create credentials', error as Error, {
component: 'SecureCredentialsComponent',
operation: 'handleCreateCredentials'
operation: 'handleCreateCredentials',
});
}
}
@ -180,14 +179,13 @@ export class SecureCredentialsComponent {
} else {
this.showMessage('Aucun credential trouvé ou mot de passe incorrect', 'error');
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Erreur inconnue';
this.showMessage(`Erreur lors de la récupération des credentials: ${errorMessage}`, 'error');
secureLogger.error('Failed to access credentials', error as Error, {
component: 'SecureCredentialsComponent',
operation: 'handleAccessCredentials'
operation: 'handleAccessCredentials',
});
}
}
@ -210,7 +208,8 @@ export class SecureCredentialsComponent {
}
// Simple password strength check
const score = password.length >= 12 ? (password.length >= 16 ? 5 : 4) : (password.length >= 8 ? 3 : 2);
const score =
password.length >= 12 ? (password.length >= 16 ? 5 : 4) : password.length >= 8 ? 3 : 2;
if (score < 3) {
strengthDiv.className += ' weak';
@ -233,7 +232,7 @@ export class SecureCredentialsComponent {
await this.updateUI();
this.showMessage('Credentials actualisés', 'success');
} catch {
this.showMessage('Erreur lors de l\'actualisation', 'error');
this.showMessage("Erreur lors de l'actualisation", 'error');
}
}
@ -241,26 +240,31 @@ export class SecureCredentialsComponent {
* Gère la suppression des credentials
*/
private async handleDeleteCredentials(): Promise<void> {
if (!confirm('Êtes-vous sûr de vouloir supprimer tous les credentials ? Cette action est irréversible.')) {
if (
!confirm(
'Êtes-vous sûr de vouloir supprimer tous les credentials ? Cette action est irréversible.'
)
) {
return;
}
try {
// TODO: Implement credentials deletion
console.log('Credentials deletion requested but not implemented');
secureLogger.warn('Credentials deletion requested but not implemented', {
component: 'SecureCredentials',
});
this.showMessage('Suppression des credentials non implémentée', 'warning');
await this.updateUI();
// Émettre l'événement
eventBus.emit('credentials:deleted');
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Erreur inconnue';
this.showMessage(`Erreur lors de la suppression: ${errorMessage}`, 'error');
secureLogger.error('Failed to delete credentials', error as Error, {
component: 'SecureCredentialsComponent',
operation: 'handleDeleteCredentials'
operation: 'handleDeleteCredentials',
});
}
}
@ -293,11 +297,10 @@ export class SecureCredentialsComponent {
statusElement.textContent = hasCredentials ? 'Disponibles' : 'Non disponibles';
statusElement.className = hasCredentials ? 'value success' : 'value error';
}
} catch (error) {
secureLogger.error('Failed to update UI', error as Error, {
component: 'SecureCredentialsComponent',
operation: 'updateUI'
operation: 'updateUI',
});
}
}
@ -331,7 +334,9 @@ export class SecureCredentialsComponent {
*/
private showMessage(message: string, type: 'success' | 'error' | 'warning' | 'info'): void {
const messagesContainer = document.getElementById('credentials-messages');
if (!messagesContainer) {return;}
if (!messagesContainer) {
return;
}
const messageDiv = document.createElement('div');
messageDiv.className = `message ${type}`;
@ -351,8 +356,7 @@ export class SecureCredentialsComponent {
* Gère l'événement de création de credentials
*/
private handleCredentialsCreated(data: any): void {
// Use data variable
console.log('Credentials created:', data);
secureLogger.info('Credentials created', { component: 'SecureCredentials' });
this.showMessage('Credentials créés avec succès !', 'success');
this.updateUI();
}
@ -378,7 +382,7 @@ export class SecureCredentialsComponent {
secureLogger.info('SecureCredentialsComponent destroyed', {
component: 'SecureCredentialsComponent',
operation: 'destroy'
operation: 'destroy',
});
}
}

View File

@ -187,9 +187,7 @@
</div>
</div>
<button id="continueBtn" class="continue-btn" disabled>
Aller au pairing
</button>
</div>
<script type="module" src="./block-sync.ts"></script>

View File

@ -2,6 +2,7 @@ import loginHtml from './home.html?raw';
import loginScript from './home.ts?raw';
import loginCss from '../../4nk.css?raw';
import { initHomePage } from './home';
import { secureLogger } from '../../services/secure-logger';
export class LoginComponent extends HTMLElement {
_callback: any;
@ -11,7 +12,7 @@ export class LoginComponent extends HTMLElement {
}
connectedCallback() {
console.log('CALLBACK LOGIN PAGE');
secureLogger.info('Login component connected', { component: 'LoginComponent' });
this.render();
setTimeout(() => {
initHomePage();
@ -22,7 +23,7 @@ export class LoginComponent extends HTMLElement {
if (typeof fn === 'function') {
this._callback = fn;
} else {
console.error('Callback is not a function');
secureLogger.error('Callback is not a function', { component: 'LoginComponent' });
}
}
@ -39,7 +40,7 @@ export class LoginComponent extends HTMLElement {
<script type="module">
${loginScript}
</scipt>
`;}
}
}

View File

@ -112,7 +112,7 @@
<div class="progress-bar" id="progressBar"></div>
</div>
<button class="continue-btn" id="continueBtn" disabled>Ajouter la date anniversaire du wallet</button>
</div>
<script type="module" src="./wallet-setup.ts"></script>

View File

@ -12,7 +12,7 @@ document.addEventListener('DOMContentLoaded', async () => {
const status = document.getElementById('status') as HTMLDivElement;
const progressBar = document.getElementById('progressBar') as HTMLDivElement;
const continueBtn = document.getElementById('continueBtn') as HTMLButtonElement;
const continueBtn = document.getElementById('continueBtn') as HTMLButtonElement | null;
function updateStatus(message: string, type: 'loading' | 'success' | 'error') {
status.textContent = message;
@ -84,12 +84,7 @@ document.addEventListener('DOMContentLoaded', async () => {
updateStatus('✅ Wallet existant trouvé', 'success');
updateProgress(100);
// Activer le bouton et rediriger
continueBtn.disabled = false;
continueBtn.textContent = 'Continuer';
continueBtn.onclick = () => {
window.location.href = '/src/pages/birthday-setup/birthday-setup.html';
};
// Redirection automatique sans bouton
// Auto-redirection après 2 secondes
setTimeout(() => {
@ -512,9 +507,7 @@ document.addEventListener('DOMContentLoaded', async () => {
secureLogger.info('🎉 Wallet setup completed successfully - wallet saved with birthday_waiting state', { component: 'WalletSetup' });
secureLogger.info('🔗 Ready to proceed to network connection and birthday setup', { component: 'WalletSetup' });
// Activer le bouton continuer
continueBtn.disabled = false;
secureLogger.info('✅ Continue button enabled', { component: 'WalletSetup' });
// Redirection automatique sans interaction utilisateur
// Redirection automatique après 3 secondes si l'utilisateur ne clique pas
setTimeout(() => {

View File

@ -131,17 +131,17 @@ export class WebAuthnService {
password: string,
mode: SecurityMode
): Promise<WebAuthnCredential> {
console.log('🔐 WebAuthnService.createCredentials called with mode:', mode);
secureLogger.debug('WebAuthnService.createCredentials called', { component: 'WebAuthn', mode });
// Vérifier la disponibilité de Proton Pass si c'est le mode sélectionné
if (mode === 'proton-pass') {
console.log('🔍 Checking Proton Pass availability...');
secureLogger.debug('Checking Proton Pass availability', { component: 'WebAuthn' });
const protonPassAvailable = await this.detectProtonPass();
if (!protonPassAvailable) {
console.log('❌ Proton Pass not available, falling back to platform authenticator');
secureLogger.debug('Proton Pass not available, falling back to platform authenticator', { component: 'WebAuthn' });
// Ne pas échouer, mais utiliser un mode de fallback
} else {
console.log('✅ Proton Pass is available and ready');
secureLogger.debug('Proton Pass is available and ready', { component: 'WebAuthn' });
}
}
@ -155,10 +155,10 @@ export class WebAuthnService {
// Configuration spécifique selon le mode
if (mode === 'proton-pass') {
authenticatorSelection.authenticatorAttachment = "platform";
console.log('🔐 Configuring for Proton Pass (platform authenticator)');
secureLogger.debug('Configuring for Proton Pass', { component: 'WebAuthn' });
} else if (mode === 'os') {
authenticatorSelection.authenticatorAttachment = "platform";
console.log('🔐 Configuring for OS authenticator (platform)');
secureLogger.debug('Configuring for OS authenticator', { component: 'WebAuthn' });
}
const publicKeyCredentialCreationOptions: PublicKeyCredentialCreationOptions = {
@ -186,24 +186,23 @@ export class WebAuthnService {
publicKeyCredentialCreationOptions.extensions = {
largeBlob: { support: "preferred" }
};
console.log('🔐 Added largeBlob extension for Proton Pass');
secureLogger.debug('Added largeBlob extension for Proton Pass', { component: 'WebAuthn' });
}
console.log('🔐 Calling navigator.credentials.create with options:', publicKeyCredentialCreationOptions);
console.log('🔐 This should trigger Proton Pass window...');
secureLogger.debug('Calling navigator.credentials.create', { component: 'WebAuthn' });
try {
const credential = await navigator.credentials.create({
publicKey: publicKeyCredentialCreationOptions
}) as PublicKeyCredential;
console.log('🔐 WebAuthn credential created successfully:', credential);
secureLogger.debug('WebAuthn credential created successfully', { component: 'WebAuthn' });
const response = credential.response as AuthenticatorAttestationResponse;
const credentialId = Array.from(new Uint8Array(credential.rawId)).map(b => b.toString(16).padStart(2, '0')).join('');
// TEST: Store credentialId in sessionStorage for testing
console.log('🔐 TEST: Storing credentialId in sessionStorage:', credentialId);
secureLogger.debug('Storing credentialId in sessionStorage', { component: 'WebAuthn' });
sessionStorage.setItem('webauthn_credential_id', credentialId);
return {
@ -211,7 +210,7 @@ export class WebAuthnService {
publicKey: Array.from(new Uint8Array(response.getPublicKey()!))
};
} catch (error) {
console.error('❌ WebAuthn credential creation failed:', error);
secureLogger.error('WebAuthn credential creation failed', error as Error, { component: 'WebAuthn' });
// Message d'erreur spécifique pour Proton Pass
if (mode === 'proton-pass') {
@ -236,7 +235,7 @@ export class WebAuthnService {
// Utiliser l'ID de la credential WebAuthn comme mot de passe pour chiffrer la clé PBKDF2
const encryptedKey = await encryptionService.encrypt(key, credential.id);
console.log('🔐 Key encrypted with WebAuthn credential');
secureLogger.debug('Key encrypted with WebAuthn credential', { component: 'WebAuthn' });
return encryptedKey;
} catch (error) {
secureLogger.error('Failed to encrypt key with WebAuthn', error as Error, {
@ -256,11 +255,8 @@ export class WebAuthnService {
*/
private async savePBKDF2Key(encryptedKey: string, credentialId: string, securityMode: SecurityMode): Promise<void> {
try {
// TEST: Log credentialId used for encryption
console.log('🔐 TEST: credentialId used for encryption:', credentialId);
const db = await this.openDatabase();
console.log(`🔍 Available stores in ${DATABASE_CONFIG.name}:`, Array.from(db.objectStoreNames));
secureLogger.debug(`Available stores in ${DATABASE_CONFIG.name}`, { component: 'WebAuthn', stores: Array.from(db.objectStoreNames) });
const transaction = db.transaction([DATABASE_CONFIG.stores.pbkdf2keys.name], 'readwrite');
const store = transaction.objectStore(DATABASE_CONFIG.stores.pbkdf2keys.name);
@ -272,7 +268,7 @@ export class WebAuthnService {
request.onerror = () => reject(request.error);
});
console.log(`🔐 PBKDF2 key stored with security mode: ${securityMode}`);
secureLogger.info(`PBKDF2 key stored with security mode: ${securityMode}`, { component: 'WebAuthn' });
} catch (error) {
secureLogger.error('Failed to save PBKDF2 key', error as Error, {
@ -301,7 +297,7 @@ export class WebAuthnService {
return result && typeof result === 'string';
} catch (error) {
console.log(`🔍 No PBKDF2 key found for security mode: ${securityMode}`);
secureLogger.debug(`No PBKDF2 key found for security mode: ${securityMode}`, { component: 'WebAuthn' });
return false;
}
}

View File

@ -83,18 +83,18 @@ export class DeviceReaderService {
const key = await secureCredentialsService.retrievePBKDF2Key(mode as any);
if (key) {
pbkdf2Key = key;
console.log(`✅ DeviceReaderService: PBKDF2 key found for mode: ${mode}`);
secureLogger.info(`PBKDF2 key found for mode: ${mode}`, { component: 'DeviceReader' });
break;
}
}
} catch (e) {
// Continue to next mode
console.log(`⚠️ DeviceReaderService: No PBKDF2 key for mode ${mode}`);
secureLogger.debug(`No PBKDF2 key for mode ${mode}`, { component: 'DeviceReader' });
}
}
if (!pbkdf2Key) {
console.error('❌ DeviceReaderService: Failed to retrieve PBKDF2 key for decryption');
secureLogger.error('Failed to retrieve PBKDF2 key for decryption', { component: 'DeviceReader' });
throw new Error('PBKDF2 key not found - cannot decrypt device');
}
@ -105,15 +105,15 @@ export class DeviceReaderService {
pbkdf2Key
);
const device: Device = JSON.parse(decryptedDeviceString);
console.log('✅ DeviceReaderService: Device decrypted successfully');
secureLogger.info('Device decrypted successfully', { component: 'DeviceReader' });
return device;
} else {
// Old plain format - return as is (should not happen in production)
console.warn('⚠️ DeviceReaderService: Device found in plain format (old format)');
secureLogger.warn('Device found in plain format (old format)', { component: 'DeviceReader' });
return dbRes as Device;
}
} catch (error) {
console.error('❌ DeviceReaderService: Error reading device:', error);
secureLogger.error('Error reading device:', error as Error, { component: 'DeviceReader' });
throw error;
}
}

View File

@ -72,8 +72,7 @@ export default class IframePairingService {
// Get the service instance to access the generated words
const service = await Services.getInstance();
const _device = service.dumpDeviceFromMemory();
// Use _device variable
console.log('Device from memory:', _device);
secureLogger.debug('Device from memory', { component: 'IframePairing' });
const creatorAddress = await service.getDeviceAddress();
if (creatorAddress) {
@ -91,7 +90,7 @@ export default class IframePairingService {
});
}
} catch (error) {
console.error('Error creating pairing:', error);
secureLogger.error('Error creating pairing:', error as Error, { component: 'IframePairing' });
this.sendMessage(MessageType.PAIRING_4WORDS_ERROR, {
error: (error as Error).message,
type: 'creator',
@ -101,7 +100,7 @@ export default class IframePairingService {
private async handleJoinPairing(data: { words: string }) {
try {
console.log('🔗 Joining pairing process via iframe with words:', data.words);
secureLogger.info('Joining pairing process via iframe', { component: 'IframePairing', hasWords: !!data.words });
this._isJoiner = true;
// Update status
@ -146,7 +145,7 @@ export default class IframePairingService {
});
}
} catch (error) {
console.error('Error joining pairing:', error);
secureLogger.error('Error joining pairing:', error as Error, { component: 'IframePairing' });
this.sendMessage(MessageType.PAIRING_4WORDS_ERROR, {
error: (error as Error).message,
type: 'joiner',

View File

@ -238,8 +238,6 @@ export class PairingService {
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
// Use errorMessage variable
console.log('Pairing error:', errorMessage);
secureLogger.error('Failed to confirm pairing', error as Error, {
component: 'PairingService',
@ -276,8 +274,6 @@ export class PairingService {
return { success: true, data: result };
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
// Use errorMessage variable
console.log('Pairing error:', errorMessage);
secureLogger.error('Failed to cancel pairing', error as Error, {
component: 'PairingService',
@ -350,8 +346,6 @@ export class PairingService {
return { success: true };
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
// Use errorMessage variable
console.log('Pairing error:', errorMessage);
secureLogger.error('Failed to delete secure credentials', error as Error, {
component: 'PairingService',

View File

@ -149,7 +149,7 @@ export class PerformanceMonitor {
getAllMetrics(): Record<string, PerformanceStats> {
const result: Record<string, PerformanceStats> = {};
this.metrics.forEach((values, name) => {
this.metrics.forEach((_values, name) => {
const stats = this.getMetricStats(name);
if (stats) {
result[name] = stats;

View File

@ -1,3 +1,5 @@
import { secureLogger } from '../services/secure-logger';
interface INotification {
id: number;
title: string;
@ -80,7 +82,7 @@ class NotificationStore {
`;
notifElement.onclick = () => {
// Chat functionality removed
console.warn('Chat functionality has been removed');
secureLogger.warn('Chat functionality has been removed', { component: 'NotificationStore' });
this.removeNotification(index);
};
board.appendChild(notifElement);