fix(service): Remplacer le polling des clés par un listener pour éviter une race condition
This commit is contained in:
parent
f610a1bfa6
commit
95e7044e0a
@ -36,6 +36,7 @@ export default class Services {
|
|||||||
private relayReadyResolver: (() => void) | null = null;
|
private relayReadyResolver: (() => void) | null = null;
|
||||||
private relayReadyPromise: Promise<void> | null = null;
|
private relayReadyPromise: Promise<void> | null = null;
|
||||||
private secretsAreCompromised: boolean = false;
|
private secretsAreCompromised: boolean = false;
|
||||||
|
private pendingKeyRequests: Map<string, (key: string) => void> = new Map();
|
||||||
// Private constructor to prevent direct instantiation from outside
|
// Private constructor to prevent direct instantiation from outside
|
||||||
private constructor() {}
|
private constructor() {}
|
||||||
|
|
||||||
@ -1138,10 +1139,44 @@ export default class Services {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._resolvePendingKeyRequests(updatedProcess.current_process);
|
||||||
|
|
||||||
// Vérifier la logique métier spécifique au pairing
|
// Vérifier la logique métier spécifique au pairing
|
||||||
await this.checkAndConfirmPairing(processId, updatedProcess);
|
await this.checkAndConfirmPairing(processId, updatedProcess);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _resolvePendingKeyRequests(process: Process) {
|
||||||
|
if (this.pendingKeyRequests.size === 0) {
|
||||||
|
return; // Optimisation : ne rien faire si personne n'attend
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[Services:KeyResolver] 🔍 Vérification de ${this.pendingKeyRequests.size} requête(s) de clé en attente...`);
|
||||||
|
|
||||||
|
for (const state of process.states) {
|
||||||
|
if (!state.keys || Object.keys(state.keys).length === 0) {
|
||||||
|
continue; // Pas de clés dans cet état
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [attributeName, key] of Object.entries(state.keys)) {
|
||||||
|
const requestId = `${process.process_id}_${state.state_id}_${attributeName}`;
|
||||||
|
|
||||||
|
// Avons-nous une requête en attente pour CETTE clé ?
|
||||||
|
if (this.pendingKeyRequests.has(requestId)) {
|
||||||
|
console.log(`[Services:KeyResolver] ✅ Résolution de la requête pour ${requestId}`);
|
||||||
|
|
||||||
|
// Récupérer la fonction "resolve" et l'appeler avec la clé
|
||||||
|
const resolveCallback = this.pendingKeyRequests.get(requestId);
|
||||||
|
if (resolveCallback) {
|
||||||
|
resolveCallback(key as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nettoyer la requête
|
||||||
|
this.pendingKeyRequests.delete(requestId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async saveEncryptedData(encryptedData: Record<string, string>) {
|
private async saveEncryptedData(encryptedData: Record<string, string>) {
|
||||||
console.log(`[Services:saveEncryptedData] 💾 Sauvegarde de ${Object.keys(encryptedData).length} blob(s) chiffré(s)...`);
|
console.log(`[Services:saveEncryptedData] 💾 Sauvegarde de ${Object.keys(encryptedData).length} blob(s) chiffré(s)...`);
|
||||||
for (const [hash, cipher] of Object.entries(encryptedData)) {
|
for (const [hash, cipher] of Object.entries(encryptedData)) {
|
||||||
@ -1906,11 +1941,11 @@ export default class Services {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NOUVELLE MÉTHODE PRIVÉE
|
* NOUVELLE MÉTHODE PRIVÉE (MODIFIÉE)
|
||||||
* Gère la logique complexe de demande aux pairs et la boucle de retentative.
|
* Gère la logique de demande aux pairs en utilisant un système de Promise au lieu de polling.
|
||||||
*/
|
*/
|
||||||
private async _fetchMissingKey(processId: string, state: ProcessState, attribute: string): Promise<{ hash: string | null; key: string | null }> {
|
private async _fetchMissingKey(processId: string, state: ProcessState, attribute: string): Promise<{ hash: string | null; key: string | null }> {
|
||||||
console.group(`🔐 Gestion de la clé manquante pour '${attribute}'`);
|
console.group(`🔐 Gestion de la clé manquante pour '${attribute}' (via Promise)`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const process = await this.getProcess(processId);
|
const process = await this.getProcess(processId);
|
||||||
@ -1919,53 +1954,42 @@ export default class Services {
|
|||||||
return { hash: null, key: null };
|
return { hash: null, key: null };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1. Demander les connexions et envoyer la requête (comme avant)
|
||||||
await this.ensureConnections(process);
|
await this.ensureConnections(process);
|
||||||
console.log(`🗣️ Demande de données aux pairs...`);
|
console.log(`🗣️ Demande de données aux pairs...`);
|
||||||
await this.requestDataFromPeers(processId, [state.state_id], [state.roles]);
|
await this.requestDataFromPeers(processId, [state.state_id], [state.roles]);
|
||||||
|
|
||||||
// Boucle de retentative
|
// 2. CRÉER LA PROMISE D'ATTENTE
|
||||||
const maxRetries = 5;
|
const requestId = `${processId}_${state.state_id}_${attribute}`;
|
||||||
const retryDelay = 500;
|
const keyRequestPromise = new Promise<string>((resolve, reject) => {
|
||||||
let retries = 0;
|
// Sécurité : Définir un timeout (ex: 15 secondes)
|
||||||
let hash: string | null = null;
|
const timeout = setTimeout(() => {
|
||||||
let key: string | null = null;
|
console.warn(`⌛ Timeout: La clé pour '${attribute}' n'est jamais arrivée.`);
|
||||||
|
this.pendingKeyRequests.delete(requestId); // Nettoyer
|
||||||
|
reject(new Error(`Timeout waiting for key: ${attribute}`));
|
||||||
|
}, 15000); // 15 secondes
|
||||||
|
|
||||||
console.groupCollapsed(`⏳ Boucle d'attente de la clé (max ${maxRetries} tentatives)`);
|
// Enregistrer le "resolve" pour qu'il soit appelé de l'extérieur
|
||||||
try {
|
this.pendingKeyRequests.set(requestId, (key: string) => {
|
||||||
while (retries < maxRetries) {
|
clearTimeout(timeout); // Annuler le timeout
|
||||||
console.log(`(Tentative ${retries + 1}/${maxRetries})...`);
|
console.log(`✅ Clé reçue via listener pour '${attribute}'!`);
|
||||||
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
resolve(key);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// On rafraîchit les données depuis la source
|
// 3. Attendre la clé
|
||||||
const updatedProcess = await this.getProcess(processId);
|
const receivedKey = await keyRequestPromise;
|
||||||
const updatedState = this.getStateFromId(updatedProcess, state.state_id);
|
|
||||||
|
|
||||||
if (updatedState) {
|
// 4. Re-vérifier l'état (le hash a pu changer)
|
||||||
hash = updatedState.pcd_commitment[attribute];
|
const updatedProcess = await this.getProcess(processId);
|
||||||
key = updatedState.keys[attribute];
|
const updatedState = this.getStateFromId(updatedProcess, state.state_id);
|
||||||
|
const updatedHash = updatedState ? updatedState.pcd_commitment[attribute] : state.pcd_commitment[attribute];
|
||||||
|
|
||||||
if (hash && key) {
|
return { hash: updatedHash, key: receivedKey };
|
||||||
console.log('✅ Clé et hash reçus !');
|
|
||||||
break; // Sortir de la boucle while
|
|
||||||
}
|
|
||||||
}
|
|
||||||
retries++;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
// Garantit la fermeture du groupe de la boucle
|
|
||||||
console.groupEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hash || !key) {
|
|
||||||
console.warn(`⌛ Clé ou hash toujours manquant après ${maxRetries} tentatives.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { hash, key };
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`💥 Erreur durant _fetchMissingKey: ${e}`);
|
console.error(`💥 Erreur durant _fetchMissingKey: ${e}`);
|
||||||
return { hash: null, key: null };
|
return { hash: null, key: null };
|
||||||
} finally {
|
} finally {
|
||||||
// Garantit la fermeture du groupe de gestion de clé
|
|
||||||
console.groupEnd();
|
console.groupEnd();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user