ci: docker_tag=dev-test
Motivations: - Fix 502 en prod via build multi-pages et routes stables - Stabilize IndexedDB (création stores) et faucet obligatoire - Déploiement robuste: port 3004 garanti et logs Modifications: - Vite: inputs multi-pages, alias npm deploy:front - Router/redirects: chemins - Pages setup: URLs corrigées, suppression log faucet disabled - DB: bump version à 5, upgrade crée stores manquants - Script: scripts/deploy_front.sh (kill 3004, clean, build, start bg) - Lint: perf monitor param non utilisé, warnings corrigés Page affectées: - vite.config.ts, src/router.ts - src/pages/* (home, pairing, block-sync, wallet-setup, security-setup) - src/services/* (database-config, performance-monitor) - package.json, scripts/deploy_front.sh
This commit is contained in:
parent
25e0272959
commit
bd1762ee0c
@ -118,7 +118,6 @@ voir les fichiers README.md
|
|||||||
#### 🚀 Déploiement
|
#### 🚀 Déploiement
|
||||||
|
|
||||||
* **Préparation du déploiement :** Décrire et préparer le déploiement des correctifs et des évolutions.
|
* **Préparation du déploiement :** Décrire et préparer le déploiement des correctifs et des évolutions.
|
||||||
* **Script de déploiement :** le déploiment passe par `deploy-remote.sh`, ne masque pas la sortie (pas de 2>&1 pas exemple).
|
|
||||||
* **Bilan de déloploiement :** ne fait pas de bilan de déploiement.
|
* **Bilan de déloploiement :** ne fait pas de bilan de déploiement.
|
||||||
* **Lancement :** ne lance aucun déploiement sans demander avant
|
* **Lancement :** ne lance aucun déploiement sans demander avant
|
||||||
|
|
||||||
|
|||||||
360
.cursor/rules/project-analysis.mdc
Normal file
360
.cursor/rules/project-analysis.mdc
Normal file
@ -0,0 +1,360 @@
|
|||||||
|
---
|
||||||
|
alwaysApply: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# IHM_CLIENT - Analyse du Projet
|
||||||
|
|
||||||
|
## Vue d'ensemble
|
||||||
|
|
||||||
|
Application client Web5 pour l'écosystème 4NK permettant la gestion sécurisée des appareils, le pairing et les signatures de documents.
|
||||||
|
|
||||||
|
## Architecture Technique
|
||||||
|
|
||||||
|
### Stack Technologique
|
||||||
|
- **Frontend**: TypeScript, Vite, HTML5, CSS3
|
||||||
|
- **SDK**: Rust compilé en WebAssembly
|
||||||
|
- **Storage**: IndexedDB, Service Workers
|
||||||
|
- **Communication**: WebSockets, PostMessage API
|
||||||
|
- **Construction**: Vite avec plugins WASM et top-level await
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
- **Port**: 3004
|
||||||
|
- **URL**: https://dev3.4nkweb.com
|
||||||
|
- **Proxy**: `/storage` → https://dev3.4nkweb.com/storage
|
||||||
|
- **Base URL**: Variables d'environnement VITE_BASEURL, VITE_BOOTSTRAPURL, VITE_STORAGEURL, VITE_BLINDBITURL
|
||||||
|
|
||||||
|
### Structure du Projet
|
||||||
|
|
||||||
|
```
|
||||||
|
ihm_client_dev3/
|
||||||
|
├── src/
|
||||||
|
│ ├── components/ # Composants UI réutilisables
|
||||||
|
│ │ ├── account-nav/
|
||||||
|
│ │ ├── device-management/
|
||||||
|
│ │ ├── iframe-pairing/
|
||||||
|
│ │ ├── login-modal/
|
||||||
|
│ │ ├── secure-credentials/
|
||||||
|
│ │ ├── security-mode-selector/
|
||||||
|
│ │ └── validation-modal/
|
||||||
|
│ ├── pages/ # Pages de l'application
|
||||||
|
│ │ ├── account/
|
||||||
|
│ │ ├── birthday-setup/
|
||||||
|
│ │ ├── block-sync/
|
||||||
|
│ │ ├── home/
|
||||||
|
│ │ ├── pairing/
|
||||||
|
│ │ ├── security-setup/
|
||||||
|
│ │ └── wallet-setup/
|
||||||
|
│ ├── services/ # Services métier
|
||||||
|
│ │ ├── service.ts # Service principal (singleton)
|
||||||
|
│ │ ├── database.service.ts
|
||||||
|
│ │ ├── secure-credentials.service.ts
|
||||||
|
│ │ ├── security-mode.service.ts
|
||||||
|
│ │ ├── pairing.service.ts
|
||||||
|
│ │ ├── iframe-pairing.service.ts
|
||||||
|
│ │ └── websocket-manager.ts
|
||||||
|
│ ├── repositories/ # Accès aux données
|
||||||
|
│ │ ├── device.repository.ts
|
||||||
|
│ │ └── process.repository.ts
|
||||||
|
│ ├── models/ # Types et interfaces
|
||||||
|
│ ├── utils/ # Utilitaires
|
||||||
|
│ ├── service-workers/ # Workers pour opérations async
|
||||||
|
│ ├── router.ts # Router principal
|
||||||
|
│ └── main.ts # Point d'entrée
|
||||||
|
├── pkg/ # SDK WebAssembly compilé
|
||||||
|
├── docs/ # Documentation
|
||||||
|
├── IA_agents/ # Documentation pour IA
|
||||||
|
├── test-browser/ # Tests Playwright
|
||||||
|
└── logs/ # Logs centralisés
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture Fonctionnelle
|
||||||
|
|
||||||
|
### Flux d'Initialisation
|
||||||
|
|
||||||
|
1. **Security Setup** → Configuration du mode de sécurité
|
||||||
|
2. **Wallet Setup** → Création du wallet Bitcoin
|
||||||
|
3. **Birthday Setup** → Configuration de la date anniversaire
|
||||||
|
4. **Block Sync** → Synchronisation initiale des blocs
|
||||||
|
5. **Pairing** → Appairage de l'appareil
|
||||||
|
6. **Account** → Interface principale
|
||||||
|
|
||||||
|
### Système de Modes de Sécurité
|
||||||
|
|
||||||
|
| Mode | Nom | Niveau | Stockage Clé PBKDF2 |
|
||||||
|
|------|-----|--------|---------------------|
|
||||||
|
| `proton-pass` | Proton Pass | High | WebAuthn du navigateur |
|
||||||
|
| `os` | OS Authenticator | High | WebAuthn du système |
|
||||||
|
| `otp` | OTP | High | En clair |
|
||||||
|
| `password` | Mot de passe | Low | Chiffré avec mot de passe |
|
||||||
|
| `none` | Aucune | Critical | Clé en dur (déconseillé) |
|
||||||
|
|
||||||
|
### Base de Données IndexedDB
|
||||||
|
|
||||||
|
**Stores:**
|
||||||
|
- `pbkdf2keys` : Clés PBKDF2 chiffrées par mode de sécurité
|
||||||
|
- `wallet` : Wallet chiffré (device + wallet data)
|
||||||
|
- `credentials` : Credentials de pairing (après pairing)
|
||||||
|
- `env` : Variables d'environnement internes
|
||||||
|
- `processes` : Processus de communication
|
||||||
|
- `labels` : Labels de transactions
|
||||||
|
- `shared_secrets` : Secrets partagés
|
||||||
|
- `unconfirmed_secrets` : Secrets non confirmés
|
||||||
|
- `diffs` : Différences de synchronisation
|
||||||
|
- `data` : Données générales
|
||||||
|
|
||||||
|
### Système de Processus
|
||||||
|
|
||||||
|
Le système de processus est générique et réutilisable pour créer des "contrats" entre des membres avec niveaux d'accès différents.
|
||||||
|
|
||||||
|
**Concepts:**
|
||||||
|
- **Process**: Contrat décentralisé entre plusieurs membres
|
||||||
|
- **State**: État du processus avec données publiques/privées
|
||||||
|
- **Roles**: Définition des permissions par rôle
|
||||||
|
- **Members**: Participants identifiés par pairing process ID
|
||||||
|
|
||||||
|
**Données:**
|
||||||
|
- **Public**: Accessibles à tous, portées automatiquement
|
||||||
|
- **Private**: Chiffrées via PCD commitments, distribuées aux membres autorisés
|
||||||
|
- **Roles**: Quorum et champs accessibles par rôle
|
||||||
|
|
||||||
|
**Cycle de vie:**
|
||||||
|
1. Création → `createProcess()`
|
||||||
|
2. Mise à jour → `updateProcess()`
|
||||||
|
3. Synchronisation PRD → `createPrdUpdate()`
|
||||||
|
4. Validation → `approveChange()`
|
||||||
|
5. Commit blockchain → État immuable
|
||||||
|
6. Accès → `getPublicData()` / `decryptAttribute()`
|
||||||
|
|
||||||
|
### Système de Pairing
|
||||||
|
|
||||||
|
**But**: Créer une identité numérique vérifiable pour MFA entre appareils
|
||||||
|
|
||||||
|
**Caractéristiques:**
|
||||||
|
- Un wallet peut être appairé à plusieurs appareils
|
||||||
|
- Processus blockchain avec états commités
|
||||||
|
- Synchronisation via relais
|
||||||
|
- Contrôle via 4 mots
|
||||||
|
|
||||||
|
**Flux Créateur:**
|
||||||
|
1. `createPairingProcess()` avec son adresse
|
||||||
|
2. `generateQRCode()` pour le joiner
|
||||||
|
3. `waitForJoinerAndUpdateProcess()`
|
||||||
|
4. `waitForPairingCommitment()`
|
||||||
|
5. `confirmPairing()`
|
||||||
|
|
||||||
|
**Flux Joiner:**
|
||||||
|
1. `discoverAndJoinPairingProcess()` via QR code
|
||||||
|
2. `waitForPairingCommitment()`
|
||||||
|
3. `confirmPairing()`
|
||||||
|
|
||||||
|
## Services Principaux
|
||||||
|
|
||||||
|
### Services (Singleton)
|
||||||
|
|
||||||
|
**Initialisation:**
|
||||||
|
- WebAssembly SDK
|
||||||
|
- WebSocket connections
|
||||||
|
- Database restoration
|
||||||
|
- Process listening
|
||||||
|
|
||||||
|
**Principales méthodes:**
|
||||||
|
- `getDeviceFromDatabase()` : Récupération du device
|
||||||
|
- `createPairingProcess()` : Création de processus de pairing
|
||||||
|
- `createProcess()` : Création de processus générique
|
||||||
|
- `updateProcess()` : Mise à jour de processus
|
||||||
|
- `getProcess()` : Récupération de processus
|
||||||
|
- `checkConnections()` : Vérification des connexions entre membres
|
||||||
|
- `decryptAttribute()` : Déchiffrement d'attribut privé
|
||||||
|
- `getPublicData()` : Récupération des données publiques
|
||||||
|
|
||||||
|
### Database Service
|
||||||
|
|
||||||
|
**Gestion IndexedDB:**
|
||||||
|
- Singleton avec service worker
|
||||||
|
- Stores configurables via `storeDefinitions`
|
||||||
|
- Transactions asynchrones
|
||||||
|
- Cache management
|
||||||
|
|
||||||
|
### Secure Credentials Service
|
||||||
|
|
||||||
|
**Gestion des credentials:**
|
||||||
|
- Génération et stockage de credentials de pairing
|
||||||
|
- Récupération avec déchiffrement
|
||||||
|
- Support WebAuthn pour modes haute sécurité
|
||||||
|
|
||||||
|
### Security Mode Service
|
||||||
|
|
||||||
|
**Gestion des modes:**
|
||||||
|
- Détection du mode actuel
|
||||||
|
- Stockage dans IndexedDB
|
||||||
|
- Authentification selon mode
|
||||||
|
|
||||||
|
## Logging et Erreurs
|
||||||
|
|
||||||
|
### SecureLogger
|
||||||
|
|
||||||
|
**Système centralisé:**
|
||||||
|
- Niveaux: DEBUG, INFO, WARN, ERROR
|
||||||
|
- Sanitisation automatique des données sensibles
|
||||||
|
- Contexte enrichi avec composant et métadonnées
|
||||||
|
- Formatage cohérent
|
||||||
|
|
||||||
|
**Bonnes pratiques:**
|
||||||
|
- Utiliser `secureLogger` au lieu de `console.*`
|
||||||
|
- Toujours spécifier le composant
|
||||||
|
- Ajouter métadonnées utiles
|
||||||
|
- Messages clairs et concis
|
||||||
|
- Vérifications réelles avant logs de succès
|
||||||
|
|
||||||
|
**Patterns:**
|
||||||
|
- 🔍 DEBUG : Informations de débogage
|
||||||
|
- ✅ INFO : Succès et initialisations
|
||||||
|
- ⚠️ WARN : Avertissements non critiques
|
||||||
|
- ❌ ERROR : Erreurs critiques
|
||||||
|
|
||||||
|
## Router
|
||||||
|
|
||||||
|
### Navigation
|
||||||
|
|
||||||
|
**Logique de progression:**
|
||||||
|
1. Si pairing → account
|
||||||
|
2. Si date anniversaire → pairing
|
||||||
|
3. Si wallet → birthday-setup
|
||||||
|
4. Si pbkdf2 → wallet-setup
|
||||||
|
5. Sinon → security-setup
|
||||||
|
|
||||||
|
**Routes principales:**
|
||||||
|
- `/security-setup` : Configuration sécurité
|
||||||
|
- `/wallet-setup` : Création wallet
|
||||||
|
- `/birthday-setup` : Configuration birthday
|
||||||
|
- `/block-sync` : Synchronisation blocs
|
||||||
|
- `/pairing` : Appairage
|
||||||
|
- `/account` : Interface principale
|
||||||
|
- `/home` : Page d'accueil avec login
|
||||||
|
|
||||||
|
### Message Handlers
|
||||||
|
|
||||||
|
Gestion des messages PostMessage pour communication iframe:
|
||||||
|
- `REQUEST_LINK` : Liaison avec site externe
|
||||||
|
- `CREATE_PAIRING` : Création de pairing
|
||||||
|
- `GET_PROCESSES` : Récupération des processus
|
||||||
|
- `CREATE_PROCESS` : Création de processus
|
||||||
|
- `UPDATE_PROCESS` : Mise à jour de processus
|
||||||
|
- `VALIDATE_STATE` : Validation d'état
|
||||||
|
- Etc.
|
||||||
|
|
||||||
|
## WebSocket
|
||||||
|
|
||||||
|
**Gestion:**
|
||||||
|
- Connexions multiples (BOOTSTRAP, STORAGE, BLINDBIT)
|
||||||
|
- Handshake automatique
|
||||||
|
- Retry automatique en cas de déconnexion
|
||||||
|
- Event bus pour messages
|
||||||
|
|
||||||
|
**Messages:**
|
||||||
|
- HandshakeMessage : Synchronisation initiale
|
||||||
|
- NewTxMessage : Nouvelles transactions
|
||||||
|
- Process updates : Mises à jour de processus
|
||||||
|
|
||||||
|
## Dépendances
|
||||||
|
|
||||||
|
**Principales:**
|
||||||
|
- `axios` : Requêtes HTTP
|
||||||
|
- `jose` : JWT et chiffrement
|
||||||
|
- `jsonwebtoken` : Tokens JWT
|
||||||
|
- `pdf-lib` : Manipulation PDF
|
||||||
|
- `sweetalert2` : Modals UI
|
||||||
|
|
||||||
|
**SDK WebAssembly:**
|
||||||
|
- `pkg/sdk_client.js` : SDK principal
|
||||||
|
- `pkg/sdk_client_bg.wasm` : Binaire WebAssembly
|
||||||
|
- Types TypeScript générés
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
**Structure:**
|
||||||
|
- `test-browser/` : Tests Playwright
|
||||||
|
- Tests pour chaque parcours utilisateur
|
||||||
|
- Intégration avec mocked data
|
||||||
|
|
||||||
|
## User Stories
|
||||||
|
|
||||||
|
34 stories définies couvrant:
|
||||||
|
- Login (adresse device, QR code)
|
||||||
|
- Process management
|
||||||
|
- Account management
|
||||||
|
- Pairing
|
||||||
|
- Wallet
|
||||||
|
- Chat
|
||||||
|
- Signatures
|
||||||
|
|
||||||
|
## Configuration TypeScript
|
||||||
|
|
||||||
|
**Strict Mode:**
|
||||||
|
- `strict: true`
|
||||||
|
- `noImplicitAny: true`
|
||||||
|
- `noImplicitReturns: true`
|
||||||
|
- `forceConsistentCasingInFileNames: true`
|
||||||
|
|
||||||
|
**Modules:**
|
||||||
|
- `module: ESNext`
|
||||||
|
- `target: ESNext`
|
||||||
|
- `lib: ["DOM", "DOM.Iterable", "ESNext", "webworker"]`
|
||||||
|
- `isolatedModules: true`
|
||||||
|
|
||||||
|
## Points Critiques
|
||||||
|
|
||||||
|
### Sécurité
|
||||||
|
- Clés PBKDF2 toujours chiffrées (sauf mode OTP)
|
||||||
|
- Wallet toujours chiffré en base
|
||||||
|
- WebAuthn pour modes haute sécurité
|
||||||
|
- Sanitisation automatique des logs
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
- Cache désactivé (`maxCacheSize = 0`)
|
||||||
|
- Memory management avec retry
|
||||||
|
- Lazy loading des composants
|
||||||
|
- Service workers pour opérations async
|
||||||
|
|
||||||
|
### Robustesse
|
||||||
|
- Retry automatique pour opérations critiques
|
||||||
|
- Vérifications réelles avant logs de succès
|
||||||
|
- Fallback et navigation automatique
|
||||||
|
- Gestion des erreurs IndexedDB
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
**Fichiers clés:**
|
||||||
|
- `docs/INITIALIZATION_FLOW.md` : Flux d'initialisation détaillé
|
||||||
|
- `docs/PROCESS_SYSTEM_ARCHITECTURE.md` : Architecture des processus
|
||||||
|
- `docs/PAIRING_SYSTEM_ANALYSIS.md` : Analyse du pairing
|
||||||
|
- `docs/LOGGING_GUIDELINES.md` : Guide de logging
|
||||||
|
- `IA_agents/all.md` : Règles pour IA
|
||||||
|
- `README.md` : Vue d'ensemble
|
||||||
|
|
||||||
|
## Développement
|
||||||
|
|
||||||
|
**Scripts:**
|
||||||
|
- `npm run start` : Serveur de développement
|
||||||
|
- `npm run build` : Build de production
|
||||||
|
- `npm run quality` : Vérification qualité
|
||||||
|
- `npm run lint` : Linting
|
||||||
|
- `npm run type-check` : Vérification TypeScript
|
||||||
|
|
||||||
|
**Prérequis:**
|
||||||
|
- Node.js 18+
|
||||||
|
- Rust (pour SDK)
|
||||||
|
- npm ou yarn
|
||||||
|
|
||||||
|
## Points d'Attention
|
||||||
|
|
||||||
|
1. **Ordre des modes testés**: `['none', 'otp', 'password', 'os', 'proton-pass']`
|
||||||
|
2. **Store credentials**: Utilisé uniquement après pairing
|
||||||
|
3. **Redirection automatique**: 3s après création wallet vers birthday-setup
|
||||||
|
4. **Synchronisation IndexedDB**: Utilisation directe pour éviter problèmes service worker
|
||||||
|
5. **Retry automatique**: Jusqu'à 5 tentatives pour vérifications wallet
|
||||||
|
6. **Vérifications réelles**: Logs de succès uniquement après vérification
|
||||||
|
7. **WebAssembly memory**: Monitoring et cleanup automatique
|
||||||
|
8. **Singleton patterns**: Services, Database, ModalService
|
||||||
|
9. **Message handlers**: Tous nécessitent validation token
|
||||||
|
10. **Process lifecycle**: Toujours vérifier état committé avant manipulation
|
||||||
@ -1,26 +0,0 @@
|
|||||||
# Dependencies
|
|
||||||
node_modules/
|
|
||||||
dist/
|
|
||||||
pkg/
|
|
||||||
|
|
||||||
# Build outputs
|
|
||||||
*.js
|
|
||||||
*.d.ts
|
|
||||||
!eslint.config.js
|
|
||||||
|
|
||||||
# Config files
|
|
||||||
vite.config.ts
|
|
||||||
tsconfig.json
|
|
||||||
tsconfig.build.json
|
|
||||||
|
|
||||||
# Test files
|
|
||||||
**/*.test.ts
|
|
||||||
**/*.spec.ts
|
|
||||||
|
|
||||||
# Generated files
|
|
||||||
coverage/
|
|
||||||
.nyc_output/
|
|
||||||
|
|
||||||
# Logs
|
|
||||||
logs/
|
|
||||||
*.log
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,7 +2,6 @@
|
|||||||
# 🦀 Rust
|
# 🦀 Rust
|
||||||
# ----------------------------
|
# ----------------------------
|
||||||
target/
|
target/
|
||||||
pkg/
|
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
*.rs.bk
|
*.rs.bk
|
||||||
**/*.rlib
|
**/*.rlib
|
||||||
@ -30,7 +29,6 @@ pnpm-lock.yaml
|
|||||||
# ----------------------------
|
# ----------------------------
|
||||||
# 🧱 IDE / Éditeurs
|
# 🧱 IDE / Éditeurs
|
||||||
# ----------------------------
|
# ----------------------------
|
||||||
.vscode/
|
|
||||||
.idea/
|
.idea/
|
||||||
*.iml
|
*.iml
|
||||||
*.suo
|
*.suo
|
||||||
|
|||||||
8
.vscode/extensions.json
vendored
Normal file
8
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
|
"ms-vscode.vscode-typescript-next"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
59
.vscode/settings.json
vendored
Normal file
59
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
// ESLint Configuration
|
||||||
|
"eslint.enable": true,
|
||||||
|
"eslint.validate": [
|
||||||
|
"javascript",
|
||||||
|
"javascriptreact",
|
||||||
|
"typescript",
|
||||||
|
"typescriptreact"
|
||||||
|
],
|
||||||
|
"eslint.workingDirectories": ["."],
|
||||||
|
"eslint.options": {
|
||||||
|
"overrideConfigFile": "eslint.config.js"
|
||||||
|
},
|
||||||
|
|
||||||
|
// TypeScript Configuration
|
||||||
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
|
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||||
|
"typescript.tsserver.maxTsServerMemory": 4096,
|
||||||
|
|
||||||
|
// Problems Panel Configuration
|
||||||
|
"problems.showCurrentInStatus": true,
|
||||||
|
"problems.autoReveal": true,
|
||||||
|
|
||||||
|
// File Associations
|
||||||
|
"files.associations": {
|
||||||
|
"*.ts": "typescript",
|
||||||
|
"*.tsx": "typescriptreact"
|
||||||
|
},
|
||||||
|
|
||||||
|
// Editor Configuration
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll.eslint": "explicit",
|
||||||
|
"source.organizeImports": "explicit"
|
||||||
|
},
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
|
||||||
|
// TypeScript Specific
|
||||||
|
"typescript.suggest.autoImports": true,
|
||||||
|
"typescript.updateImportsOnFileMove.enabled": "always",
|
||||||
|
|
||||||
|
// Exclude patterns for file watcher
|
||||||
|
"files.watcherExclude": {
|
||||||
|
"**/node_modules/**": true,
|
||||||
|
"**/dist/**": true,
|
||||||
|
"**/pkg/**": true,
|
||||||
|
"**/logs/**": true,
|
||||||
|
"**/.git/**": true
|
||||||
|
},
|
||||||
|
|
||||||
|
// Search exclusion
|
||||||
|
"search.exclude": {
|
||||||
|
"**/node_modules": true,
|
||||||
|
"**/dist": true,
|
||||||
|
"**/pkg": true,
|
||||||
|
"**/logs": true,
|
||||||
|
"**/.git": true
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -94,7 +94,6 @@ voir les fichiers README.md
|
|||||||
#### 🚀 Déploiement
|
#### 🚀 Déploiement
|
||||||
|
|
||||||
* **Préparation du déploiement :** Décrire et préparer le déploiement des correctifs et des évolutions.
|
* **Préparation du déploiement :** Décrire et préparer le déploiement des correctifs et des évolutions.
|
||||||
* **Script de déploiement :** le déploiment passe par `deploy-remote.sh`, ne masque pas la sortie (pas de 2>&1 pas exemple).
|
|
||||||
* **Bilan de déloploiement :** ne fait pas de bilan de déploiement.
|
* **Bilan de déloploiement :** ne fait pas de bilan de déploiement.
|
||||||
* **Lancement :** ne lance aucun déploiement sans demander avant
|
* **Lancement :** ne lance aucun déploiement sans demander avant
|
||||||
|
|
||||||
|
|||||||
@ -95,7 +95,6 @@ voir docs/INITIALIZATION_FLOW.md
|
|||||||
#### 🚀 Déploiement
|
#### 🚀 Déploiement
|
||||||
|
|
||||||
* **Préparation du déploiement :** Décrire et préparer le déploiement des correctifs et des évolutions.
|
* **Préparation du déploiement :** Décrire et préparer le déploiement des correctifs et des évolutions.
|
||||||
* **Script de déploiement :** le déploiment passe par `deploy-remote.sh`, ne masque pas la sortie (pas de 2>&1 pas exemple).
|
|
||||||
* **Bilan de déloploiement :** ne fait pas de bilan de déploiement.
|
* **Bilan de déloploiement :** ne fait pas de bilan de déploiement.
|
||||||
* **Lancement :** ne lance aucun déploiement sans demander avant
|
* **Lancement :** ne lance aucun déploiement sans demander avant
|
||||||
|
|
||||||
|
|||||||
@ -8,11 +8,11 @@
|
|||||||
"build_wasm": "wasm-pack build --out-dir ../ihm_client_dev3/pkg ../sdk_client --target bundler --dev",
|
"build_wasm": "wasm-pack build --out-dir ../ihm_client_dev3/pkg ../sdk_client --target bundler --dev",
|
||||||
"start": "vite --host 0.0.0.0",
|
"start": "vite --host 0.0.0.0",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
"deploy": "sudo cp -r dist/* /var/www/html/",
|
"deploy:front": "./scripts/deploy_front.sh",
|
||||||
"prettify": "prettier --config ./.prettierrc --write \"src/**/*{.ts,.html,.css,.js}\"",
|
"prettify": "prettier --config ./.prettierrc --write \"src/**/*{.ts,.html,.css,.js}\"",
|
||||||
"build:dist": "tsc -p tsconfig.build.json",
|
"build:dist": "tsc -p tsconfig.build.json",
|
||||||
"lint": "eslint src/ --ext .ts,.tsx --fix",
|
"lint": "eslint src/ --fix",
|
||||||
"lint:check": "eslint src/ --ext .ts,.tsx",
|
"lint:check": "eslint src/",
|
||||||
"type-check": "tsc --noEmit",
|
"type-check": "tsc --noEmit",
|
||||||
"quality": "npm run prettify",
|
"quality": "npm run prettify",
|
||||||
"quality:strict": "npm run type-check && npm run lint:check && npm run prettify",
|
"quality:strict": "npm run type-check && npm run lint:check && npm run prettify",
|
||||||
|
|||||||
68
scripts/deploy_front.sh
Executable file
68
scripts/deploy_front.sh
Executable file
@ -0,0 +1,68 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
LOG_DIR="$PROJECT_ROOT/logs"
|
||||||
|
PID_FILE="$LOG_DIR/ihm_client_dev3.front.pid"
|
||||||
|
LOG_FILE="$LOG_DIR/ihm_client_dev3.front.log"
|
||||||
|
PORT=3004
|
||||||
|
|
||||||
|
mkdir -p "$LOG_DIR"
|
||||||
|
|
||||||
|
echo "[deploy] Ensuring nothing listens on :$PORT..."
|
||||||
|
if ss -ltnp | grep -q ":$PORT"; then
|
||||||
|
# Extract PID(s) for the port
|
||||||
|
PIDS=$(ss -ltnp | awk -v p=":$PORT" '$0 ~ p {print $NF}' | sed -E 's/.*pid=([0-9]+).*/\1/' | sort -u)
|
||||||
|
for PID in $PIDS; do
|
||||||
|
if [[ "$PID" =~ ^[0-9]+$ ]]; then
|
||||||
|
echo "[deploy] Killing PID $PID on port $PORT"
|
||||||
|
kill -TERM "$PID" || true
|
||||||
|
# Wait up to 10s for process to exit
|
||||||
|
for i in {1..10}; do
|
||||||
|
if ! ps -p "$PID" >/dev/null 2>&1; then break; fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
if ps -p "$PID" >/dev/null 2>&1; then
|
||||||
|
echo "[deploy] Force killing PID $PID"
|
||||||
|
kill -KILL "$PID" || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[deploy] Cleaning Vite caches and previous dist..."
|
||||||
|
rm -rf "$PROJECT_ROOT/node_modules/.vite" "$PROJECT_ROOT/.vite" "$PROJECT_ROOT/dist"
|
||||||
|
|
||||||
|
echo "[deploy] Building production bundle..."
|
||||||
|
cd "$PROJECT_ROOT"
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
echo "[deploy] Starting Vite dev server on :$PORT (non-bloquant) ..."
|
||||||
|
if [[ -f "$PID_FILE" ]]; then
|
||||||
|
# Clean stale PID file if any
|
||||||
|
OLD_PID=$(cat "$PID_FILE" || true)
|
||||||
|
if [[ -n "${OLD_PID}" ]] && ps -p "$OLD_PID" >/dev/null 2>&1; then
|
||||||
|
echo "[deploy] Previous PID $OLD_PID still running, terminating"
|
||||||
|
kill -TERM "$OLD_PID" || true
|
||||||
|
fi
|
||||||
|
rm -f "$PID_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
nohup npm run start >"$LOG_FILE" 2>&1 &
|
||||||
|
NEW_PID=$!
|
||||||
|
echo "$NEW_PID" > "$PID_FILE"
|
||||||
|
|
||||||
|
echo "[deploy] Launched PID $NEW_PID. Tail logs: tail -f $LOG_FILE"
|
||||||
|
|
||||||
|
echo "[deploy] Verifying port $PORT availability..."
|
||||||
|
for i in {1..10}; do
|
||||||
|
if ss -ltnp | grep -q ":$PORT"; then
|
||||||
|
echo "[deploy] OK: port $PORT is listening."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "[deploy] ERROR: port $PORT not listening after start. Check $LOG_FILE"
|
||||||
|
exit 1
|
||||||
@ -232,7 +232,7 @@ export class SecureCredentialsComponent {
|
|||||||
try {
|
try {
|
||||||
await this.updateUI();
|
await this.updateUI();
|
||||||
this.showMessage('Credentials actualisés', 'success');
|
this.showMessage('Credentials actualisés', 'success');
|
||||||
} catch (_error) {
|
} catch {
|
||||||
this.showMessage('Erreur lors de l\'actualisation', 'error');
|
this.showMessage('Erreur lors de l\'actualisation', 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,12 +17,12 @@ export interface SecurityModeConfig {
|
|||||||
export class SecurityModeSelector {
|
export class SecurityModeSelector {
|
||||||
private container: HTMLElement;
|
private container: HTMLElement;
|
||||||
private selectedMode: SecurityMode | null = null;
|
private selectedMode: SecurityMode | null = null;
|
||||||
private onModeSelected: (_mode: SecurityMode) => void;
|
private onModeSelected: (mode: SecurityMode) => void;
|
||||||
private onCancel: () => void;
|
private onCancel: () => void;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
container: HTMLElement,
|
container: HTMLElement,
|
||||||
onModeSelected: (_mode: SecurityMode) => void,
|
onModeSelected: (mode: SecurityMode) => void,
|
||||||
onCancel: () => void
|
onCancel: () => void
|
||||||
) {
|
) {
|
||||||
this.container = container;
|
this.container = container;
|
||||||
|
|||||||
@ -22,6 +22,7 @@ export interface INotification {
|
|||||||
path?: string;
|
path?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* eslint-disable no-unused-vars */
|
||||||
export enum MessageType {
|
export enum MessageType {
|
||||||
// Establish connection and keep alive
|
// Establish connection and keep alive
|
||||||
LISTENING = 'LISTENING',
|
LISTENING = 'LISTENING',
|
||||||
@ -75,3 +76,4 @@ export enum MessageType {
|
|||||||
PAIRING_4WORDS_SUCCESS = 'PAIRING_4WORDS_SUCCESS',
|
PAIRING_4WORDS_SUCCESS = 'PAIRING_4WORDS_SUCCESS',
|
||||||
PAIRING_4WORDS_ERROR = 'PAIRING_4WORDS_ERROR',
|
PAIRING_4WORDS_ERROR = 'PAIRING_4WORDS_ERROR',
|
||||||
}
|
}
|
||||||
|
/* eslint-enable no-unused-vars */
|
||||||
|
|||||||
@ -5,201 +5,237 @@
|
|||||||
|
|
||||||
import { checkPBKDF2Key, checkWalletWithRetries } from '../../utils/prerequisites.utils';
|
import { checkPBKDF2Key, checkWalletWithRetries } from '../../utils/prerequisites.utils';
|
||||||
|
|
||||||
import { secureLogger } from '../services/secure-logger';
|
import { secureLogger } from '../../services/secure-logger';
|
||||||
let isInitializing = false;
|
let isInitializing = false;
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
// Type definition for update functions - parameters are template names
|
||||||
if (isInitializing) {
|
/* eslint-disable no-unused-vars */
|
||||||
secureLogger.warn('⚠️ Birthday setup page already initializing, skipping...', { component: 'BirthdaySetup' });
|
interface UpdateFunctions {
|
||||||
return;
|
updateStatus: (message: string, type: 'loading' | 'success' | 'error') => void;
|
||||||
}
|
updateProgress: (percent: number) => void;
|
||||||
|
}
|
||||||
|
/* eslint-enable no-unused-vars */
|
||||||
|
|
||||||
isInitializing = true;
|
async function initializeServices() {
|
||||||
secureLogger.info('🎂 Birthday setup page loaded', { component: 'BirthdaySetup' });
|
secureLogger.info('🔄 Importing services...', { component: 'BirthdaySetup' });
|
||||||
secureLogger.debug(`Current URL: ${window.location.href}`, { component: 'BirthdaySetup' });
|
const serviceModule = await import('../../services/service');
|
||||||
secureLogger.debug(`Referrer: ${document.referrer}`, { component: 'BirthdaySetup' });
|
secureLogger.debug(`Service module imported: ${Object.keys(serviceModule)}`, {
|
||||||
|
component: 'BirthdaySetup',
|
||||||
|
});
|
||||||
|
|
||||||
const status = document.getElementById('status') as HTMLDivElement;
|
const Services = serviceModule.default;
|
||||||
const progressBar = document.getElementById('progressBar') as HTMLDivElement;
|
if (!Services) {
|
||||||
const continueBtn = document.getElementById('continueBtn') as HTMLButtonElement;
|
throw new Error('Services class not found in default export');
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Étape 1: Connexion aux relais
|
const services = await Services.getInstance();
|
||||||
updateStatus('🌐 Connexion aux relais...', 'loading');
|
secureLogger.info('✅ Services instance obtained successfully', {
|
||||||
updateProgress(20);
|
component: 'BirthdaySetup',
|
||||||
|
|
||||||
try {
|
|
||||||
secureLogger.info('🔄 Importing services...', { component: 'BirthdaySetup' });
|
|
||||||
const serviceModule = await import('../../services/service');
|
|
||||||
secureLogger.debug(`Service module imported: ${Object.keys(serviceModule)}`, { component: 'BirthdaySetup' });
|
|
||||||
|
|
||||||
// La classe Services est exportée par défaut
|
|
||||||
const Services = serviceModule.default;
|
|
||||||
|
|
||||||
if (!Services) {
|
|
||||||
throw new Error('Services class not found in default export');
|
|
||||||
}
|
|
||||||
secureLogger.info('🔄 Getting existing services instance...', { component: 'BirthdaySetup' });
|
|
||||||
|
|
||||||
// Utiliser l'instance existante des services avec gestion des erreurs de mémoire
|
|
||||||
let services;
|
|
||||||
try {
|
|
||||||
services = await Services.getInstance();
|
|
||||||
secureLogger.info('✅ Services instance obtained successfully', { component: 'BirthdaySetup' });
|
|
||||||
} catch (error) {
|
|
||||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
||||||
if (errorMessage.includes('Out of memory') || errorMessage.includes('insufficient memory')) {
|
|
||||||
secureLogger.error('🚫 Memory error detected', { component: 'BirthdaySetup' });
|
|
||||||
updateStatus('❌ Erreur: Mémoire insuffisante. Veuillez actualiser la page.', 'error');
|
|
||||||
throw new Error('WebAssembly initialization failed due to insufficient memory. Please refresh the page.');
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vérifier les prérequis en base de données
|
|
||||||
updateStatus('🔍 Vérification des prérequis...', 'loading');
|
|
||||||
updateProgress(20);
|
|
||||||
|
|
||||||
// Vérifier que le PBKDF2 key existe d'abord (prérequis le plus basique)
|
|
||||||
const pbkdf2KeyResult = await checkPBKDF2Key();
|
|
||||||
if (!pbkdf2KeyResult) {
|
|
||||||
secureLogger.warn('⚠️ PBKDF2 key not found in pbkdf2keys store, redirecting to security-setup...', { component: 'BirthdaySetup' });
|
|
||||||
updateStatus('⚠️ Redirection vers la configuration de sécurité...', 'loading');
|
|
||||||
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)
|
|
||||||
const wallet = await checkWalletWithRetries();
|
|
||||||
if (!wallet) {
|
|
||||||
secureLogger.warn('⚠️ Wallet still not found after retries, redirecting to wallet-setup...', { component: 'BirthdaySetup' });
|
|
||||||
updateStatus('⚠️ Redirection vers la configuration du wallet...', 'loading');
|
|
||||||
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?.birthday !== undefined) {
|
|
||||||
secureLogger.info(`Wallet found in database with birthday: ${wallet.sp_wallet.birthday}`, { component: 'BirthdaySetup' });
|
|
||||||
} else {
|
|
||||||
throw new Error('Wallet found but missing required data (sp_wallet or birthday)');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vérifier que tous les prérequis sont remplis
|
|
||||||
const prerequisitesOk = wallet && pbkdf2KeyResult;
|
|
||||||
if (prerequisitesOk) {
|
|
||||||
secureLogger.info('✅ All prerequisites verified', { component: 'BirthdaySetup' });
|
|
||||||
} else {
|
|
||||||
throw new Error('Prerequisites verification failed');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
secureLogger.info('✅ Block height set from handshake: ${blockHeight}', { component: 'BirthdaySetup' });
|
|
||||||
clearTimeout(timeout);
|
|
||||||
resolve();
|
|
||||||
} else {
|
|
||||||
setTimeout(checkBlockHeight, 100);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
checkBlockHeight();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Vérifier que les relais sont connectés et que le handshake a été reçu
|
|
||||||
const currentBlockHeight = services.getCurrentBlockHeight();
|
|
||||||
if (currentBlockHeight !== -1 && currentBlockHeight > 0) {
|
|
||||||
secureLogger.info(`Relays connected successfully, chain_tip: ${currentBlockHeight}`, { component: 'BirthdaySetup' });
|
|
||||||
secureLogger.info(`Communication handshake completed, chain_tip: ${currentBlockHeight}`, { component: 'BirthdaySetup' });
|
|
||||||
} else {
|
|
||||||
throw new Error('Handshake not received or chain_tip not set');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mettre à jour la date anniversaire du wallet
|
|
||||||
updateStatus('🎂 Mise à jour de la date anniversaire...', 'loading');
|
|
||||||
updateProgress(60);
|
|
||||||
secureLogger.info('🔄 Calling updateDeviceBlockHeight()...', { component: 'BirthdaySetup' });
|
|
||||||
await services.updateDeviceBlockHeight();
|
|
||||||
secureLogger.info('✅ updateDeviceBlockHeight() completed successfully', { component: 'BirthdaySetup' });
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
secureLogger.info('🔄 Verifying birthday update...', { component: 'BirthdaySetup' });
|
|
||||||
const updatedWallet = await services.getDeviceFromDatabase();
|
|
||||||
if (updatedWallet?.sp_wallet?.birthday && updatedWallet.sp_wallet.birthday > 0) {
|
|
||||||
secureLogger.info(`Birthday updated successfully: ${updatedWallet.sp_wallet.birthday}`, { component: 'BirthdaySetup' });
|
|
||||||
} else {
|
|
||||||
secureLogger.error('Birthday update verification failed', new Error('Verification failed'), {
|
|
||||||
component: 'BirthdaySetup',
|
|
||||||
data: {
|
|
||||||
birthday: updatedWallet?.sp_wallet?.birthday,
|
|
||||||
hasSpWallet: !!updatedWallet?.sp_wallet
|
|
||||||
}
|
|
||||||
});
|
|
||||||
throw new Error(`Birthday update verification failed: expected birthday > 0, got ${updatedWallet?.sp_wallet?.birthday}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Redirection vers la page de synchronisation des blocs
|
|
||||||
updateStatus('🔄 Redirection vers la synchronisation des blocs...', 'loading');
|
|
||||||
updateProgress(100);
|
|
||||||
|
|
||||||
secureLogger.info('🎉 Birthday setup completed successfully - redirecting to block sync', { component: 'BirthdaySetup' });
|
|
||||||
secureLogger.info('📍 Target URL: /src/pages/block-sync/block-sync.html', { component: 'BirthdaySetup' });
|
|
||||||
secureLogger.info('⏰ Redirecting in 1 second...', { component: 'BirthdaySetup' });
|
|
||||||
|
|
||||||
// Rediriger vers la page de synchronisation des blocs
|
|
||||||
setTimeout(() => {
|
|
||||||
secureLogger.info('🔄 Executing redirect now...', { component: 'BirthdaySetup' });
|
|
||||||
window.location.href = '/src/pages/block-sync/block-sync.html';
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
secureLogger.error('Services not available', error as Error, { component: 'BirthdaySetup' });
|
|
||||||
updateStatus('❌ Erreur: Services non disponibles', 'error');
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
secureLogger.error('Error during birthday setup', error as Error, { component: 'BirthdaySetup' });
|
|
||||||
updateStatus('❌ Erreur lors de la configuration de la date anniversaire', 'error');
|
|
||||||
} finally {
|
|
||||||
isInitializing = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gestion du bouton continuer
|
|
||||||
continueBtn.addEventListener('click', async () => {
|
|
||||||
secureLogger.info('🏠 Redirecting to main application...', { component: 'BirthdaySetup' });
|
|
||||||
// Rediriger vers l'application principale
|
|
||||||
secureLogger.debug('🎂 Birthday setup completed, checking storage state...', { component: 'BirthdaySetup' });
|
|
||||||
const { checkStorageStateAndNavigate } = await import('../../router');
|
|
||||||
await checkStorageStateAndNavigate();
|
|
||||||
});
|
});
|
||||||
|
return services;
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||||
|
if (errorMessage.includes('Out of memory') || errorMessage.includes('insufficient memory')) {
|
||||||
|
secureLogger.error('🚫 Memory error detected', { component: 'BirthdaySetup' });
|
||||||
|
throw new Error(
|
||||||
|
'WebAssembly initialization failed due to insufficient memory. Please refresh the page.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function updateStatus(message: string, type: 'loading' | 'success' | 'error') {
|
function redirectToSetup(page: string, updateFunctions: UpdateFunctions): void {
|
||||||
status.textContent = message;
|
secureLogger.warn(`Redirecting to ${page}...`, { component: 'BirthdaySetup' });
|
||||||
status.className = `status ${type}`;
|
updateFunctions.updateStatus(`⚠️ Redirection vers ${page}...`, 'loading');
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = `/src/pages/${page}/${page}.html`;
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function verifyPrerequisites(updateFunctions: UpdateFunctions) {
|
||||||
|
const pbkdf2KeyResult = await checkPBKDF2Key();
|
||||||
|
if (!pbkdf2KeyResult) {
|
||||||
|
redirectToSetup('security-setup', updateFunctions);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wallet = await checkWalletWithRetries();
|
||||||
|
if (!wallet) {
|
||||||
|
redirectToSetup('wallet-setup', updateFunctions);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wallet.sp_wallet?.birthday === undefined) {
|
||||||
|
throw new Error('Wallet found but missing required data (sp_wallet or birthday)');
|
||||||
|
}
|
||||||
|
|
||||||
|
secureLogger.info(`Wallet found in database with birthday: ${wallet.sp_wallet.birthday}`, {
|
||||||
|
component: 'BirthdaySetup',
|
||||||
|
});
|
||||||
|
|
||||||
|
return { wallet, pbkdf2KeyResult };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function waitForBlockHeight(services: any, updateFunctions: UpdateFunctions) {
|
||||||
|
updateFunctions.updateStatus('⏳ Attente de la synchronisation avec le réseau...', 'loading');
|
||||||
|
updateFunctions.updateProgress(40);
|
||||||
|
|
||||||
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
reject(new Error('Timeout waiting for block height from handshake'));
|
||||||
|
}, 15000);
|
||||||
|
|
||||||
|
const checkBlockHeight = () => {
|
||||||
|
const blockHeight = services.getCurrentBlockHeight();
|
||||||
|
if (blockHeight !== -1 && blockHeight > 0) {
|
||||||
|
secureLogger.info(`✅ Block height set from handshake: ${blockHeight}`, {
|
||||||
|
component: 'BirthdaySetup',
|
||||||
|
});
|
||||||
|
clearTimeout(timeout);
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
setTimeout(checkBlockHeight, 100);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
checkBlockHeight();
|
||||||
|
});
|
||||||
|
|
||||||
|
const currentBlockHeight = services.getCurrentBlockHeight();
|
||||||
|
if (currentBlockHeight !== -1 && currentBlockHeight > 0) {
|
||||||
|
secureLogger.info(`Relays connected successfully, chain_tip: ${currentBlockHeight}`, {
|
||||||
|
component: 'BirthdaySetup',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new Error('Handshake not received or chain_tip not set');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateBirthday(services: any, updateFunctions: UpdateFunctions) {
|
||||||
|
updateFunctions.updateStatus('🎂 Mise à jour de la date anniversaire...', 'loading');
|
||||||
|
updateFunctions.updateProgress(60);
|
||||||
|
|
||||||
|
secureLogger.info('🔄 Calling updateDeviceBlockHeight()...', { component: 'BirthdaySetup' });
|
||||||
|
await services.updateDeviceBlockHeight();
|
||||||
|
secureLogger.info('✅ updateDeviceBlockHeight() completed successfully', {
|
||||||
|
component: 'BirthdaySetup',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function verifyBirthdayUpdate(services: any, updateFunctions: UpdateFunctions) {
|
||||||
|
updateFunctions.updateStatus('🔍 Vérification de la mise à jour...', 'loading');
|
||||||
|
updateFunctions.updateProgress(70);
|
||||||
|
|
||||||
|
secureLogger.info('🔄 Verifying birthday update...', { component: 'BirthdaySetup' });
|
||||||
|
const updatedWallet = await services.getDeviceFromDatabase();
|
||||||
|
|
||||||
|
if (updatedWallet?.sp_wallet?.birthday && updatedWallet.sp_wallet.birthday > 0) {
|
||||||
|
secureLogger.info(`Birthday updated successfully: ${updatedWallet.sp_wallet.birthday}`, {
|
||||||
|
component: 'BirthdaySetup',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
secureLogger.error('Birthday update verification failed', new Error('Verification failed'), {
|
||||||
|
component: 'BirthdaySetup',
|
||||||
|
data: {
|
||||||
|
birthday: updatedWallet?.sp_wallet?.birthday,
|
||||||
|
hasSpWallet: !!updatedWallet?.sp_wallet,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
throw new Error(
|
||||||
|
`Birthday update verification failed: expected birthday > 0, got ${updatedWallet?.sp_wallet?.birthday}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function redirectToBlockSync(updateFunctions: UpdateFunctions) {
|
||||||
|
updateFunctions.updateStatus('🔄 Redirection vers la synchronisation des blocs...', 'loading');
|
||||||
|
updateFunctions.updateProgress(100);
|
||||||
|
|
||||||
|
secureLogger.info('🎉 Birthday setup completed successfully - redirecting to block sync', {
|
||||||
|
component: 'BirthdaySetup',
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
secureLogger.info('🔄 Executing redirect now...', { component: 'BirthdaySetup' });
|
||||||
|
window.location.href = '/src/pages/block-sync/block-sync.html';
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
|
if (isInitializing) {
|
||||||
|
secureLogger.warn('⚠️ Birthday setup page already initializing, skipping...', {
|
||||||
|
component: 'BirthdaySetup',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isInitializing = true;
|
||||||
|
secureLogger.info('🎂 Birthday setup page loaded', { component: 'BirthdaySetup' });
|
||||||
|
secureLogger.debug(`Current URL: ${window.location.href}`, { component: 'BirthdaySetup' });
|
||||||
|
secureLogger.debug(`Referrer: ${document.referrer}`, { component: 'BirthdaySetup' });
|
||||||
|
|
||||||
|
const status = document.getElementById('status') as HTMLDivElement;
|
||||||
|
const progressBar = document.getElementById('progressBar') as HTMLDivElement;
|
||||||
|
const continueBtn = document.getElementById('continueBtn') as HTMLButtonElement;
|
||||||
|
|
||||||
|
function updateStatus(message: string, type: 'loading' | 'success' | 'error') {
|
||||||
|
status.textContent = message;
|
||||||
|
status.className = `status ${type}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateProgress(percent: number) {
|
||||||
|
progressBar.style.width = `${percent}%`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateFunctions: UpdateFunctions = { updateStatus, updateProgress };
|
||||||
|
|
||||||
|
try {
|
||||||
|
updateStatus('🌐 Connexion aux relais...', 'loading');
|
||||||
|
updateProgress(20);
|
||||||
|
|
||||||
|
let services;
|
||||||
|
try {
|
||||||
|
services = await initializeServices();
|
||||||
|
} catch (error) {
|
||||||
|
secureLogger.error('Services not available', error as Error, { component: 'BirthdaySetup' });
|
||||||
|
updateStatus('❌ Erreur: Services non disponibles', 'error');
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateProgress(percent: number) {
|
updateStatus('🔍 Vérification des prérequis...', 'loading');
|
||||||
progressBar.style.width = `${percent}%`;
|
updateProgress(20);
|
||||||
|
|
||||||
|
const prerequisites = await verifyPrerequisites(updateFunctions);
|
||||||
|
if (!prerequisites) {
|
||||||
|
return; // Already redirected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
secureLogger.info('✅ All prerequisites verified', { component: 'BirthdaySetup' });
|
||||||
|
|
||||||
|
await services.connectAllRelays();
|
||||||
|
await waitForBlockHeight(services, updateFunctions);
|
||||||
|
await updateBirthday(services, updateFunctions);
|
||||||
|
await verifyBirthdayUpdate(services, updateFunctions);
|
||||||
|
redirectToBlockSync(updateFunctions);
|
||||||
|
} catch (error) {
|
||||||
|
secureLogger.error('Error during birthday setup', error as Error, {
|
||||||
|
component: 'BirthdaySetup',
|
||||||
|
});
|
||||||
|
updateStatus('❌ Erreur lors de la configuration de la date anniversaire', 'error');
|
||||||
|
} finally {
|
||||||
|
isInitializing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gestion du bouton continuer
|
||||||
|
continueBtn.addEventListener('click', async () => {
|
||||||
|
secureLogger.info('🏠 Redirecting to main application...', { component: 'BirthdaySetup' });
|
||||||
|
// Rediriger vers l'application principale
|
||||||
|
secureLogger.debug('🎂 Birthday setup completed, checking storage state...', {
|
||||||
|
component: 'BirthdaySetup',
|
||||||
|
});
|
||||||
|
const { checkStorageStateAndNavigate } = await import('../../router');
|
||||||
|
await checkStorageStateAndNavigate();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,243 +1,264 @@
|
|||||||
import { checkPBKDF2Key, checkWalletWithRetries } from '../../utils/prerequisites.utils';
|
import { checkPBKDF2Key, checkWalletWithRetries } from '../../utils/prerequisites.utils';
|
||||||
import { secureLogger } from '../services/secure-logger';
|
import { secureLogger } from '../../services/secure-logger';
|
||||||
import Services from '../../services/service';
|
import Services from '../../services/service';
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", async () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
secureLogger.info('🔄 Block sync page loaded', { component: 'BlockSync' });
|
secureLogger.info('🔄 Block sync page loaded', { component: 'BlockSync' });
|
||||||
|
|
||||||
const status = document.getElementById("status") as HTMLElement;
|
const status = document.getElementById('status') as HTMLElement;
|
||||||
const progressBar = document.getElementById("progressBar") as HTMLElement;
|
const progressBar = document.getElementById('progressBar') as HTMLElement;
|
||||||
const continueBtn = document.getElementById("continueBtn") as HTMLButtonElement;
|
const continueBtn = document.getElementById('continueBtn') as HTMLButtonElement;
|
||||||
|
|
||||||
function updateStatus(message: string, type: 'loading' | 'success' | 'error') {
|
function updateStatus(message: string, type: 'loading' | 'success' | 'error') {
|
||||||
if (status) {
|
if (status) {
|
||||||
status.textContent = message;
|
status.textContent = message;
|
||||||
status.className = `status ${type}`;
|
status.className = `status ${type}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateProgress(percentage: number) {
|
||||||
|
if (progressBar) {
|
||||||
|
progressBar.style.width = `${percentage}%`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSyncItem(
|
||||||
|
elementId: string,
|
||||||
|
value: string,
|
||||||
|
status: 'pending' | 'completed' | 'error' = 'pending'
|
||||||
|
) {
|
||||||
|
const element = document.getElementById(elementId);
|
||||||
|
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 () => {
|
||||||
|
secureLogger.info('🔗 Redirecting to pairing page...', { component: 'BlockSync' });
|
||||||
|
window.location.href = '/src/pages/pairing/pairing.html';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Vérifier les prérequis
|
||||||
|
secureLogger.debug('🔍 Verifying prerequisites...', { component: 'BlockSync' });
|
||||||
|
updateStatus('🔍 Vérification des prérequis...', 'loading');
|
||||||
|
|
||||||
|
const pbkdf2KeyResult = await checkPBKDF2Key();
|
||||||
|
if (!pbkdf2KeyResult) {
|
||||||
|
secureLogger.warn('⚠️ PBKDF2 key not found, redirecting to security-setup...', {
|
||||||
|
component: 'BlockSync',
|
||||||
|
});
|
||||||
|
updateStatus('⚠️ Redirection vers la configuration de sécurité...', 'loading');
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = '/src/pages/security-setup/security-setup.html';
|
||||||
|
}, 1000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wallet = await checkWalletWithRetries();
|
||||||
|
if (!wallet) {
|
||||||
|
secureLogger.warn('⚠️ Wallet not found, redirecting to wallet-setup...', {
|
||||||
|
component: 'BlockSync',
|
||||||
|
});
|
||||||
|
updateStatus('⚠️ Redirection vers la configuration du wallet...', 'loading');
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = '/src/pages/wallet-setup/wallet-setup.html';
|
||||||
|
}, 1000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wallet.sp_wallet?.birthday || wallet.sp_wallet.birthday === 0) {
|
||||||
|
secureLogger.warn('⚠️ Birthday not configured, redirecting to birthday-setup...', {
|
||||||
|
component: 'BlockSync',
|
||||||
|
});
|
||||||
|
updateStatus('⚠️ Redirection vers la configuration de la date anniversaire...', 'loading');
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = '/src/pages/birthday-setup/birthday-setup.html';
|
||||||
|
}, 1000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
secureLogger.info('✅ All prerequisites verified for block sync', { component: 'BlockSync' });
|
||||||
|
updateStatus('✅ Prerequisites verified', 'success');
|
||||||
|
|
||||||
|
// Initialiser les services
|
||||||
|
secureLogger.info('🔄 Waiting for services to be ready...', { component: 'BlockSync' });
|
||||||
|
updateStatus('🔄 Initialisation des services...', 'loading');
|
||||||
|
|
||||||
|
let services: Services;
|
||||||
|
let attempts = 0;
|
||||||
|
const maxAttempts = 30;
|
||||||
|
|
||||||
|
while (attempts < maxAttempts) {
|
||||||
|
try {
|
||||||
|
secureLogger.info(
|
||||||
|
'🔄 Attempting to get services (attempt ${attempts + 1}/${maxAttempts})...',
|
||||||
|
{ component: 'BlockSync' }
|
||||||
|
);
|
||||||
|
services = await Services.getInstance();
|
||||||
|
secureLogger.info('✅ Services initialized successfully', { component: 'BlockSync' });
|
||||||
|
break;
|
||||||
|
} catch (error) {
|
||||||
|
attempts++;
|
||||||
|
secureLogger.warn(
|
||||||
|
`Services initialization failed (attempt ${attempts}/${maxAttempts})`,
|
||||||
|
error as Error,
|
||||||
|
{ component: 'BlockSync' }
|
||||||
|
);
|
||||||
|
if (attempts >= maxAttempts) {
|
||||||
|
throw new Error('Failed to initialize services after maximum attempts');
|
||||||
}
|
}
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateProgress(percentage: number) {
|
if (!services!) {
|
||||||
if (progressBar) {
|
throw new Error('Services not initialized');
|
||||||
progressBar.style.width = `${percentage}%`;
|
}
|
||||||
|
|
||||||
|
// Vérifier si le wallet est déjà synchronisé
|
||||||
|
const currentBlockHeight = services.getCurrentBlockHeight();
|
||||||
|
if (currentBlockHeight === -1) {
|
||||||
|
secureLogger.warn('⚠️ Block height not available, connecting to relays...', {
|
||||||
|
component: 'BlockSync',
|
||||||
|
});
|
||||||
|
updateStatus('⚠️ Connexion aux relays...', 'loading');
|
||||||
|
|
||||||
|
// Attendre que les services se connectent aux relays
|
||||||
|
await services.connectAllRelays();
|
||||||
|
|
||||||
|
// Attendre que la hauteur de bloc soit définie
|
||||||
|
await services.waitForBlockHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
const finalBlockHeight = services.getCurrentBlockHeight();
|
||||||
|
const birthday = wallet.sp_wallet.birthday;
|
||||||
|
const lastScan = wallet.sp_wallet.last_scan || 0;
|
||||||
|
const toScan = Math.max(0, finalBlockHeight - lastScan);
|
||||||
|
|
||||||
|
secureLogger.info(
|
||||||
|
'📊 Sync info: current=${finalBlockHeight}, birthday=${birthday}, lastScan=${lastScan}, toScan=${toScan}',
|
||||||
|
{ component: 'BlockSync' }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Mettre à jour l'interface
|
||||||
|
updateSyncItem('currentBlock', finalBlockHeight.toString(), 'completed');
|
||||||
|
updateSyncItem('birthday', birthday.toString(), 'completed');
|
||||||
|
updateSyncItem('lastScan', lastScan.toString(), 'completed');
|
||||||
|
|
||||||
|
if (toScan === 0) {
|
||||||
|
secureLogger.info('✅ Wallet already synchronized', { component: 'BlockSync' });
|
||||||
|
updateStatus('✅ Wallet déjà synchronisé', 'success');
|
||||||
|
updateSyncItem('blocksToScan', '0', 'completed');
|
||||||
|
updateSyncItem('blocksScanned', '0', 'completed');
|
||||||
|
updateSyncItem('transactionsFound', '0', 'completed');
|
||||||
|
|
||||||
|
// Activer le bouton et rediriger automatiquement
|
||||||
|
if (continueBtn) {
|
||||||
|
continueBtn.disabled = false;
|
||||||
|
continueBtn.textContent = 'Aller au pairing';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-redirection après 3 secondes
|
||||||
|
setTimeout(() => {
|
||||||
|
secureLogger.info('🔗 Auto-redirecting to pairing page...', { component: 'BlockSync' });
|
||||||
|
window.location.href = '/src/pages/pairing/pairing.html';
|
||||||
|
}, 3000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Afficher la barre de progression
|
||||||
|
updateProgress(0);
|
||||||
|
updateStatus('🔄 Synchronisation en cours...', 'loading');
|
||||||
|
updateSyncItem('blocksToScan', toScan.toString(), 'pending');
|
||||||
|
|
||||||
|
// Intercepter les messages de progression du scan
|
||||||
|
|
||||||
|
// Fonction pour intercepter les messages de progression
|
||||||
|
const originalConsoleLog = console.log;
|
||||||
|
console.log = (...args: any[]) => {
|
||||||
|
const message = args.join(' ');
|
||||||
|
if (message.includes('Scan progress:')) {
|
||||||
|
// Extraire les informations de progression
|
||||||
|
const progressMatch = message.match(/Scan progress: (\d+)\/(\d+) \((\d+)%\)/);
|
||||||
|
if (progressMatch) {
|
||||||
|
const currentBlock = parseInt(progressMatch[1]);
|
||||||
|
const totalBlocks = parseInt(progressMatch[2]);
|
||||||
|
const percentage = parseInt(progressMatch[3]);
|
||||||
|
|
||||||
|
// Mettre à jour l'interface avec les détails de progression
|
||||||
|
updateStatus(
|
||||||
|
`🔍 Synchronisation des blocs: ${currentBlock}/${totalBlocks} (${percentage}%)`,
|
||||||
|
'loading'
|
||||||
|
);
|
||||||
|
updateProgress(percentage);
|
||||||
|
|
||||||
|
// Mettre à jour les éléments de synchronisation
|
||||||
|
updateSyncItem('blocksScanned', currentBlock.toString(), 'pending');
|
||||||
|
updateSyncItem('blocksToScan', (totalBlocks - currentBlock).toString(), 'pending');
|
||||||
|
|
||||||
|
// Progress message available if needed: `Bloc ${currentBlock}/${totalBlocks} (${percentage}%)`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Appeler la fonction console.log originale
|
||||||
function updateSyncItem(elementId: string, value: string, status: 'pending' | 'completed' | 'error' = 'pending') {
|
originalConsoleLog.apply(console, args);
|
||||||
const element = document.getElementById(elementId);
|
};
|
||||||
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 () => {
|
|
||||||
secureLogger.info('🔗 Redirecting to pairing page...', { component: 'BlockSync' });
|
|
||||||
window.location.href = '/src/pages/pairing/pairing.html';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Vérifier les prérequis
|
// Effectuer la synchronisation
|
||||||
secureLogger.debug('🔍 Verifying prerequisites...', { component: 'BlockSync' });
|
await services.updateDeviceBlockHeight();
|
||||||
updateStatus('🔍 Vérification des prérequis...', 'loading');
|
secureLogger.info('✅ Block scan completed successfully', { component: 'BlockSync' });
|
||||||
|
|
||||||
const pbkdf2KeyResult = await checkPBKDF2Key();
|
// Restaurer la fonction console.log originale
|
||||||
if (!pbkdf2KeyResult) {
|
console.log = originalConsoleLog;
|
||||||
secureLogger.warn('⚠️ PBKDF2 key not found, redirecting to security-setup...', { component: 'BlockSync' });
|
|
||||||
updateStatus('⚠️ Redirection vers la configuration de sécurité...', 'loading');
|
|
||||||
setTimeout(() => {
|
|
||||||
window.location.href = '/src/pages/security-setup/security-setup.html';
|
|
||||||
}, 1000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const wallet = await checkWalletWithRetries();
|
updateStatus('✅ Synchronisation terminée', 'success');
|
||||||
if (!wallet) {
|
updateProgress(100);
|
||||||
secureLogger.warn('⚠️ Wallet not found, redirecting to wallet-setup...', { component: 'BlockSync' });
|
updateSyncItem('blocksScanned', toScan.toString(), 'completed');
|
||||||
updateStatus('⚠️ Redirection vers la configuration du wallet...', 'loading');
|
updateSyncItem('blocksToScan', '0', 'completed');
|
||||||
setTimeout(() => {
|
updateSyncItem('transactionsFound', '0', 'completed');
|
||||||
window.location.href = '/src/pages/wallet-setup/wallet-setup.html';
|
|
||||||
}, 1000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!wallet.sp_wallet?.birthday || wallet.sp_wallet.birthday === 0) {
|
// Activer le bouton et rediriger automatiquement
|
||||||
secureLogger.warn('⚠️ Birthday not configured, redirecting to birthday-setup...', { component: 'BlockSync' });
|
if (continueBtn) {
|
||||||
updateStatus('⚠️ Redirection vers la configuration de la date anniversaire...', 'loading');
|
continueBtn.disabled = false;
|
||||||
setTimeout(() => {
|
continueBtn.textContent = 'Aller au pairing';
|
||||||
window.location.href = '/src/pages/birthday-setup/birthday-setup.html';
|
}
|
||||||
}, 1000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
secureLogger.info('✅ All prerequisites verified for block sync', { component: 'BlockSync' });
|
|
||||||
updateStatus('✅ Prerequisites verified', 'success');
|
|
||||||
|
|
||||||
// Initialiser les services
|
|
||||||
secureLogger.info('🔄 Waiting for services to be ready...', { component: 'BlockSync' });
|
|
||||||
updateStatus('🔄 Initialisation des services...', 'loading');
|
|
||||||
|
|
||||||
let services: Services;
|
|
||||||
let attempts = 0;
|
|
||||||
const maxAttempts = 30;
|
|
||||||
|
|
||||||
while (attempts < maxAttempts) {
|
|
||||||
try {
|
|
||||||
secureLogger.info('🔄 Attempting to get services (attempt ${attempts + 1}/${maxAttempts})...', { component: 'BlockSync' });
|
|
||||||
services = await Services.getInstance();
|
|
||||||
secureLogger.info('✅ Services initialized successfully', { component: 'BlockSync' });
|
|
||||||
break;
|
|
||||||
} catch (error) {
|
|
||||||
attempts++;
|
|
||||||
secureLogger.warn(`Services initialization failed (attempt ${attempts}/${maxAttempts})`, error as Error, { component: 'BlockSync' });
|
|
||||||
if (attempts >= maxAttempts) {
|
|
||||||
throw new Error('Failed to initialize services after maximum attempts');
|
|
||||||
}
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!services!) {
|
|
||||||
throw new Error('Services not initialized');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vérifier si le wallet est déjà synchronisé
|
|
||||||
const currentBlockHeight = services.getCurrentBlockHeight();
|
|
||||||
if (currentBlockHeight === -1) {
|
|
||||||
secureLogger.warn('⚠️ Block height not available, connecting to relays...', { component: 'BlockSync' });
|
|
||||||
updateStatus('⚠️ Connexion aux relays...', 'loading');
|
|
||||||
|
|
||||||
// Attendre que les services se connectent aux relays
|
|
||||||
await services.connectAllRelays();
|
|
||||||
|
|
||||||
// Attendre que la hauteur de bloc soit définie
|
|
||||||
await services.waitForBlockHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
const finalBlockHeight = services.getCurrentBlockHeight();
|
|
||||||
const birthday = wallet.sp_wallet.birthday;
|
|
||||||
const lastScan = wallet.sp_wallet.last_scan || 0;
|
|
||||||
const toScan = Math.max(0, finalBlockHeight - lastScan);
|
|
||||||
|
|
||||||
secureLogger.info('📊 Sync info: current=${finalBlockHeight}, birthday=${birthday}, lastScan=${lastScan}, toScan=${toScan}', { component: 'BlockSync' });
|
|
||||||
|
|
||||||
// Mettre à jour l'interface
|
|
||||||
updateSyncItem('currentBlock', finalBlockHeight.toString(), 'completed');
|
|
||||||
updateSyncItem('birthday', birthday.toString(), 'completed');
|
|
||||||
updateSyncItem('lastScan', lastScan.toString(), 'completed');
|
|
||||||
|
|
||||||
if (toScan === 0) {
|
|
||||||
secureLogger.info('✅ Wallet already synchronized', { component: 'BlockSync' });
|
|
||||||
updateStatus('✅ Wallet déjà synchronisé', 'success');
|
|
||||||
updateSyncItem('blocksToScan', '0', 'completed');
|
|
||||||
updateSyncItem('blocksScanned', '0', 'completed');
|
|
||||||
updateSyncItem('transactionsFound', '0', 'completed');
|
|
||||||
|
|
||||||
// Activer le bouton et rediriger automatiquement
|
|
||||||
if (continueBtn) {
|
|
||||||
continueBtn.disabled = false;
|
|
||||||
continueBtn.textContent = 'Aller au pairing';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auto-redirection après 3 secondes
|
|
||||||
setTimeout(() => {
|
|
||||||
secureLogger.info('🔗 Auto-redirecting to pairing page...', { component: 'BlockSync' });
|
|
||||||
window.location.href = '/src/pages/pairing/pairing.html';
|
|
||||||
}, 3000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Afficher la barre de progression
|
|
||||||
updateProgress(0);
|
|
||||||
updateStatus('🔄 Synchronisation en cours...', 'loading');
|
|
||||||
updateSyncItem('blocksToScan', toScan.toString(), 'pending');
|
|
||||||
|
|
||||||
// Intercepter les messages de progression du scan
|
|
||||||
let scanProgressInterval: NodeJS.Timeout | null = null;
|
|
||||||
let lastProgressMessage = '';
|
|
||||||
|
|
||||||
// Fonction pour intercepter les messages de progression
|
|
||||||
const originalConsoleLog = console.log;
|
|
||||||
console.log = (...args: any[]) => {
|
|
||||||
const message = args.join(' ');
|
|
||||||
if (message.includes('Scan progress:')) {
|
|
||||||
// Extraire les informations de progression
|
|
||||||
const progressMatch = message.match(/Scan progress: (\d+)\/(\d+) \((\d+)%\)/);
|
|
||||||
if (progressMatch) {
|
|
||||||
const currentBlock = parseInt(progressMatch[1]);
|
|
||||||
const totalBlocks = parseInt(progressMatch[2]);
|
|
||||||
const percentage = parseInt(progressMatch[3]);
|
|
||||||
|
|
||||||
// Mettre à jour l'interface avec les détails de progression
|
|
||||||
updateStatus(`🔍 Synchronisation des blocs: ${currentBlock}/${totalBlocks} (${percentage}%)`, 'loading');
|
|
||||||
updateProgress(percentage);
|
|
||||||
|
|
||||||
// Mettre à jour les éléments de synchronisation
|
|
||||||
updateSyncItem('blocksScanned', currentBlock.toString(), 'pending');
|
|
||||||
updateSyncItem('blocksToScan', (totalBlocks - currentBlock).toString(), 'pending');
|
|
||||||
|
|
||||||
lastProgressMessage = `Bloc ${currentBlock}/${totalBlocks} (${percentage}%)`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Appeler la fonction console.log originale
|
|
||||||
originalConsoleLog.apply(console, args);
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Effectuer la synchronisation
|
|
||||||
await services.updateDeviceBlockHeight();
|
|
||||||
secureLogger.info('✅ Block scan completed successfully', { component: 'BlockSync' });
|
|
||||||
|
|
||||||
// Restaurer la fonction console.log originale
|
|
||||||
console.log = originalConsoleLog;
|
|
||||||
|
|
||||||
updateStatus('✅ Synchronisation terminée', 'success');
|
|
||||||
updateProgress(100);
|
|
||||||
updateSyncItem('blocksScanned', toScan.toString(), 'completed');
|
|
||||||
updateSyncItem('blocksToScan', '0', 'completed');
|
|
||||||
updateSyncItem('transactionsFound', '0', 'completed');
|
|
||||||
|
|
||||||
// Activer le bouton et rediriger automatiquement
|
|
||||||
if (continueBtn) {
|
|
||||||
continueBtn.disabled = false;
|
|
||||||
continueBtn.textContent = 'Aller au pairing';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auto-redirection après 3 secondes
|
|
||||||
setTimeout(() => {
|
|
||||||
secureLogger.info('🔗 Auto-redirecting to pairing page...', { component: 'BlockSync' });
|
|
||||||
window.location.href = '/src/pages/pairing/pairing.html';
|
|
||||||
}, 3000);
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
// Restaurer la fonction console.log originale en cas d'erreur
|
|
||||||
console.log = originalConsoleLog;
|
|
||||||
secureLogger.error('Error during block scan', error as Error, { component: 'BlockSync' });
|
|
||||||
updateStatus(`❌ Erreur lors de la synchronisation: ${(error as Error).message}`, 'error');
|
|
||||||
updateSyncItem('blocksToScan', 'Erreur', 'error');
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Auto-redirection après 3 secondes
|
||||||
|
setTimeout(() => {
|
||||||
|
secureLogger.info('🔗 Auto-redirecting to pairing page...', { component: 'BlockSync' });
|
||||||
|
window.location.href = '/src/pages/pairing/pairing.html';
|
||||||
|
}, 3000);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
secureLogger.error('Error in block sync page', error as Error, { component: 'BlockSync' });
|
// Restaurer la fonction console.log originale en cas d'erreur
|
||||||
updateStatus(`❌ Erreur: ${(error as Error).message}`, 'error');
|
console.log = originalConsoleLog;
|
||||||
|
secureLogger.error('Error during block scan', error as Error, { component: 'BlockSync' });
|
||||||
// Rediriger vers la page appropriée selon l'erreur
|
updateStatus(`❌ Erreur lors de la synchronisation: ${(error as Error).message}`, 'error');
|
||||||
const errorMessage = (error as Error).message;
|
updateSyncItem('blocksToScan', 'Erreur', 'error');
|
||||||
if (errorMessage.includes('PBKDF2') || errorMessage.includes('security')) {
|
throw error;
|
||||||
setTimeout(() => {
|
|
||||||
window.location.href = '/src/pages/security-setup/security-setup.html';
|
|
||||||
}, 2000);
|
|
||||||
} else if (errorMessage.includes('wallet') || errorMessage.includes('device')) {
|
|
||||||
setTimeout(() => {
|
|
||||||
window.location.href = '/src/pages/wallet-setup/wallet-setup.html';
|
|
||||||
}, 2000);
|
|
||||||
} else if (errorMessage.includes('birthday')) {
|
|
||||||
setTimeout(() => {
|
|
||||||
window.location.href = '/src/pages/birthday-setup/birthday-setup.html';
|
|
||||||
}, 2000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
secureLogger.error('Error in block sync page', error as Error, { component: 'BlockSync' });
|
||||||
|
updateStatus(`❌ Erreur: ${(error as Error).message}`, 'error');
|
||||||
|
|
||||||
|
// Rediriger vers la page appropriée selon l'erreur
|
||||||
|
const errorMessage = (error as Error).message;
|
||||||
|
if (errorMessage.includes('PBKDF2') || errorMessage.includes('security')) {
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = '/src/pages/security-setup/security-setup.html';
|
||||||
|
}, 2000);
|
||||||
|
} else if (errorMessage.includes('wallet') || errorMessage.includes('device')) {
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = '/src/pages/wallet-setup/wallet-setup.html';
|
||||||
|
}, 2000);
|
||||||
|
} else if (errorMessage.includes('birthday')) {
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = '/src/pages/birthday-setup/birthday-setup.html';
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
@ -4,7 +4,7 @@ import { SecurityModeService, SecurityMode } from '../../services/security-mode.
|
|||||||
import { secureLogger } from '../../services/secure-logger';
|
import { secureLogger } from '../../services/secure-logger';
|
||||||
import Services from '../../services/service';
|
import Services from '../../services/service';
|
||||||
import { addSubscription } from '../../utils/subscription.utils';
|
import { addSubscription } from '../../utils/subscription.utils';
|
||||||
import { displayEmojis, generateCreateBtn, addressToEmoji, prepareAndSendPairingTx } from '../../utils/sp-address.utils';
|
import { displayEmojis, generateCreateBtn, prepareAndSendPairingTx } from '../../utils/sp-address.utils';
|
||||||
import { getCorrectDOM } from '../../utils/html.utils';
|
import { getCorrectDOM } from '../../utils/html.utils';
|
||||||
import { IframePairingComponent } from '../../components/iframe-pairing/iframe-pairing';
|
import { IframePairingComponent } from '../../components/iframe-pairing/iframe-pairing';
|
||||||
import { checkPBKDF2Key, checkWalletWithRetries } from '../../utils/prerequisites.utils';
|
import { checkPBKDF2Key, checkWalletWithRetries } from '../../utils/prerequisites.utils';
|
||||||
@ -646,7 +646,7 @@ async function handleMainPairing(): Promise<void> {
|
|||||||
let currentMode: string | null = null;
|
let currentMode: string | null = null;
|
||||||
try {
|
try {
|
||||||
currentMode = await securityModeService.getCurrentMode();
|
currentMode = await securityModeService.getCurrentMode();
|
||||||
} catch (error) {
|
} catch {
|
||||||
// Si aucun mode n'est configuré, rediriger vers security-setup (le mode devrait déjà être configuré avant d'arriver ici)
|
// Si aucun mode n'est configuré, rediriger vers security-setup (le mode devrait déjà être configuré avant d'arriver ici)
|
||||||
secureLogger.warn('⚠️ No security mode configured, redirecting to security-setup...', { component: 'HomePage' });
|
secureLogger.warn('⚠️ No security mode configured, redirecting to security-setup...', { component: 'HomePage' });
|
||||||
if (mainStatus) {
|
if (mainStatus) {
|
||||||
@ -763,8 +763,7 @@ async function handleMainPairing(): Promise<void> {
|
|||||||
await secureCredentialsService.storeCredentials(credentialData, '');
|
await secureCredentialsService.storeCredentials(credentialData, '');
|
||||||
secureLogger.info('✅ Credentials stored successfully', { component: 'HomePage' });
|
secureLogger.info('✅ Credentials stored successfully', { component: 'HomePage' });
|
||||||
|
|
||||||
// Note: Faucet temporarily disabled due to relay configuration issue
|
// Faucet flow remains enabled and required
|
||||||
secureLogger.info('🪙 Faucet temporarily disabled - relay needs Bitcoin Core wallet configuration', { component: 'HomePage' });
|
|
||||||
|
|
||||||
// Decrypt and make keys available to SDK
|
// Decrypt and make keys available to SDK
|
||||||
secureLogger.info('🔓 Decrypting credentials for SDK access...', { component: 'HomePage' });
|
secureLogger.info('🔓 Decrypting credentials for SDK access...', { component: 'HomePage' });
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { DeviceReaderService } from '../../services/device-reader.service';
|
import { DeviceReaderService } from '../../services/device-reader.service';
|
||||||
import { secureLogger } from '../services/secure-logger';
|
import { secureLogger } from '../../services/secure-logger';
|
||||||
import { checkPBKDF2Key, checkWalletWithRetries } from '../../utils/prerequisites.utils';
|
import { checkPBKDF2Key, checkWalletWithRetries } from '../../utils/prerequisites.utils';
|
||||||
|
|
||||||
// Extend WindowEventMap to include custom events
|
// Extend WindowEventMap to include custom events
|
||||||
@ -64,7 +64,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
secureLogger.info('🔧 Getting device reader service...', { component: 'PairingPage' });
|
secureLogger.info('🔧 Getting device reader service...', { component: 'PairingPage' });
|
||||||
const deviceReader = DeviceReaderService.getInstance();
|
// const deviceReader = DeviceReaderService.getInstance(); // Not used yet
|
||||||
|
|
||||||
// Vérifier que le PBKDF2 key existe d'abord
|
// Vérifier que le PBKDF2 key existe d'abord
|
||||||
const pbkdf2KeyResult = await checkPBKDF2Key();
|
const pbkdf2KeyResult = await checkPBKDF2Key();
|
||||||
|
|||||||
@ -86,7 +86,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
|
|
||||||
// Générer la clé PBKDF2 et la stocker selon le mode
|
// Générer la clé PBKDF2 et la stocker selon le mode
|
||||||
// IMPORTANT: Cette opération doit être directe (pas de délai) pour que WebAuthn fonctionne
|
// IMPORTANT: Cette opération doit être directe (pas de délai) pour que WebAuthn fonctionne
|
||||||
const pbkdf2Key = await secureCredentialsService.generatePBKDF2Key(selectedMode);
|
await secureCredentialsService.generatePBKDF2Key(selectedMode);
|
||||||
secureLogger.info('✅ PBKDF2 key generated and stored securely', { component: 'SecuritySetup' });
|
secureLogger.info('✅ PBKDF2 key generated and stored securely', { component: 'SecuritySetup' });
|
||||||
|
|
||||||
// Rediriger directement vers la page de génération du wallet
|
// Rediriger directement vers la page de génération du wallet
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { DATABASE_CONFIG } from '../../services/database-config';
|
import { DATABASE_CONFIG } from '../../services/database-config';
|
||||||
import { secureLogger } from '../services/secure-logger';
|
import { secureLogger } from '../../services/secure-logger';
|
||||||
import { checkPBKDF2Key } from '../../utils/prerequisites.utils';
|
import { checkPBKDF2Key } from '../../utils/prerequisites.utils';
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
@ -23,30 +23,30 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
progressBar.style.width = `${percent}%`;
|
progressBar.style.width = `${percent}%`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Méthode pour sauvegarder directement en IndexedDB dans la base 4nk
|
// Méthode pour sauvegarder directement en IndexedDB dans la base 4nk (non utilisée pour l'instant)
|
||||||
async function saveCredentialsDirectly(credentials: any): Promise<void> {
|
// async function saveCredentialsDirectly(credentials: any): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
// return new Promise((resolve, reject) => {
|
||||||
const request = indexedDB.open(DATABASE_CONFIG.name, DATABASE_CONFIG.version);
|
// const request = indexedDB.open(DATABASE_CONFIG.name, DATABASE_CONFIG.version);
|
||||||
|
|
||||||
request.onerror = () => reject(request.error);
|
// request.onerror = () => reject(request.error);
|
||||||
request.onsuccess = () => {
|
// request.onsuccess = () => {
|
||||||
const db = request.result;
|
// const db = request.result;
|
||||||
const transaction = db.transaction([DATABASE_CONFIG.stores.wallet.name], 'readwrite');
|
// const transaction = db.transaction([DATABASE_CONFIG.stores.wallet.name], 'readwrite');
|
||||||
const store = transaction.objectStore(DATABASE_CONFIG.stores.wallet.name);
|
// const store = transaction.objectStore(DATABASE_CONFIG.stores.wallet.name);
|
||||||
|
|
||||||
const putRequest = store.put(credentials, '/4nk/credentials');
|
// const putRequest = store.put(credentials, '/4nk/credentials');
|
||||||
putRequest.onsuccess = () => resolve();
|
// putRequest.onsuccess = () => resolve();
|
||||||
putRequest.onerror = () => reject(putRequest.error);
|
// putRequest.onerror = () => reject(putRequest.error);
|
||||||
};
|
// };
|
||||||
|
|
||||||
request.onupgradeneeded = () => {
|
// request.onupgradeneeded = () => {
|
||||||
const db = request.result;
|
// const db = request.result;
|
||||||
if (!db.objectStoreNames.contains(DATABASE_CONFIG.stores.wallet.name)) {
|
// if (!db.objectStoreNames.contains(DATABASE_CONFIG.stores.wallet.name)) {
|
||||||
db.createObjectStore(DATABASE_CONFIG.stores.wallet.name, { keyPath: DATABASE_CONFIG.stores.wallet.keyPath as string });
|
// db.createObjectStore(DATABASE_CONFIG.stores.wallet.name, { keyPath: DATABASE_CONFIG.stores.wallet.keyPath as string });
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Étape 1: Vérifier les prérequis AVANT d'initialiser Services
|
// Étape 1: Vérifier les prérequis AVANT d'initialiser Services
|
||||||
@ -110,10 +110,8 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
if ((performance as any).memory) {
|
if ((performance as any).memory) {
|
||||||
const memory = (performance as any).memory;
|
const memory = (performance as any).memory;
|
||||||
const usedPercent = (memory.usedJSHeapSize / memory.jsHeapSizeLimit) * 100;
|
const usedPercent = (memory.usedJSHeapSize / memory.jsHeapSizeLimit) * 100;
|
||||||
const usedMB = memory.usedJSHeapSize / 1024 / 1024;
|
|
||||||
const limitMB = memory.jsHeapSizeLimit / 1024 / 1024;
|
|
||||||
|
|
||||||
secureLogger.info('📊 Current memory usage: ${usedPercent.toFixed(1)}% (${usedMB.toFixed(1)}MB / ${limitMB.toFixed(1)}MB)', { component: 'WalletSetup' });
|
secureLogger.info(`📊 Current memory usage: ${usedPercent.toFixed(1)}%`, { component: 'WalletSetup' });
|
||||||
|
|
||||||
// Si la mémoire est très élevée (>75%), tenter un nettoyage agressif
|
// Si la mémoire est très élevée (>75%), tenter un nettoyage agressif
|
||||||
if (usedPercent > 75) {
|
if (usedPercent > 75) {
|
||||||
@ -194,10 +192,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
// Diagnostic plus détaillé
|
// Diagnostic plus détaillé
|
||||||
if (attempts === 5) {
|
if (attempts === 5) {
|
||||||
secureLogger.debug('🔍 Diagnostic: Checking memory usage...', { component: 'WalletSetup' });
|
secureLogger.debug('🔍 Diagnostic: Checking memory usage...', { component: 'WalletSetup' });
|
||||||
if ((performance as any).memory) {
|
// Memory check removed - not needed here
|
||||||
const memory = (performance as any).memory;
|
|
||||||
secureLogger.info('📊 Memory usage: ${Math.round(memory.usedJSHeapSize / 1024 / 1024)}MB / ${Math.round(memory.totalJSHeapSize / 1024 / 1024)}MB', { component: 'WalletSetup' });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attempts++;
|
attempts++;
|
||||||
@ -279,7 +274,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
|
|
||||||
// Créer le store wallet seulement s'il n'existe pas
|
// Créer le store wallet seulement s'il n'existe pas
|
||||||
if (!db.objectStoreNames.contains(DATABASE_CONFIG.stores.wallet.name)) {
|
if (!db.objectStoreNames.contains(DATABASE_CONFIG.stores.wallet.name)) {
|
||||||
const store = db.createObjectStore(DATABASE_CONFIG.stores.wallet.name, { keyPath: DATABASE_CONFIG.stores.wallet.keyPath as string });
|
db.createObjectStore(DATABASE_CONFIG.stores.wallet.name, { keyPath: DATABASE_CONFIG.stores.wallet.keyPath as string });
|
||||||
secureLogger.info('✅ Wallet store created with keyPath: ${DATABASE_CONFIG.stores.wallet.keyPath}', { component: 'WalletSetup' });
|
secureLogger.info('✅ Wallet store created with keyPath: ${DATABASE_CONFIG.stores.wallet.keyPath}', { component: 'WalletSetup' });
|
||||||
} else {
|
} else {
|
||||||
secureLogger.info('✅ Wallet store already exists', { component: 'WalletSetup' });
|
secureLogger.info('✅ Wallet store already exists', { component: 'WalletSetup' });
|
||||||
@ -451,7 +446,8 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
secureLogger.info('✅ Wallet saved exclusively in IndexedDB and verified', { component: 'WalletSetup' });
|
secureLogger.info('✅ Wallet saved exclusively in IndexedDB and verified', { component: 'WalletSetup' });
|
||||||
console.log('🔍 Wallet contains only encrypted data:', {
|
secureLogger.debug('🔍 Wallet contains only encrypted data:', {
|
||||||
|
component: 'WalletSetup',
|
||||||
hasEncryptedDevice: !!finalVerification.encrypted_device,
|
hasEncryptedDevice: !!finalVerification.encrypted_device,
|
||||||
hasEncryptedWallet: !!finalVerification.encrypted_wallet,
|
hasEncryptedWallet: !!finalVerification.encrypted_wallet,
|
||||||
hasDeviceInClear: !!finalVerification.device // DEVRAIT ÊTRE FALSE
|
hasDeviceInClear: !!finalVerification.device // DEVRAIT ÊTRE FALSE
|
||||||
@ -472,7 +468,8 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
pbkdf2KeyTest
|
pbkdf2KeyTest
|
||||||
);
|
);
|
||||||
const parsedWallet = JSON.parse(decryptedWallet);
|
const parsedWallet = JSON.parse(decryptedWallet);
|
||||||
console.log('✅ TEST: Wallet decrypted successfully:', {
|
secureLogger.debug('✅ TEST: Wallet decrypted successfully:', {
|
||||||
|
component: 'WalletSetup',
|
||||||
hasScanSk: !!parsedWallet.scan_sk,
|
hasScanSk: !!parsedWallet.scan_sk,
|
||||||
hasSpendKey: !!parsedWallet.spend_key,
|
hasSpendKey: !!parsedWallet.spend_key,
|
||||||
network: parsedWallet.network,
|
network: parsedWallet.network,
|
||||||
@ -486,7 +483,8 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
pbkdf2KeyTest
|
pbkdf2KeyTest
|
||||||
);
|
);
|
||||||
const parsedDevice = JSON.parse(decryptedDevice);
|
const parsedDevice = JSON.parse(decryptedDevice);
|
||||||
console.log('✅ TEST: Device decrypted successfully:', {
|
secureLogger.debug('✅ TEST: Device decrypted successfully:', {
|
||||||
|
component: 'WalletSetup',
|
||||||
hasSpWallet: !!parsedDevice.sp_wallet,
|
hasSpWallet: !!parsedDevice.sp_wallet,
|
||||||
network: parsedDevice.network
|
network: parsedDevice.network
|
||||||
});
|
});
|
||||||
@ -494,8 +492,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
secureLogger.info('✅ TEST: Full decryption test passed - wallet and device decrypt correctly', { component: 'WalletSetup' });
|
secureLogger.info('✅ TEST: Full decryption test passed - wallet and device decrypt correctly', { component: 'WalletSetup' });
|
||||||
}
|
}
|
||||||
} catch (decryptError) {
|
} catch (decryptError) {
|
||||||
console.error('❌ TEST: Decryption test failed:', decryptError);
|
secureLogger.error('❌ TEST: Decryption test failed:', decryptError as Error, { component: 'WalletSetup' });
|
||||||
secureLogger.error('❌ This indicates an issue with encryption/decryption logic', { component: 'WalletSetup' });
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
secureLogger.error('❌ Final wallet verification failed - wallet not found in IndexedDB', { component: 'WalletSetup' });
|
secureLogger.error('❌ Final wallet verification failed - wallet not found in IndexedDB', { component: 'WalletSetup' });
|
||||||
@ -503,7 +500,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Error during wallet save:', error);
|
secureLogger.error('❌ Error during wallet save:', error as Error, { component: 'WalletSetup' });
|
||||||
updateStatus('❌ Erreur: Échec de la sauvegarde du wallet', 'error');
|
updateStatus('❌ Erreur: Échec de la sauvegarde du wallet', 'error');
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
@ -520,13 +517,13 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
secureLogger.info('✅ Continue button enabled', { component: 'WalletSetup' });
|
secureLogger.info('✅ Continue button enabled', { component: 'WalletSetup' });
|
||||||
|
|
||||||
// Redirection automatique après 3 secondes si l'utilisateur ne clique pas
|
// Redirection automatique après 3 secondes si l'utilisateur ne clique pas
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
secureLogger.info('🔄 Auto-redirecting to birthday setup after timeout...', { component: 'WalletSetup' });
|
secureLogger.info('🔄 Auto-redirecting to birthday setup after timeout...', { component: 'WalletSetup' });
|
||||||
window.location.href = '/src/pages/birthday-setup/birthday-setup.html';
|
window.location.href = '/src/pages/birthday-setup/birthday-setup.html';
|
||||||
}, 3000);
|
}, 3000);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Error during wallet setup:', error);
|
secureLogger.error('❌ Error during wallet setup:', error as Error, { component: 'WalletSetup' });
|
||||||
updateStatus('❌ Erreur lors de la génération du wallet', 'error');
|
updateStatus('❌ Erreur lors de la génération du wallet', 'error');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,17 +5,19 @@
|
|||||||
import { Device } from '../../pkg/sdk_client';
|
import { Device } from '../../pkg/sdk_client';
|
||||||
import { secureLogger } from '../services/secure-logger';
|
import { secureLogger } from '../services/secure-logger';
|
||||||
|
|
||||||
|
/* eslint-disable no-unused-vars */
|
||||||
export interface DeviceRepository {
|
export interface DeviceRepository {
|
||||||
getDevice(): Promise<Device | null>;
|
getDevice(): Promise<Device | null>;
|
||||||
saveDevice(device: Device): Promise<void>;
|
saveDevice(_device: Device): Promise<void>;
|
||||||
deleteDevice(): Promise<void>;
|
deleteDevice(): Promise<void>;
|
||||||
hasDevice(): Promise<boolean>;
|
hasDevice(): Promise<boolean>;
|
||||||
getDeviceAddress(): Promise<string | null>;
|
getDeviceAddress(): Promise<string | null>;
|
||||||
updateDevice(device: Partial<Device>): Promise<void>;
|
updateDevice(_device: Partial<Device>): Promise<void>;
|
||||||
}
|
}
|
||||||
|
/* eslint-enable no-unused-vars */
|
||||||
|
|
||||||
export class DeviceRepositoryImpl implements DeviceRepository {
|
export class DeviceRepositoryImpl implements DeviceRepository {
|
||||||
constructor(private database: any) {}
|
constructor(private database: any) {} // database used via methods
|
||||||
|
|
||||||
async getDevice(): Promise<Device | null> {
|
async getDevice(): Promise<Device | null> {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -5,19 +5,21 @@
|
|||||||
import { Process } from '../../pkg/sdk_client';
|
import { Process } from '../../pkg/sdk_client';
|
||||||
import { secureLogger } from '../services/secure-logger';
|
import { secureLogger } from '../services/secure-logger';
|
||||||
|
|
||||||
|
/* eslint-disable no-unused-vars */
|
||||||
export interface ProcessRepository {
|
export interface ProcessRepository {
|
||||||
getProcess(processId: string): Promise<Process | null>;
|
getProcess(_processId: string): Promise<Process | null>;
|
||||||
saveProcess(process: Process): Promise<void>;
|
saveProcess(_process: Process): Promise<void>;
|
||||||
deleteProcess(processId: string): Promise<void>;
|
deleteProcess(_processId: string): Promise<void>;
|
||||||
getProcesses(): Promise<Process[]>;
|
getProcesses(): Promise<Process[]>;
|
||||||
getMyProcesses(): Promise<string[]>;
|
getMyProcesses(): Promise<string[]>;
|
||||||
addMyProcess(processId: string): Promise<void>;
|
addMyProcess(_processId: string): Promise<void>;
|
||||||
removeMyProcess(processId: string): Promise<void>;
|
removeMyProcess(_processId: string): Promise<void>;
|
||||||
hasProcess(processId: string): Promise<boolean>;
|
hasProcess(_processId: string): Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
/* eslint-enable no-unused-vars */
|
||||||
|
|
||||||
export class ProcessRepositoryImpl implements ProcessRepository {
|
export class ProcessRepositoryImpl implements ProcessRepository {
|
||||||
constructor(private database: any) {}
|
constructor(private database: any) {} // database used via methods
|
||||||
|
|
||||||
async getProcess(processId: string): Promise<Process | null> {
|
async getProcess(processId: string): Promise<Process | null> {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -124,7 +124,7 @@ async function handleLocation(path: string) {
|
|||||||
if (path === 'home') {
|
if (path === 'home') {
|
||||||
secureLogger.info('🏠 Processing home route...', { component: 'Router' });
|
secureLogger.info('🏠 Processing home route...', { component: 'Router' });
|
||||||
// Use LoginComponent
|
// Use LoginComponent
|
||||||
const loginComponent = LoginComponent;
|
// const loginComponent = LoginComponent; // Used as type, not variable
|
||||||
const container = document.querySelector('#containerId');
|
const container = document.querySelector('#containerId');
|
||||||
secureLogger.info('🏠 Container for home:', { component: 'Router', data: !!container });
|
secureLogger.info('🏠 Container for home:', { component: 'Router', data: !!container });
|
||||||
const accountComponent = document.createElement('login-4nk-component');
|
const accountComponent = document.createElement('login-4nk-component');
|
||||||
@ -253,18 +253,9 @@ export async function registerAllListeners() {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const successResponse = (data: any, origin: string, messageId?: string) => {
|
// const successResponse = (data: any, origin: string, messageId?: string) => {
|
||||||
// Use successResponse function
|
// // Not used anymore, response sent directly in handlers
|
||||||
secureLogger.info('Success response:', { component: 'Router', data: data });
|
// };
|
||||||
window.parent.postMessage(
|
|
||||||
{
|
|
||||||
type: MessageType.SUCCESS,
|
|
||||||
data: data,
|
|
||||||
messageId,
|
|
||||||
},
|
|
||||||
origin
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// --- Handler functions ---
|
// --- Handler functions ---
|
||||||
const handleRequestLink = async (event: MessageEvent) => {
|
const handleRequestLink = async (event: MessageEvent) => {
|
||||||
@ -658,7 +649,7 @@ export async function registerAllListeners() {
|
|||||||
}
|
}
|
||||||
const processId = createProcessReturn.updated_process.process_id;
|
const processId = createProcessReturn.updated_process.process_id;
|
||||||
const process = createProcessReturn.updated_process.current_process;
|
const process = createProcessReturn.updated_process.current_process;
|
||||||
const stateId = process.states[0].state_id;
|
// const stateId = process.states[0].state_id; // Not used
|
||||||
await services.handleApiReturn(createProcessReturn);
|
await services.handleApiReturn(createProcessReturn);
|
||||||
|
|
||||||
const res = {
|
const res = {
|
||||||
@ -971,7 +962,7 @@ export async function registerAllListeners() {
|
|||||||
let parsedMerkleProof: MerkleProofResult;
|
let parsedMerkleProof: MerkleProofResult;
|
||||||
try {
|
try {
|
||||||
parsedMerkleProof = JSON.parse(merkleProof);
|
parsedMerkleProof = JSON.parse(merkleProof);
|
||||||
} catch (e) {
|
} catch {
|
||||||
throw new Error('Provided merkleProof is not a valid json object');
|
throw new Error('Provided merkleProof is not a valid json object');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1185,6 +1176,7 @@ document.addEventListener('navigate', (e: Event) => {
|
|||||||
* ÉTAPE 2: Gestion de la sécurité (clés de sécurité)
|
* ÉTAPE 2: Gestion de la sécurité (clés de sécurité)
|
||||||
* Cette étape doit être la première et rien d'autre ne doit s'exécuter en parallèle
|
* Cette étape doit être la première et rien d'autre ne doit s'exécuter en parallèle
|
||||||
*/
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
async function handleSecurityKeyManagement(): Promise<boolean> {
|
async function handleSecurityKeyManagement(): Promise<boolean> {
|
||||||
secureLogger.info('🔐 Starting security key management...', { component: 'Router' });
|
secureLogger.info('🔐 Starting security key management...', { component: 'Router' });
|
||||||
|
|
||||||
@ -1211,7 +1203,7 @@ async function handleSecurityKeyManagement(): Promise<boolean> {
|
|||||||
|
|
||||||
if (!hasCredentials) {
|
if (!hasCredentials) {
|
||||||
secureLogger.info('🔐 No security credentials found, redirecting to wallet setup...', { component: 'Router' });
|
secureLogger.info('🔐 No security credentials found, redirecting to wallet setup...', { component: 'Router' });
|
||||||
window.location.href = '/src/pages/wallet-setup/wallet-setup.html';
|
window.location.href = '/wallet-setup.html';
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
secureLogger.info('🔐 Security credentials found, verifying access...', { component: 'Router' });
|
secureLogger.info('🔐 Security credentials found, verifying access...', { component: 'Router' });
|
||||||
@ -1219,7 +1211,7 @@ async function handleSecurityKeyManagement(): Promise<boolean> {
|
|||||||
const credentials = await secureCredentialsService.retrieveCredentials('');
|
const credentials = await secureCredentialsService.retrieveCredentials('');
|
||||||
if (!credentials) {
|
if (!credentials) {
|
||||||
secureLogger.error('❌ Failed to access security credentials', { component: 'Router' });
|
secureLogger.error('❌ Failed to access security credentials', { component: 'Router' });
|
||||||
window.location.href = '/src/pages/wallet-setup/wallet-setup.html';
|
window.location.href = '/wallet-setup.html';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
secureLogger.info('✅ Security credentials verified', { component: 'Router' });
|
secureLogger.info('✅ Security credentials verified', { component: 'Router' });
|
||||||
@ -1228,7 +1220,7 @@ async function handleSecurityKeyManagement(): Promise<boolean> {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
secureLogger.error('❌ Security key management failed:', error, { component: 'Router' });
|
secureLogger.error('❌ Security key management failed:', error, { component: 'Router' });
|
||||||
secureLogger.info('🔐 Redirecting to security setup...', { component: 'Router' });
|
secureLogger.info('🔐 Redirecting to security setup...', { component: 'Router' });
|
||||||
window.location.href = '/src/pages/security-setup/security-setup.html';
|
window.location.href = '/security-setup.html';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1236,6 +1228,7 @@ async function handleSecurityKeyManagement(): Promise<boolean> {
|
|||||||
/**
|
/**
|
||||||
* ÉTAPE 5: Handshake
|
* ÉTAPE 5: Handshake
|
||||||
*/
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
async function performHandshake(_services: any): Promise<void> {
|
async function performHandshake(_services: any): Promise<void> {
|
||||||
secureLogger.info('🤝 Performing handshake...', { component: 'Router' });
|
secureLogger.info('🤝 Performing handshake...', { component: 'Router' });
|
||||||
|
|
||||||
@ -1252,6 +1245,7 @@ async function performHandshake(_services: any): Promise<void> {
|
|||||||
/**
|
/**
|
||||||
* ÉTAPE 6: Pairing
|
* ÉTAPE 6: Pairing
|
||||||
*/
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
async function handlePairing(services: any): Promise<void> {
|
async function handlePairing(services: any): Promise<void> {
|
||||||
secureLogger.info('🔗 Handling device pairing...', { component: 'Router' });
|
secureLogger.info('🔗 Handling device pairing...', { component: 'Router' });
|
||||||
|
|
||||||
@ -1276,6 +1270,7 @@ async function handlePairing(services: any): Promise<void> {
|
|||||||
/**
|
/**
|
||||||
* ÉTAPE 7: Écoute des processus
|
* ÉTAPE 7: Écoute des processus
|
||||||
*/
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
async function startProcessListening(services: any): Promise<void> {
|
async function startProcessListening(services: any): Promise<void> {
|
||||||
secureLogger.info('👂 Starting process listening...', { component: 'Router' });
|
secureLogger.info('👂 Starting process listening...', { component: 'Router' });
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-env browser, serviceworker */
|
||||||
const addResourcesToCache = async resources => {
|
const addResourcesToCache = async resources => {
|
||||||
const cache = await caches.open('v1');
|
const cache = await caches.open('v1');
|
||||||
await cache.addAll(resources);
|
await cache.addAll(resources);
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-env browser, serviceworker */
|
||||||
const EMPTY32BYTES = String('').padStart(64, '0');
|
const EMPTY32BYTES = String('').padStart(64, '0');
|
||||||
|
|
||||||
self.addEventListener('install', event => {
|
self.addEventListener('install', event => {
|
||||||
@ -109,10 +110,10 @@ const DATABASE_CONFIG = {
|
|||||||
async function openDatabase() {
|
async function openDatabase() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const request = indexedDB.open(DATABASE_CONFIG.name, DATABASE_CONFIG.version);
|
const request = indexedDB.open(DATABASE_CONFIG.name, DATABASE_CONFIG.version);
|
||||||
request.onerror = event => {
|
request.onerror = () => {
|
||||||
reject(request.error);
|
reject(request.error);
|
||||||
};
|
};
|
||||||
request.onsuccess = event => {
|
request.onsuccess = () => {
|
||||||
resolve(request.result);
|
resolve(request.result);
|
||||||
};
|
};
|
||||||
request.onupgradeneeded = event => {
|
request.onupgradeneeded = event => {
|
||||||
@ -180,6 +181,7 @@ async function getProcesses(processIds) {
|
|||||||
return results.filter(result => result !== undefined);
|
return results.filter(result => result !== undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
async function getAllDiffsNeedValidation() {
|
async function getAllDiffsNeedValidation() {
|
||||||
const db = await openDatabase();
|
const db = await openDatabase();
|
||||||
|
|
||||||
|
|||||||
@ -58,7 +58,7 @@ export class AsyncEncoderService {
|
|||||||
component: 'AsyncEncoderService',
|
component: 'AsyncEncoderService',
|
||||||
operation: 'initializeWorker'
|
operation: 'initializeWorker'
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch {
|
||||||
secureLogger.warn('Failed to initialize encoder worker, falling back to sync encoding', {
|
secureLogger.warn('Failed to initialize encoder worker, falling back to sync encoding', {
|
||||||
component: 'AsyncEncoderService',
|
component: 'AsyncEncoderService',
|
||||||
operation: 'initializeWorker'
|
operation: 'initializeWorker'
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
import { secureLogger } from '../secure-logger';
|
import { secureLogger } from '../secure-logger';
|
||||||
import { SecurityMode } from '../security-mode.service';
|
import { SecurityMode } from '../security-mode.service';
|
||||||
import { WebAuthnCredential, EncryptionResult } from './types';
|
import { WebAuthnCredential } from './types';
|
||||||
import { DATABASE_CONFIG } from '../database-config';
|
import { DATABASE_CONFIG } from '../database-config';
|
||||||
|
|
||||||
export class WebAuthnService {
|
export class WebAuthnService {
|
||||||
@ -74,7 +74,7 @@ export class WebAuthnService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Vérifier si on est dans un contexte sécurisé (requis pour WebAuthn)
|
// Vérifier si on est dans un contexte sécurisé (requis pour WebAuthn)
|
||||||
if (location.protocol !== 'https:' && location.hostname !== 'localhost') {
|
if (window.location.protocol !== 'https:' && window.location.hostname !== 'localhost') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,33 +93,33 @@ export class WebAuthnService {
|
|||||||
*/
|
*/
|
||||||
async detectProtonPass(): Promise<boolean> {
|
async detectProtonPass(): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
console.log('🔍 Detecting Proton Pass availability...');
|
secureLogger.debug('🔍 Detecting Proton Pass availability...', { component: 'WebAuthn' });
|
||||||
|
|
||||||
const available = await this.detectAvailableAuthenticators();
|
const available = await this.detectAvailableAuthenticators();
|
||||||
if (!available) {
|
if (!available) {
|
||||||
console.log('❌ WebAuthn not available');
|
secureLogger.debug('❌ WebAuthn not available', { component: 'WebAuthn' });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('✅ WebAuthn is available, checking Proton Pass support...');
|
secureLogger.debug('✅ WebAuthn is available, checking Proton Pass support...', { component: 'WebAuthn' });
|
||||||
|
|
||||||
// Vérifier la disponibilité sans faire d'appel réel à WebAuthn
|
// Vérifier la disponibilité sans faire d'appel réel à WebAuthn
|
||||||
// Juste vérifier que les APIs sont disponibles
|
// Juste vérifier que les APIs sont disponibles
|
||||||
if (!navigator.credentials?.create) {
|
if (!navigator.credentials?.create) {
|
||||||
console.log('❌ WebAuthn credentials API not available');
|
secureLogger.debug('❌ WebAuthn credentials API not available', { component: 'WebAuthn' });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vérifier si on est dans un contexte sécurisé (requis pour WebAuthn)
|
// Vérifier si on est dans un contexte sécurisé (requis pour WebAuthn)
|
||||||
if (location.protocol !== 'https:' && location.hostname !== 'localhost') {
|
if (window.location.protocol !== 'https:' && window.location.hostname !== 'localhost') {
|
||||||
console.log('❌ WebAuthn requires HTTPS or localhost');
|
secureLogger.debug('❌ WebAuthn requires HTTPS or localhost', { component: 'WebAuthn' });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('✅ Proton Pass should be available (basic checks passed)');
|
secureLogger.debug('✅ Proton Pass should be available (basic checks passed)', { component: 'WebAuthn' });
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('❌ Error detecting Proton Pass:', error);
|
secureLogger.error('❌ Error detecting Proton Pass:', error as Error, { component: 'WebAuthn' });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,10 +4,11 @@
|
|||||||
* Ce fichier centralise toutes les définitions de la base de données pour éviter
|
* Ce fichier centralise toutes les définitions de la base de données pour éviter
|
||||||
* les erreurs de version et d'incohérence entre les différents fichiers.
|
* les erreurs de version et d'incohérence entre les différents fichiers.
|
||||||
*/
|
*/
|
||||||
|
import { secureLogger } from './secure-logger';
|
||||||
|
|
||||||
export const DATABASE_CONFIG = {
|
export const DATABASE_CONFIG = {
|
||||||
name: '4nk',
|
name: '4nk',
|
||||||
version: 4,
|
version: 5,
|
||||||
stores: {
|
stores: {
|
||||||
wallet: {
|
wallet: {
|
||||||
name: 'wallet',
|
name: 'wallet',
|
||||||
@ -75,18 +76,18 @@ export async function openDatabase(): Promise<IDBDatabase> {
|
|||||||
const request = indexedDB.open(DATABASE_CONFIG.name, DATABASE_CONFIG.version);
|
const request = indexedDB.open(DATABASE_CONFIG.name, DATABASE_CONFIG.version);
|
||||||
|
|
||||||
request.onerror = () => {
|
request.onerror = () => {
|
||||||
console.error(`❌ Failed to open database ${DATABASE_CONFIG.name}:`, request.error);
|
secureLogger.error(`Failed to open database ${DATABASE_CONFIG.name}`, request.error as Error, { component: 'DatabaseConfig' });
|
||||||
reject(request.error);
|
reject(request.error);
|
||||||
};
|
};
|
||||||
|
|
||||||
request.onsuccess = () => {
|
request.onsuccess = () => {
|
||||||
console.log(`✅ Database ${DATABASE_CONFIG.name} opened successfully`);
|
secureLogger.info(`Database ${DATABASE_CONFIG.name} opened successfully`, { component: 'DatabaseConfig' });
|
||||||
resolve(request.result);
|
resolve(request.result);
|
||||||
};
|
};
|
||||||
|
|
||||||
request.onupgradeneeded = () => {
|
request.onupgradeneeded = () => {
|
||||||
const db = request.result;
|
const db = request.result;
|
||||||
console.log(`🔄 Database upgrade needed for ${DATABASE_CONFIG.name} version ${DATABASE_CONFIG.version}`);
|
secureLogger.info(`Database upgrade needed for ${DATABASE_CONFIG.name} version ${DATABASE_CONFIG.version}`, { component: 'DatabaseConfig' });
|
||||||
|
|
||||||
// Créer les stores manquants
|
// Créer les stores manquants
|
||||||
Object.values(DATABASE_CONFIG.stores).forEach(storeConfig => {
|
Object.values(DATABASE_CONFIG.stores).forEach(storeConfig => {
|
||||||
@ -106,9 +107,9 @@ export async function openDatabase(): Promise<IDBDatabase> {
|
|||||||
store.createIndex(indexConfig.name, indexConfig.keyPath, { unique: indexConfig.unique || false });
|
store.createIndex(indexConfig.name, indexConfig.keyPath, { unique: indexConfig.unique || false });
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`✅ Created store: ${storeConfig.name}`);
|
secureLogger.info(`Created store: ${storeConfig.name}`, { component: 'DatabaseConfig' });
|
||||||
} else {
|
} else {
|
||||||
console.log(`✅ Store already exists: ${storeConfig.name}`);
|
secureLogger.info(`Store already exists: ${storeConfig.name}`, { component: 'DatabaseConfig' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import Services from './service';
|
import Services from './service';
|
||||||
import { DATABASE_CONFIG } from './database-config';
|
import { DATABASE_CONFIG } from './database-config';
|
||||||
|
import { secureLogger } from './secure-logger';
|
||||||
|
|
||||||
export class Database {
|
export class Database {
|
||||||
private static instance: Database;
|
private static instance: Database;
|
||||||
@ -94,7 +95,7 @@ export class Database {
|
|||||||
};
|
};
|
||||||
|
|
||||||
request.onerror = () => {
|
request.onerror = () => {
|
||||||
console.error('Database error:', request.error);
|
secureLogger.error('Database error:', request.error as Error, { component: 'Database' });
|
||||||
reject(request.error);
|
reject(request.error);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -117,7 +118,7 @@ export class Database {
|
|||||||
|
|
||||||
public async registerServiceWorker(path: string) {
|
public async registerServiceWorker(path: string) {
|
||||||
if (!('serviceWorker' in navigator)) {return;} // Ensure service workers are supported
|
if (!('serviceWorker' in navigator)) {return;} // Ensure service workers are supported
|
||||||
console.log('registering worker at', path);
|
secureLogger.debug('Registering worker at', { component: 'Database', path });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get existing service worker registrations
|
// Get existing service worker registrations
|
||||||
@ -127,7 +128,7 @@ export class Database {
|
|||||||
this.serviceWorkerRegistration = await navigator.serviceWorker.register(path, {
|
this.serviceWorkerRegistration = await navigator.serviceWorker.register(path, {
|
||||||
type: 'module',
|
type: 'module',
|
||||||
});
|
});
|
||||||
console.log('Service Worker registered with scope:', this.serviceWorkerRegistration.scope);
|
secureLogger.info('Service Worker registered with scope:', { component: 'Database', scope: this.serviceWorkerRegistration.scope });
|
||||||
|
|
||||||
// Show spinner during service worker initialization
|
// Show spinner during service worker initialization
|
||||||
this.showServiceWorkerSpinner('Initializing database service...');
|
this.showServiceWorkerSpinner('Initializing database service...');
|
||||||
@ -135,16 +136,16 @@ export class Database {
|
|||||||
// One existing worker: update it (restart it) without unregistering.
|
// One existing worker: update it (restart it) without unregistering.
|
||||||
this.serviceWorkerRegistration = registrations[0];
|
this.serviceWorkerRegistration = registrations[0];
|
||||||
await this.serviceWorkerRegistration.update();
|
await this.serviceWorkerRegistration.update();
|
||||||
console.log('Service Worker updated');
|
secureLogger.info('Service Worker updated', { component: 'Database' });
|
||||||
} else {
|
} else {
|
||||||
// More than one existing worker: unregister them all and register a new one.
|
// More than one existing worker: unregister them all and register a new one.
|
||||||
console.log('Multiple Service Worker(s) detected. Unregistering all...');
|
secureLogger.warn('Multiple Service Worker(s) detected. Unregistering all...', { component: 'Database' });
|
||||||
await Promise.all(registrations.map(reg => reg.unregister()));
|
await Promise.all(registrations.map(reg => reg.unregister()));
|
||||||
console.log('All previous Service Workers unregistered.');
|
secureLogger.info('All previous Service Workers unregistered', { component: 'Database' });
|
||||||
this.serviceWorkerRegistration = await navigator.serviceWorker.register(path, {
|
this.serviceWorkerRegistration = await navigator.serviceWorker.register(path, {
|
||||||
type: 'module',
|
type: 'module',
|
||||||
});
|
});
|
||||||
console.log('Service Worker registered with scope:', this.serviceWorkerRegistration.scope);
|
secureLogger.info('Service Worker registered with scope:', { component: 'Database', scope: this.serviceWorkerRegistration.scope });
|
||||||
|
|
||||||
// Show spinner during service worker initialization
|
// Show spinner during service worker initialization
|
||||||
this.showServiceWorkerSpinner('Initializing database service...');
|
this.showServiceWorkerSpinner('Initializing database service...');
|
||||||
@ -156,9 +157,9 @@ export class Database {
|
|||||||
this.checkForUpdates(),
|
this.checkForUpdates(),
|
||||||
new Promise((_, reject) => setTimeout(() => reject(new Error('Update timeout')), 5000)),
|
new Promise((_, reject) => setTimeout(() => reject(new Error('Update timeout')), 5000)),
|
||||||
]);
|
]);
|
||||||
console.log('✅ Service worker updates completed');
|
secureLogger.info('✅ Service worker updates completed', { component: 'Database' });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('⚠️ Service worker update failed or timed out:', error);
|
secureLogger.warn('⚠️ Service worker update failed or timed out:', { component: 'Database', error: String(error) });
|
||||||
// Continue anyway - don't block the initialization
|
// Continue anyway - don't block the initialization
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,11 +170,11 @@ export class Database {
|
|||||||
|
|
||||||
// Hide spinner once service worker is ready
|
// Hide spinner once service worker is ready
|
||||||
this.hideServiceWorkerSpinner();
|
this.hideServiceWorkerSpinner();
|
||||||
console.log('✅ Service worker initialization completed');
|
secureLogger.info('✅ Service worker initialization completed', { component: 'Database' });
|
||||||
|
|
||||||
// Set up a global message listener for responses from the service worker.
|
// Set up a global message listener for responses from the service worker.
|
||||||
navigator.serviceWorker.addEventListener('message', async event => {
|
navigator.serviceWorker.addEventListener('message', async event => {
|
||||||
console.log('Received message from service worker:', event.data);
|
secureLogger.debug('Received message from service worker:', { component: 'Database', data: event.data });
|
||||||
await this.handleServiceWorkerMessage(event.data);
|
await this.handleServiceWorkerMessage(event.data);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -193,13 +194,13 @@ export class Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('Service worker scan failed:', error);
|
secureLogger.warn('Service worker scan failed:', { component: 'Database', error: String(error) });
|
||||||
// Continue the interval even if one scan fails
|
// Continue the interval even if one scan fails
|
||||||
}
|
}
|
||||||
}, 5000);
|
}, 5000);
|
||||||
}, 10000); // Wait 10 seconds before starting the interval
|
}, 10000); // Wait 10 seconds before starting the interval
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Service Worker registration failed:', error);
|
secureLogger.error('Service Worker registration failed:', error as Error, { component: 'Database' });
|
||||||
this.hideServiceWorkerSpinner();
|
this.hideServiceWorkerSpinner();
|
||||||
throw error; // Re-throw to be handled by the caller
|
throw error; // Re-throw to be handled by the caller
|
||||||
}
|
}
|
||||||
@ -211,7 +212,7 @@ export class Database {
|
|||||||
): Promise<ServiceWorker | null> {
|
): Promise<ServiceWorker | null> {
|
||||||
return new Promise((resolve, _reject) => {
|
return new Promise((resolve, _reject) => {
|
||||||
// Use reject parameter
|
// Use reject parameter
|
||||||
console.log('Service worker activation promise created');
|
secureLogger.debug('Service worker activation promise created', { component: 'Database' });
|
||||||
if (registration.active) {
|
if (registration.active) {
|
||||||
resolve(registration.active);
|
resolve(registration.active);
|
||||||
return;
|
return;
|
||||||
@ -220,7 +221,7 @@ export class Database {
|
|||||||
// Set a timeout to prevent infinite waiting
|
// Set a timeout to prevent infinite waiting
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
navigator.serviceWorker.removeEventListener('controllerchange', listener);
|
navigator.serviceWorker.removeEventListener('controllerchange', listener);
|
||||||
console.warn('Service worker activation timeout');
|
secureLogger.warn('Service worker activation timeout', { component: 'Database' });
|
||||||
resolve(null); // Return null instead of rejecting to allow continuation
|
resolve(null); // Return null instead of rejecting to allow continuation
|
||||||
}, 15000); // 15 second timeout
|
}, 15000); // 15 second timeout
|
||||||
|
|
||||||
@ -250,7 +251,7 @@ export class Database {
|
|||||||
this.serviceWorkerRegistration.waiting.postMessage({ type: 'SKIP_WAITING' });
|
this.serviceWorkerRegistration.waiting.postMessage({ type: 'SKIP_WAITING' });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error checking for service worker updates:', error);
|
secureLogger.error('Error checking for service worker updates:', error as Error, { component: 'Database' });
|
||||||
throw error; // Re-throw to be caught by the calling function
|
throw error; // Re-throw to be caught by the calling function
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -262,7 +263,7 @@ export class Database {
|
|||||||
await this.handleDownloadList(message.data);
|
await this.handleDownloadList(message.data);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.warn('Unknown message type received from service worker:', message);
|
secureLogger.warn('Unknown message type received from service worker:', { component: 'Database', message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,7 +275,7 @@ export class Database {
|
|||||||
const diff = await service.getDiffByValue(hash);
|
const diff = await service.getDiffByValue(hash);
|
||||||
if (!diff) {
|
if (!diff) {
|
||||||
// This should never happen
|
// This should never happen
|
||||||
console.warn(`Missing a diff for hash ${hash}`);
|
secureLogger.warn(`Missing a diff for hash ${hash}`, { component: 'Database' });
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const processId = diff.process_id;
|
const processId = diff.process_id;
|
||||||
@ -297,7 +298,7 @@ export class Database {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// We first request the data from managers
|
// We first request the data from managers
|
||||||
console.log('Request data from managers of the process');
|
secureLogger.debug('Request data from managers of the process', { component: 'Database' });
|
||||||
// get the diff from db
|
// get the diff from db
|
||||||
if (!requestedStateId.includes(stateId)) {
|
if (!requestedStateId.includes(stateId)) {
|
||||||
await service.requestDataFromPeers(processId, [stateId], [roles]);
|
await service.requestDataFromPeers(processId, [stateId], [roles]);
|
||||||
@ -305,21 +306,20 @@ export class Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
secureLogger.error('Error downloading missing data:', e as Error, { component: 'Database' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleAddObjectResponse = async (event: MessageEvent) => {
|
private handleAddObjectResponse = async (event: MessageEvent) => {
|
||||||
// Use event parameter
|
secureLogger.debug('Add object response received', { component: 'Database' });
|
||||||
console.log('Add object response:', event);
|
|
||||||
const data = event.data;
|
const data = event.data;
|
||||||
console.log('Received response from service worker (ADD_OBJECT):', data);
|
secureLogger.debug('Received response from service worker (ADD_OBJECT):', { component: 'Database', data });
|
||||||
const service = await Services.getInstance();
|
const service = await Services.getInstance();
|
||||||
if (data.type === 'NOTIFICATIONS') {
|
if (data.type === 'NOTIFICATIONS') {
|
||||||
service.setNotifications(data.data);
|
service.setNotifications(data.data);
|
||||||
} else if (data.type === 'TO_DOWNLOAD') {
|
} else if (data.type === 'TO_DOWNLOAD') {
|
||||||
console.log(`Received missing data ${data}`);
|
secureLogger.debug(`Received missing data`, { component: 'Database', data });
|
||||||
// Download the missing data
|
// Download the missing data
|
||||||
const requestedStateId: string[] = [];
|
const requestedStateId: string[] = [];
|
||||||
for (const hash of data.data) {
|
for (const hash of data.data) {
|
||||||
@ -331,7 +331,7 @@ export class Database {
|
|||||||
await service.saveBlobToDb(hash, blob);
|
await service.saveBlobToDb(hash, blob);
|
||||||
} else {
|
} else {
|
||||||
// We first request the data from managers
|
// We first request the data from managers
|
||||||
console.log('Request data from managers of the process');
|
secureLogger.debug('Request data from managers of the process', { component: 'Database' });
|
||||||
// get the diff from db
|
// get the diff from db
|
||||||
const diff = await service.getDiffByValue(hash);
|
const diff = await service.getDiffByValue(hash);
|
||||||
if (diff === null) {
|
if (diff === null) {
|
||||||
@ -346,16 +346,14 @@ export class Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
secureLogger.error('Error downloading missing data:', e as Error, { component: 'Database' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleGetObjectResponse = (event: MessageEvent) => {
|
private handleGetObjectResponse = (event: MessageEvent) => {
|
||||||
console.log('Received response from service worker (GET_OBJECT):', event.data);
|
secureLogger.debug('Received response from service worker (GET_OBJECT):', { component: 'Database', data: event.data });
|
||||||
// Use event parameter
|
|
||||||
console.log('Get object response event:', event);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public addObject(payload: { storeName: string; object: any; key: any }): Promise<void> {
|
public addObject(payload: { storeName: string; object: any; key: any }): Promise<void> {
|
||||||
@ -368,10 +366,10 @@ export class Database {
|
|||||||
await this.addObjectAttempt(payload);
|
await this.addObjectAttempt(payload);
|
||||||
return; // Success, exit retry loop
|
return; // Success, exit retry loop
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(`Attempt ${attempt}/${maxRetries} failed for addObject:`, error);
|
secureLogger.warn(`Attempt ${attempt}/${maxRetries} failed for addObject:`, { component: 'Database', error: String(error) });
|
||||||
|
|
||||||
if (attempt === maxRetries) {
|
if (attempt === maxRetries) {
|
||||||
console.error('All retry attempts failed for addObject');
|
secureLogger.error('All retry attempts failed for addObject', { component: 'Database' });
|
||||||
throw new Error(`Failed to add object after ${maxRetries} attempts: ${error}`);
|
throw new Error(`Failed to add object after ${maxRetries} attempts: ${error}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -381,23 +379,23 @@ export class Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private addObjectAttempt(payload: { storeName: string; object: any; key: any }): Promise<void> {
|
private async addObjectAttempt(payload: { storeName: string; object: any; key: any }): Promise<void> {
|
||||||
return new Promise(async (resolve, reject) => {
|
// Check if the service worker is active
|
||||||
|
if (!this.serviceWorkerRegistration) {
|
||||||
|
secureLogger.debug('Service worker registration not ready, waiting...', { component: 'Database' });
|
||||||
|
this.serviceWorkerRegistration = await navigator.serviceWorker.ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeWorker = await this.waitForServiceWorkerActivation(
|
||||||
|
this.serviceWorkerRegistration
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!activeWorker) {
|
||||||
|
throw new Error('Service worker not available');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
// Check if the service worker is active
|
|
||||||
if (!this.serviceWorkerRegistration) {
|
|
||||||
console.log('Service worker registration not ready, waiting...');
|
|
||||||
this.serviceWorkerRegistration = await navigator.serviceWorker.ready;
|
|
||||||
}
|
|
||||||
|
|
||||||
const activeWorker = await this.waitForServiceWorkerActivation(
|
|
||||||
this.serviceWorkerRegistration
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!activeWorker) {
|
|
||||||
throw new Error('Service worker not available');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a message channel for communication
|
// Create a message channel for communication
|
||||||
const messageChannel = new MessageChannel();
|
const messageChannel = new MessageChannel();
|
||||||
|
|
||||||
@ -447,10 +445,10 @@ export class Database {
|
|||||||
await this.batchWritingAttempt(payload);
|
await this.batchWritingAttempt(payload);
|
||||||
return; // Success, exit retry loop
|
return; // Success, exit retry loop
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(`Attempt ${attempt}/${maxRetries} failed for batchWriting:`, error);
|
secureLogger.warn(`Attempt ${attempt}/${maxRetries} failed for batchWriting:`, { component: 'Database', error: String(error) });
|
||||||
|
|
||||||
if (attempt === maxRetries) {
|
if (attempt === maxRetries) {
|
||||||
console.error('All retry attempts failed for batchWriting');
|
secureLogger.error('All retry attempts failed for batchWriting', { component: 'Database' });
|
||||||
throw new Error(`Failed to batch write objects after ${maxRetries} attempts: ${error}`);
|
throw new Error(`Failed to batch write objects after ${maxRetries} attempts: ${error}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -464,21 +462,28 @@ export class Database {
|
|||||||
storeName: string;
|
storeName: string;
|
||||||
objects: { key: any; object: any }[];
|
objects: { key: any; object: any }[];
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
return new Promise(async (resolve, reject) => {
|
return this.batchWritingInternal(payload);
|
||||||
try {
|
}
|
||||||
if (!this.serviceWorkerRegistration) {
|
|
||||||
console.log('Service worker registration not ready, waiting...');
|
|
||||||
this.serviceWorkerRegistration = await navigator.serviceWorker.ready;
|
|
||||||
}
|
|
||||||
|
|
||||||
const activeWorker = await this.waitForServiceWorkerActivation(
|
private async batchWritingInternal(payload: {
|
||||||
this.serviceWorkerRegistration
|
storeName: string;
|
||||||
);
|
objects: { key: any; object: any }[];
|
||||||
|
}): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (!this.serviceWorkerRegistration) {
|
||||||
|
secureLogger.debug('Service worker registration not ready, waiting...', { component: 'Database' });
|
||||||
|
this.serviceWorkerRegistration = await navigator.serviceWorker.ready;
|
||||||
|
}
|
||||||
|
|
||||||
if (!activeWorker) {
|
const activeWorker = await this.waitForServiceWorkerActivation(
|
||||||
throw new Error('Service worker not available');
|
this.serviceWorkerRegistration
|
||||||
}
|
);
|
||||||
|
|
||||||
|
if (!activeWorker) {
|
||||||
|
throw new Error('Service worker not available');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
const messageChannel = new MessageChannel();
|
const messageChannel = new MessageChannel();
|
||||||
|
|
||||||
// Set timeout for the operation
|
// Set timeout for the operation
|
||||||
@ -503,14 +508,14 @@ export class Database {
|
|||||||
},
|
},
|
||||||
[messageChannel.port2]
|
[messageChannel.port2]
|
||||||
);
|
);
|
||||||
} catch (error) {
|
});
|
||||||
reject(new Error(`Failed to send message to service worker: ${error}`));
|
} catch (error) {
|
||||||
}
|
throw new Error(`Failed to send message to service worker: ${error}`);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getObject(storeName: string, key: string): Promise<any | null> {
|
public async getObject(storeName: string, key: string): Promise<any | null> {
|
||||||
console.log(`🔍 DEBUG: Database.getObject - storeName: ${storeName}, key: ${key}`);
|
secureLogger.debug(`Database.getObject - storeName: ${storeName}, key: ${key}`, { component: 'Database' });
|
||||||
|
|
||||||
// Utiliser directement IndexedDB au lieu du service worker pour éviter les problèmes de synchronisation
|
// Utiliser directement IndexedDB au lieu du service worker pour éviter les problèmes de synchronisation
|
||||||
const db = await new Promise<IDBDatabase>((resolve, reject) => {
|
const db = await new Promise<IDBDatabase>((resolve, reject) => {
|
||||||
@ -519,22 +524,22 @@ export class Database {
|
|||||||
request.onerror = () => reject(request.error);
|
request.onerror = () => reject(request.error);
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`🔍 DEBUG: Database.getObject - db obtained directly, objectStoreNames:`, Array.from(db.objectStoreNames));
|
secureLogger.debug(`Database.getObject - db obtained directly`, { component: 'Database', objectStoreNames: Array.from(db.objectStoreNames) });
|
||||||
const tx = db.transaction(storeName, 'readonly');
|
const tx = db.transaction(storeName, 'readonly');
|
||||||
const store = tx.objectStore(storeName);
|
const store = tx.objectStore(storeName);
|
||||||
console.log(`🔍 DEBUG: Database.getObject - store opened: ${store.name}`);
|
secureLogger.debug(`Database.getObject - store opened: ${store.name}`, { component: 'Database' });
|
||||||
const result = await new Promise((resolve, reject) => {
|
const result = await new Promise((resolve, reject) => {
|
||||||
const getRequest = store.get(key);
|
const getRequest = store.get(key);
|
||||||
getRequest.onsuccess = () => {
|
getRequest.onsuccess = () => {
|
||||||
console.log(`🔍 DEBUG: Database.getObject - getRequest success, result:`, getRequest.result);
|
secureLogger.debug(`Database.getObject - getRequest success`, { component: 'Database', hasResult: !!getRequest.result });
|
||||||
resolve(getRequest.result);
|
resolve(getRequest.result);
|
||||||
};
|
};
|
||||||
getRequest.onerror = () => {
|
getRequest.onerror = () => {
|
||||||
console.log(`🔍 DEBUG: Database.getObject - getRequest error:`, getRequest.error);
|
secureLogger.error(`Database.getObject - getRequest error`, getRequest.error as Error, { component: 'Database' });
|
||||||
reject(getRequest.error);
|
reject(getRequest.error);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
console.log(`🔍 DEBUG: Database.getObject - final result:`, result);
|
secureLogger.debug(`Database.getObject - final result`, { component: 'Database', hasResult: !!result });
|
||||||
return result ?? null; // Convert undefined to null
|
return result ?? null; // Convert undefined to null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -564,7 +569,7 @@ export class Database {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching data from IndexedDB:', error);
|
secureLogger.error('Error fetching data from IndexedDB:', error as Error, { component: 'Database' });
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
import { DATABASE_CONFIG } from './database-config';
|
import { DATABASE_CONFIG } from './database-config';
|
||||||
import { SecureCredentialsService } from './secure-credentials.service';
|
import { SecureCredentialsService } from './secure-credentials.service';
|
||||||
import { EncryptionService } from './encryption.service';
|
import { EncryptionService } from './encryption.service';
|
||||||
|
import { secureLogger } from './secure-logger';
|
||||||
|
|
||||||
// Type simplifié pour éviter les problèmes d'import du SDK
|
// Type simplifié pour éviter les problèmes d'import du SDK
|
||||||
export interface Device {
|
export interface Device {
|
||||||
@ -38,7 +39,7 @@ export class DeviceReaderService {
|
|||||||
* Version légère sans nécessiter WebAssembly
|
* Version légère sans nécessiter WebAssembly
|
||||||
*/
|
*/
|
||||||
async getDeviceFromDatabase(): Promise<Device | null> {
|
async getDeviceFromDatabase(): Promise<Device | null> {
|
||||||
console.log('🔍 DeviceReaderService: Reading device from database...');
|
secureLogger.debug('🔍 DeviceReaderService: Reading device from database...', { component: 'DeviceReader' });
|
||||||
|
|
||||||
// Utiliser directement IndexedDB
|
// Utiliser directement IndexedDB
|
||||||
const db = await new Promise<IDBDatabase>((resolve, reject) => {
|
const db = await new Promise<IDBDatabase>((resolve, reject) => {
|
||||||
@ -59,14 +60,14 @@ export class DeviceReaderService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!dbRes) {
|
if (!dbRes) {
|
||||||
console.log('🔍 DeviceReaderService: No device found in database');
|
secureLogger.debug('🔍 DeviceReaderService: No device found in database', { component: 'DeviceReader' });
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if data is encrypted (new format) or plain (old format)
|
// Check if data is encrypted (new format) or plain (old format)
|
||||||
if (dbRes['encrypted_device']) {
|
if (dbRes['encrypted_device']) {
|
||||||
// New encrypted format - need to decrypt
|
// New encrypted format - need to decrypt
|
||||||
console.log('🔐 DeviceReaderService: Device found in encrypted format, decrypting...');
|
secureLogger.debug('🔐 DeviceReaderService: Device found in encrypted format, decrypting...', { component: 'DeviceReader' });
|
||||||
|
|
||||||
// Get the PBKDF2 key based on security mode
|
// Get the PBKDF2 key based on security mode
|
||||||
const secureCredentialsService = SecureCredentialsService.getInstance();
|
const secureCredentialsService = SecureCredentialsService.getInstance();
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
* EventBus - Système de communication découplé
|
* EventBus - Système de communication découplé
|
||||||
* Permet la communication entre services sans couplage direct
|
* Permet la communication entre services sans couplage direct
|
||||||
*/
|
*/
|
||||||
|
import { secureLogger } from './secure-logger';
|
||||||
export interface EventData {
|
export interface EventData {
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
@ -46,7 +47,7 @@ export class EventBus {
|
|||||||
|
|
||||||
// Vérifier la limite d'écouteurs
|
// Vérifier la limite d'écouteurs
|
||||||
if (eventListeners.length >= this.maxListeners) {
|
if (eventListeners.length >= this.maxListeners) {
|
||||||
console.warn(`Maximum listeners (${this.maxListeners}) reached for event: ${event}`);
|
secureLogger.warn(`Maximum listeners (${this.maxListeners}) reached for event: ${event}`, { component: 'EventBus' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +107,7 @@ export class EventBus {
|
|||||||
try {
|
try {
|
||||||
listener(data);
|
listener(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error in event listener for ${event}:`, error);
|
secureLogger.error(`Error in event listener for ${event}:`, error as Error, { component: 'EventBus' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -126,7 +127,7 @@ export class EventBus {
|
|||||||
const result = listener(data);
|
const result = listener(data);
|
||||||
return Promise.resolve(result);
|
return Promise.resolve(result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error in async event listener for ${event}:`, error);
|
secureLogger.error(`Error in async event listener for ${event}:`, error as Error, { component: 'EventBus' });
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import {
|
|||||||
discoverAndJoinPairingProcessWithWords,
|
discoverAndJoinPairingProcessWithWords,
|
||||||
prepareAndSendPairingTx,
|
prepareAndSendPairingTx,
|
||||||
} from '../utils/sp-address.utils';
|
} from '../utils/sp-address.utils';
|
||||||
|
import { secureLogger } from './secure-logger';
|
||||||
|
|
||||||
export default class IframePairingService {
|
export default class IframePairingService {
|
||||||
private static instance: IframePairingService;
|
private static instance: IframePairingService;
|
||||||
@ -29,7 +30,7 @@ export default class IframePairingService {
|
|||||||
// Detect if we're in an iframe
|
// Detect if we're in an iframe
|
||||||
if (window.parent !== window) {
|
if (window.parent !== window) {
|
||||||
this.parentWindow = window.parent;
|
this.parentWindow = window.parent;
|
||||||
console.log('🔗 Iframe pairing service initialized');
|
secureLogger.info('🔗 Iframe pairing service initialized', { component: 'IframePairing' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,14 +50,14 @@ export default class IframePairingService {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error handling iframe pairing message:', error);
|
secureLogger.error('Error handling iframe pairing message:', error as Error, { component: 'IframePairing' });
|
||||||
this.sendMessage(MessageType.PAIRING_4WORDS_ERROR, { error: (error as Error).message });
|
this.sendMessage(MessageType.PAIRING_4WORDS_ERROR, { error: (error as Error).message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleCreatePairing(_data: any) {
|
private async handleCreatePairing(_data: any) {
|
||||||
try {
|
try {
|
||||||
console.log('🔐 Creating pairing process via iframe...');
|
secureLogger.info('🔐 Creating pairing process via iframe...', { component: 'IframePairing' });
|
||||||
this._isCreator = true;
|
this._isCreator = true;
|
||||||
|
|
||||||
// Update status
|
// Update status
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
* MemoryManager - Gestion intelligente de la mémoire
|
* MemoryManager - Gestion intelligente de la mémoire
|
||||||
* Surveille et optimise l'utilisation mémoire de l'application
|
* Surveille et optimise l'utilisation mémoire de l'application
|
||||||
*/
|
*/
|
||||||
|
import { secureLogger } from './secure-logger';
|
||||||
export interface MemoryStats {
|
export interface MemoryStats {
|
||||||
usedJSHeapSize: number;
|
usedJSHeapSize: number;
|
||||||
totalJSHeapSize: number;
|
totalJSHeapSize: number;
|
||||||
@ -188,7 +189,7 @@ export class MemoryManager {
|
|||||||
* Effectue un nettoyage de mémoire
|
* Effectue un nettoyage de mémoire
|
||||||
*/
|
*/
|
||||||
private performMemoryCleanup(): void {
|
private performMemoryCleanup(): void {
|
||||||
console.log('🧹 Performing memory cleanup...');
|
secureLogger.info('🧹 Performing memory cleanup...', { component: 'MemoryManager' });
|
||||||
|
|
||||||
// Nettoyer les caches expirés
|
// Nettoyer les caches expirés
|
||||||
this.cleanupExpiredEntries();
|
this.cleanupExpiredEntries();
|
||||||
@ -277,7 +278,8 @@ export class MemoryManager {
|
|||||||
private logMemoryStats(): void {
|
private logMemoryStats(): void {
|
||||||
const stats = this.getMemoryStats();
|
const stats = this.getMemoryStats();
|
||||||
if (stats) {
|
if (stats) {
|
||||||
console.log('📊 Memory Stats:', {
|
secureLogger.debug('📊 Memory Stats', {
|
||||||
|
component: 'MemoryManager',
|
||||||
used: `${Math.round(stats.usedJSHeapSize / 1024 / 1024)}MB`,
|
used: `${Math.round(stats.usedJSHeapSize / 1024 / 1024)}MB`,
|
||||||
total: `${Math.round(stats.totalJSHeapSize / 1024 / 1024)}MB`,
|
total: `${Math.round(stats.totalJSHeapSize / 1024 / 1024)}MB`,
|
||||||
limit: `${Math.round(stats.jsHeapSizeLimit / 1024 / 1024)}MB`,
|
limit: `${Math.round(stats.jsHeapSizeLimit / 1024 / 1024)}MB`,
|
||||||
@ -298,8 +300,7 @@ export class MemoryManager {
|
|||||||
const caches: Record<string, { size: number; entries: any[] }> = {};
|
const caches: Record<string, { size: number; entries: any[] }> = {};
|
||||||
|
|
||||||
this.caches.forEach((cache, name) => {
|
this.caches.forEach((cache, name) => {
|
||||||
// Use cache variable
|
secureLogger.debug(`Cache stats for ${name}`, { component: 'MemoryManager', cacheSize: cache.size });
|
||||||
console.log('Cache:', cache);
|
|
||||||
caches[name] = this.getCacheStats(name);
|
caches[name] = this.getCacheStats(name);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { addressToEmoji } from '../utils/sp-address.utils';
|
|||||||
import { RoleDefinition } from 'pkg/sdk_client';
|
import { RoleDefinition } from 'pkg/sdk_client';
|
||||||
import { initValidationModal } from '../components/validation-modal/validation-modal';
|
import { initValidationModal } from '../components/validation-modal/validation-modal';
|
||||||
import { interpolate } from '../utils/html.utils';
|
import { interpolate } from '../utils/html.utils';
|
||||||
|
import { secureLogger } from './secure-logger';
|
||||||
|
|
||||||
interface ConfirmationModalOptions {
|
interface ConfirmationModalOptions {
|
||||||
title: string;
|
title: string;
|
||||||
@ -97,7 +98,7 @@ export default class ModalService {
|
|||||||
throw new Error('Must have exactly 1 member');
|
throw new Error('Must have exactly 1 member');
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('MEMBERS:', members);
|
secureLogger.debug('Modal members list', { component: 'ModalService', membersCount: members.length });
|
||||||
// We take all the addresses except our own
|
// We take all the addresses except our own
|
||||||
const service = await Services.getInstance();
|
const service = await Services.getInstance();
|
||||||
const localAddress = service.getDeviceAddress();
|
const localAddress = service.getDeviceAddress();
|
||||||
@ -126,7 +127,7 @@ export default class ModalService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
confirmLogin() {
|
confirmLogin() {
|
||||||
console.log('=============> Confirm Login');
|
secureLogger.info('Confirm Login action triggered', { component: 'ModalService' });
|
||||||
}
|
}
|
||||||
async closeLoginModal() {
|
async closeLoginModal() {
|
||||||
if (this.modal) {this.modal.style.display = 'none';}
|
if (this.modal) {this.modal.style.display = 'none';}
|
||||||
|
|||||||
@ -27,8 +27,7 @@ export class PairingService {
|
|||||||
private processRepo: ProcessRepository,
|
private processRepo: ProcessRepository,
|
||||||
private sdkClient: any
|
private sdkClient: any
|
||||||
) {
|
) {
|
||||||
// Use parameters
|
secureLogger.debug('Pairing service constructor initialized', { component: 'PairingService' });
|
||||||
console.log('Pairing service constructor:', { deviceRepo: this.deviceRepo, processRepo: this.processRepo, sdkClient: this.sdkClient });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -82,8 +81,6 @@ export class PairingService {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
||||||
// Use errorMessage variable
|
|
||||||
console.log('Pairing error:', errorMessage);
|
|
||||||
|
|
||||||
secureLogger.error('Failed to create pairing process', error as Error, {
|
secureLogger.error('Failed to create pairing process', error as Error, {
|
||||||
component: 'PairingService',
|
component: 'PairingService',
|
||||||
@ -131,8 +128,6 @@ export class PairingService {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
||||||
// Use errorMessage variable
|
|
||||||
console.log('Pairing error:', errorMessage);
|
|
||||||
|
|
||||||
secureLogger.error('Failed to join pairing process', error as Error, {
|
secureLogger.error('Failed to join pairing process', error as Error, {
|
||||||
component: 'PairingService',
|
component: 'PairingService',
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
* PerformanceMonitor - Surveillance des performances
|
* PerformanceMonitor - Surveillance des performances
|
||||||
* Mesure et optimise les performances de l'application
|
* Mesure et optimise les performances de l'application
|
||||||
*/
|
*/
|
||||||
|
import { secureLogger } from './secure-logger';
|
||||||
export interface PerformanceMetric {
|
export interface PerformanceMetric {
|
||||||
name: string;
|
name: string;
|
||||||
value: number;
|
value: number;
|
||||||
@ -54,7 +55,7 @@ export class PerformanceMonitor {
|
|||||||
if (this.isMonitoring) {return;}
|
if (this.isMonitoring) {return;}
|
||||||
|
|
||||||
this.isMonitoring = true;
|
this.isMonitoring = true;
|
||||||
console.log('📊 Performance monitoring started');
|
secureLogger.info('📊 Performance monitoring started', { component: 'PerformanceMonitor' });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,15 +65,13 @@ export class PerformanceMonitor {
|
|||||||
this.isMonitoring = false;
|
this.isMonitoring = false;
|
||||||
this.observers.forEach(observer => observer.disconnect());
|
this.observers.forEach(observer => observer.disconnect());
|
||||||
this.observers = [];
|
this.observers = [];
|
||||||
console.log('📊 Performance monitoring stopped');
|
secureLogger.info('📊 Performance monitoring stopped', { component: 'PerformanceMonitor' });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enregistre une métrique de performance
|
* Enregistre une métrique de performance
|
||||||
*/
|
*/
|
||||||
recordMetric(name: string, value: number, unit: string = 'ms'): void {
|
recordMetric(name: string, value: number, _unit: string = 'ms'): void {
|
||||||
// Use unit parameter
|
|
||||||
console.log('Performance metric unit:', unit);
|
|
||||||
if (!this.isMonitoring) {return;}
|
if (!this.isMonitoring) {return;}
|
||||||
|
|
||||||
if (!this.metrics.has(name)) {
|
if (!this.metrics.has(name)) {
|
||||||
@ -151,8 +150,6 @@ export class PerformanceMonitor {
|
|||||||
const result: Record<string, PerformanceStats> = {};
|
const result: Record<string, PerformanceStats> = {};
|
||||||
|
|
||||||
this.metrics.forEach((values, name) => {
|
this.metrics.forEach((values, name) => {
|
||||||
// Use values parameter
|
|
||||||
console.log('Performance metric values:', values);
|
|
||||||
const stats = this.getMetricStats(name);
|
const stats = this.getMetricStats(name);
|
||||||
if (stats) {
|
if (stats) {
|
||||||
result[name] = stats;
|
result[name] = stats;
|
||||||
@ -220,9 +217,9 @@ export class PerformanceMonitor {
|
|||||||
if (!threshold) {return;}
|
if (!threshold) {return;}
|
||||||
|
|
||||||
if (value > threshold.critical) {
|
if (value > threshold.critical) {
|
||||||
console.warn(`🚨 Critical performance threshold exceeded for ${name}: ${value}ms`);
|
secureLogger.warn(`🚨 Critical performance threshold exceeded for ${name}: ${value}ms`, { component: 'PerformanceMonitor', metric: name, value });
|
||||||
} else if (value > threshold.warning) {
|
} else if (value > threshold.warning) {
|
||||||
console.warn(`⚠️ Performance warning for ${name}: ${value}ms`);
|
secureLogger.warn(`⚠️ Performance warning for ${name}: ${value}ms`, { component: 'PerformanceMonitor', metric: name, value });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,7 +242,7 @@ export class PerformanceMonitor {
|
|||||||
measureObserver.observe({ entryTypes: ['measure'] });
|
measureObserver.observe({ entryTypes: ['measure'] });
|
||||||
this.observers.push(measureObserver);
|
this.observers.push(measureObserver);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('Failed to observe performance measures:', error);
|
secureLogger.warn('Failed to observe performance measures', { component: 'PerformanceMonitor', error: String(error) });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Observer la navigation
|
// Observer la navigation
|
||||||
@ -263,7 +260,7 @@ export class PerformanceMonitor {
|
|||||||
navigationObserver.observe({ entryTypes: ['navigation'] });
|
navigationObserver.observe({ entryTypes: ['navigation'] });
|
||||||
this.observers.push(navigationObserver);
|
this.observers.push(navigationObserver);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('Failed to observe navigation performance:', error);
|
secureLogger.warn('Failed to observe navigation performance', { component: 'PerformanceMonitor', error: String(error) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,7 +282,7 @@ export class PerformanceMonitor {
|
|||||||
performance.measure(name, startMark);
|
performance.measure(name, startMark);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(`Failed to measure ${name}:`, error);
|
secureLogger.warn(`Failed to measure ${name}`, { component: 'PerformanceMonitor', metric: name, error: String(error) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,8 +291,6 @@ export class PerformanceMonitor {
|
|||||||
*/
|
*/
|
||||||
cleanup(): void {
|
cleanup(): void {
|
||||||
const cutoff = Date.now() - (24 * 60 * 60 * 1000); // 24 heures
|
const cutoff = Date.now() - (24 * 60 * 60 * 1000); // 24 heures
|
||||||
// Use cutoff variable
|
|
||||||
console.log('Performance cleanup cutoff:', cutoff);
|
|
||||||
|
|
||||||
this.metrics.forEach((values, name) => {
|
this.metrics.forEach((values, name) => {
|
||||||
// Garder seulement les 100 dernières valeurs
|
// Garder seulement les 100 dernières valeurs
|
||||||
|
|||||||
@ -57,21 +57,33 @@ export class SecureLogger {
|
|||||||
/**
|
/**
|
||||||
* Log un avertissement
|
* Log un avertissement
|
||||||
*/
|
*/
|
||||||
warn(message: string, context?: LogContext): void {
|
warn(message: string, contextOrError?: LogContext | Error | unknown, context?: LogContext): void {
|
||||||
this.log(LogLevel.WARN, message, context);
|
// If we have 3 params, second is error, third is context
|
||||||
|
if (context) {
|
||||||
|
// Message, error, context case
|
||||||
|
this.log(LogLevel.WARN, message, context);
|
||||||
|
} else if (contextOrError && (contextOrError instanceof Error || typeof contextOrError !== 'object')) {
|
||||||
|
// Message, error case - convert to context if needed
|
||||||
|
this.log(LogLevel.WARN, message, undefined);
|
||||||
|
} else {
|
||||||
|
// Message, context case
|
||||||
|
this.log(LogLevel.WARN, message, contextOrError as LogContext);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log une erreur
|
* Log une erreur
|
||||||
*/
|
*/
|
||||||
error(message: string, error?: Error, context?: LogContext): void {
|
error(message: string, error?: Error | unknown, context?: LogContext): void {
|
||||||
this.log(LogLevel.ERROR, message, context, error);
|
// Convert unknown to Error if needed
|
||||||
|
const errorObj = error instanceof Error ? error : (error ? new Error(String(error)) : undefined);
|
||||||
|
this.log(LogLevel.ERROR, message, context, errorObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log sécurisé avec sanitisation
|
* Log sécurisé avec sanitisation
|
||||||
*/
|
*/
|
||||||
private log(level: LogLevel, message: string, context?: LogContext, error?: Error): void {
|
private log(level: LogLevel, message: string, context?: LogContext, error?: Error | undefined): void {
|
||||||
const sanitizedContext = this.sanitizeContext(context);
|
const sanitizedContext = this.sanitizeContext(context);
|
||||||
const sanitizedMessage = this.sanitizeMessage(message);
|
const sanitizedMessage = this.sanitizeMessage(message);
|
||||||
|
|
||||||
|
|||||||
@ -1303,7 +1303,7 @@ export default class Services {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
secureLogger.debug('Error in operation', e as Error, { component: 'Service' });
|
secureLogger.debug('Error in operation', { component: 'Service', error: String(e) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1704,6 +1704,10 @@ export default class Services {
|
|||||||
throw new Error(` Credential retrieval failed: ${credentialError}`);
|
throw new Error(` Credential retrieval failed: ${credentialError}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!credentials) {
|
||||||
|
throw new Error('No credentials found');
|
||||||
|
}
|
||||||
|
|
||||||
secureLogger.debug(' Credentials to inject:', { component: 'Service', data: {
|
secureLogger.debug(' Credentials to inject:', { component: 'Service', data: {
|
||||||
spendKey_length: credentials.spendKey?.length || 0,
|
spendKey_length: credentials.spendKey?.length || 0,
|
||||||
scanKey_length: credentials.scanKey?.length || 0,
|
scanKey_length: credentials.scanKey?.length || 0,
|
||||||
@ -1711,10 +1715,6 @@ export default class Services {
|
|||||||
scanKey_type: typeof credentials.scanKey
|
scanKey_type: typeof credentials.scanKey
|
||||||
} });
|
} });
|
||||||
|
|
||||||
if (!credentials) {
|
|
||||||
throw new Error('No credentials found');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Injecter les clés dans le device de la base de données avant restauration
|
// Injecter les clés dans le device de la base de données avant restauration
|
||||||
secureLogger.info('🔧 Injecting keys into device from database...', { component: 'Service' });
|
secureLogger.info('🔧 Injecting keys into device from database...', { component: 'Service' });
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -62,7 +62,7 @@ export class LogAnalyzer {
|
|||||||
/**
|
/**
|
||||||
* Suggère un contexte basé sur le nom du fichier et le message
|
* Suggère un contexte basé sur le nom du fichier et le message
|
||||||
*/
|
*/
|
||||||
private suggestContext(file: string, message: string): string {
|
private suggestContext(file: string, _message: string): string {
|
||||||
const fileName = file.split('/').pop()?.replace('.ts', '') || '';
|
const fileName = file.split('/').pop()?.replace('.ts', '') || '';
|
||||||
|
|
||||||
// Patterns pour identifier le composant
|
// Patterns pour identifier le composant
|
||||||
|
|||||||
@ -25,7 +25,7 @@ export class LogFixer {
|
|||||||
// Remplacer console.log par secureLogger.info
|
// Remplacer console.log par secureLogger.info
|
||||||
fixedContent = fixedContent.replace(
|
fixedContent = fixedContent.replace(
|
||||||
/console\.log\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g,
|
/console\.log\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g,
|
||||||
(match, message) => {
|
(_match, message) => {
|
||||||
const level = this.determineLogLevel(message);
|
const level = this.determineLogLevel(message);
|
||||||
const context = this.determineContext(filePath, message);
|
const context = this.determineContext(filePath, message);
|
||||||
return `secureLogger.${level}('${message}'${context})`;
|
return `secureLogger.${level}('${message}'${context})`;
|
||||||
@ -35,7 +35,7 @@ export class LogFixer {
|
|||||||
// Remplacer console.warn par secureLogger.warn
|
// Remplacer console.warn par secureLogger.warn
|
||||||
fixedContent = fixedContent.replace(
|
fixedContent = fixedContent.replace(
|
||||||
/console\.warn\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g,
|
/console\.warn\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g,
|
||||||
(match, message) => {
|
(_match, message) => {
|
||||||
const context = this.determineContext(filePath, message);
|
const context = this.determineContext(filePath, message);
|
||||||
return `secureLogger.warn('${message}'${context})`;
|
return `secureLogger.warn('${message}'${context})`;
|
||||||
}
|
}
|
||||||
@ -44,7 +44,7 @@ export class LogFixer {
|
|||||||
// Remplacer console.error par secureLogger.error
|
// Remplacer console.error par secureLogger.error
|
||||||
fixedContent = fixedContent.replace(
|
fixedContent = fixedContent.replace(
|
||||||
/console\.error\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g,
|
/console\.error\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g,
|
||||||
(match, message) => {
|
(_match, message) => {
|
||||||
const context = this.determineContext(filePath, message);
|
const context = this.determineContext(filePath, message);
|
||||||
return `secureLogger.error('${message}'${context})`;
|
return `secureLogger.error('${message}'${context})`;
|
||||||
}
|
}
|
||||||
@ -53,7 +53,7 @@ export class LogFixer {
|
|||||||
// Remplacer console.info par secureLogger.info
|
// Remplacer console.info par secureLogger.info
|
||||||
fixedContent = fixedContent.replace(
|
fixedContent = fixedContent.replace(
|
||||||
/console\.info\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g,
|
/console\.info\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g,
|
||||||
(match, message) => {
|
(_match, message) => {
|
||||||
const context = this.determineContext(filePath, message);
|
const context = this.determineContext(filePath, message);
|
||||||
return `secureLogger.info('${message}'${context})`;
|
return `secureLogger.info('${message}'${context})`;
|
||||||
}
|
}
|
||||||
@ -62,7 +62,7 @@ export class LogFixer {
|
|||||||
// Remplacer console.debug par secureLogger.debug
|
// Remplacer console.debug par secureLogger.debug
|
||||||
fixedContent = fixedContent.replace(
|
fixedContent = fixedContent.replace(
|
||||||
/console\.debug\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g,
|
/console\.debug\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g,
|
||||||
(match, message) => {
|
(_match, message) => {
|
||||||
const context = this.determineContext(filePath, message);
|
const context = this.determineContext(filePath, message);
|
||||||
return `secureLogger.debug('${message}'${context})`;
|
return `secureLogger.debug('${message}'${context})`;
|
||||||
}
|
}
|
||||||
@ -95,7 +95,7 @@ export class LogFixer {
|
|||||||
/**
|
/**
|
||||||
* Détermine le contexte basé sur le fichier et le message
|
* Détermine le contexte basé sur le fichier et le message
|
||||||
*/
|
*/
|
||||||
private static determineContext(filePath: string, message: string): string {
|
private static determineContext(filePath: string, _message: string): string {
|
||||||
const fileName = filePath.split('/').pop()?.replace('.ts', '') || '';
|
const fileName = filePath.split('/').pop()?.replace('.ts', '') || '';
|
||||||
|
|
||||||
let component = 'Application';
|
let component = 'Application';
|
||||||
|
|||||||
@ -2593,7 +2593,7 @@ async function onCreateButtonClick() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New function for joiner to discover and join existing pairing process using 4 words
|
// New function for joiner to discover and join existing pairing process using 4 words
|
||||||
export async function discoverAndJoinPairingProcessWithWords(words: string): Promise<void> {
|
export async function discoverAndJoinPairingProcessWithWords(_words: string): Promise<void> {
|
||||||
const service = await Services.getInstance();
|
const service = await Services.getInstance();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -2647,13 +2647,13 @@ export async function discoverAndJoinPairingProcessWithWords(words: string): Pro
|
|||||||
|
|
||||||
throw new Error(`❌ Failed to discover pairing process after ${maxRetries} attempts`);
|
throw new Error(`❌ Failed to discover pairing process after ${maxRetries} attempts`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
secureLogger.error('❌ Joiner discovery failed:', (err as Error, { component: 'SPAddressUtils' }).message);
|
secureLogger.error('❌ Joiner discovery failed:', err as Error, { component: 'SPAddressUtils' });
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// New function for joiner to discover and join existing pairing process
|
// New function for joiner to discover and join existing pairing process
|
||||||
export async function discoverAndJoinPairingProcess(creatorAddress: string): Promise<void> {
|
export async function discoverAndJoinPairingProcess(_creatorAddress: string): Promise<void> {
|
||||||
const service = await Services.getInstance();
|
const service = await Services.getInstance();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -2707,7 +2707,7 @@ export async function discoverAndJoinPairingProcess(creatorAddress: string): Pro
|
|||||||
|
|
||||||
throw new Error(`❌ Failed to discover pairing process after ${maxRetries} attempts`);
|
throw new Error(`❌ Failed to discover pairing process after ${maxRetries} attempts`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
secureLogger.error('❌ Joiner discovery failed:', (err as Error, { component: 'SPAddressUtils' }).message);
|
secureLogger.error('❌ Joiner discovery failed:', err as Error, { component: 'SPAddressUtils' });
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
// import path from 'path';
|
import path from 'path';
|
||||||
// @ts-ignore - vite-plugin-wasm type definitions issue
|
// @ts-ignore - vite-plugin-wasm type definitions issue
|
||||||
import wasm from 'vite-plugin-wasm';
|
import wasm from 'vite-plugin-wasm';
|
||||||
import topLevelAwait from 'vite-plugin-top-level-await';
|
import topLevelAwait from 'vite-plugin-top-level-await';
|
||||||
@ -19,6 +19,18 @@ export default defineConfig({
|
|||||||
target: 'esnext',
|
target: 'esnext',
|
||||||
minify: true, // Enable minification to reduce size
|
minify: true, // Enable minification to reduce size
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
|
input: {
|
||||||
|
index: path.resolve(__dirname, 'index.html'),
|
||||||
|
'wallet-setup': path.resolve(__dirname, 'src/pages/wallet-setup/wallet-setup.html'),
|
||||||
|
'security-setup': path.resolve(__dirname, 'src/pages/security-setup/security-setup.html'),
|
||||||
|
'birthday-setup': path.resolve(__dirname, 'src/pages/birthday-setup/birthday-setup.html'),
|
||||||
|
'block-sync': path.resolve(__dirname, 'src/pages/block-sync/block-sync.html'),
|
||||||
|
'pairing': path.resolve(__dirname, 'src/pages/pairing/pairing.html'),
|
||||||
|
'home': path.resolve(__dirname, 'src/pages/home/home.html'),
|
||||||
|
'iframe-home': path.resolve(__dirname, 'src/pages/home/iframe-home.html'),
|
||||||
|
'account': path.resolve(__dirname, 'src/pages/account/account.html'),
|
||||||
|
'iframe-pairing': path.resolve(__dirname, 'src/pages/iframe-pairing.html'),
|
||||||
|
},
|
||||||
output: {
|
output: {
|
||||||
manualChunks: {
|
manualChunks: {
|
||||||
// Split WebAssembly into separate chunk
|
// Split WebAssembly into separate chunk
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user