Compare commits
No commits in common. "d78dc14a2b91ee3503269cbbeef0c2c54584fa32" and "8580070dc40dd8f990d3c39f424aa3553e7aec42" have entirely different histories.
d78dc14a2b
...
8580070dc4
@ -28,6 +28,7 @@
|
||||
"vite-plugin-static-copy": "^1.0.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.7.8",
|
||||
"comlink": "^4.4.2",
|
||||
"jose": "^6.0.11",
|
||||
"vite-plugin-wasm": "^3.3.0"
|
||||
|
||||
@ -9,10 +9,6 @@ export class NetworkService {
|
||||
// Cache local
|
||||
private localRelays: Record<string, string> = {};
|
||||
|
||||
// Mécanisme d'attente (Events)
|
||||
private relayReadyResolver: ((addr: string) => void) | null = null;
|
||||
private relayReadyPromise: Promise<string> | null = null;
|
||||
|
||||
constructor(private bootstrapUrls: string[]) {
|
||||
this.workerInstance = new Worker(
|
||||
new URL("../../workers/network.worker.ts", import.meta.url),
|
||||
@ -46,47 +42,36 @@ export class NetworkService {
|
||||
await this.worker.sendMessage(flag as any, content);
|
||||
}
|
||||
|
||||
// Cette méthode est appelée par le Worker (via Services.ts) ou par onStatusChange
|
||||
public updateRelay(url: string, spAddress: string) {
|
||||
this.localRelays[url] = spAddress;
|
||||
|
||||
// ✨ EVENT TRIGGER : Si quelqu'un attendait un relais, on le débloque !
|
||||
if (spAddress && spAddress !== "" && this.relayReadyResolver) {
|
||||
this.relayReadyResolver(spAddress);
|
||||
this.relayReadyResolver = null;
|
||||
this.relayReadyPromise = null;
|
||||
}
|
||||
}
|
||||
|
||||
public getAllRelays() {
|
||||
return this.localRelays;
|
||||
}
|
||||
|
||||
// 🔥 CORRECTION ICI : Ajout d'une boucle d'attente (Polling)
|
||||
public async getAvailableRelayAddress(): Promise<string> {
|
||||
// 1. Vérification immédiate (Fast path)
|
||||
const existing = Object.values(this.localRelays).find(
|
||||
(addr) => addr && addr !== ""
|
||||
);
|
||||
if (existing) return existing;
|
||||
const maxRetries = 20; // 20 tentatives
|
||||
const interval = 500; // toutes les 500ms = 10 secondes max
|
||||
|
||||
// 2. Si pas encore là, on crée une "barrière" (Promise)
|
||||
if (!this.relayReadyPromise) {
|
||||
console.log("[NetworkService] ⏳ Attente d'un événement Handshake...");
|
||||
this.relayReadyPromise = new Promise<string>((resolve, reject) => {
|
||||
this.relayReadyResolver = resolve;
|
||||
for (let i = 0; i < maxRetries; i++) {
|
||||
// On demande au worker
|
||||
const addr = await this.worker.getAvailableRelay();
|
||||
|
||||
// Timeout de sécurité (10s) pour ne pas bloquer indéfiniment
|
||||
setTimeout(() => {
|
||||
if (this.relayReadyResolver) {
|
||||
reject(new Error("Timeout: Aucun relais reçu après 10s"));
|
||||
this.relayReadyResolver = null;
|
||||
this.relayReadyPromise = null;
|
||||
}
|
||||
}, 10000);
|
||||
});
|
||||
if (addr && addr !== "") {
|
||||
return addr; // Trouvé !
|
||||
}
|
||||
|
||||
// Pas encore là ? On attend un peu...
|
||||
if (i === 0)
|
||||
console.log("[NetworkService] ⏳ Attente du Handshake relais...");
|
||||
await new Promise((r) => setTimeout(r, interval));
|
||||
}
|
||||
|
||||
return this.relayReadyPromise;
|
||||
throw new Error(
|
||||
"Timeout: Aucun relais disponible après 10s (Handshake non reçu ?)"
|
||||
);
|
||||
}
|
||||
|
||||
// --- INTERNES ---
|
||||
@ -102,8 +87,7 @@ export class NetworkService {
|
||||
spAddress?: string
|
||||
) {
|
||||
if (status === "OPEN" && spAddress) {
|
||||
// Met à jour et déclenche potentiellement le resolve()
|
||||
this.updateRelay(url, spAddress);
|
||||
this.localRelays[url] = spAddress;
|
||||
} else if (status === "CLOSED") {
|
||||
this.localRelays[url] = "";
|
||||
}
|
||||
|
||||
@ -1,112 +1,135 @@
|
||||
export async function storeData(
|
||||
servers: string[],
|
||||
key: string,
|
||||
value: Blob,
|
||||
ttl: number | null
|
||||
): Promise<Response | null> {
|
||||
import axios, { AxiosResponse } from 'axios';
|
||||
|
||||
export async function storeData(servers: string[], key: string, value: Blob, ttl: number | null): Promise<AxiosResponse | null> {
|
||||
for (const server of servers) {
|
||||
try {
|
||||
// 1. Vérification d'existence (GET)
|
||||
// 1. On vérifie d'abord si la donnée existe en appelant le bon service
|
||||
// On passe 'server' au lieu de 'url' pour que testData construise la bonne URL
|
||||
const dataExists = await testData(server, key);
|
||||
|
||||
|
||||
if (dataExists) {
|
||||
console.log("Data already stored:", key);
|
||||
console.log('Data already stored:', key);
|
||||
continue;
|
||||
} else {
|
||||
console.log(
|
||||
"Data not stored for server, proceeding to POST:",
|
||||
key,
|
||||
server
|
||||
);
|
||||
console.log('Data not stored for server, proceeding to POST:', key, server);
|
||||
}
|
||||
|
||||
// 2. Construction URL
|
||||
|
||||
// Construction de l'URL pour le POST (stockage)
|
||||
// Cette partie était correcte
|
||||
let url: string;
|
||||
if (server.startsWith("/")) {
|
||||
if (server.startsWith('/')) {
|
||||
// Relative path
|
||||
url = `${server}/store/${encodeURIComponent(key)}`;
|
||||
if (ttl !== null) url += `?ttl=${ttl}`;
|
||||
if (ttl !== null) {
|
||||
url += `?ttl=${ttl}`;
|
||||
}
|
||||
} else {
|
||||
// Absolute URL
|
||||
const urlObj = new URL(`${server}/store/${encodeURIComponent(key)}`);
|
||||
if (ttl !== null) urlObj.searchParams.append("ttl", ttl.toString());
|
||||
if (ttl !== null) {
|
||||
urlObj.searchParams.append('ttl', ttl.toString());
|
||||
}
|
||||
url = urlObj.toString();
|
||||
}
|
||||
|
||||
// 3. Envoi (POST) avec Fetch
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/octet-stream",
|
||||
},
|
||||
body: value,
|
||||
});
|
||||
// La ligne ci-dessous a été supprimée car le test est fait au-dessus
|
||||
// const testResponse = await testData(url, key); // <-- LIGNE BOGUÉE SUPPRIMÉE
|
||||
|
||||
if (response.ok) {
|
||||
// Status 200-299
|
||||
console.log("Data stored successfully:", key);
|
||||
return response;
|
||||
} else if (response.status === 409) {
|
||||
// Conflit (déjà existant), on retourne null comme avant
|
||||
return null;
|
||||
} else {
|
||||
console.error("Received response status", response.status);
|
||||
// Send the encrypted data as the raw request body.
|
||||
const response = await axios.post(url, value, { // Note: c'est bien un POST sur 'url'
|
||||
headers: {
|
||||
'Content-Type': 'application/octet-stream'
|
||||
},
|
||||
});
|
||||
console.log('Data stored successfully:', key);
|
||||
if (response.status !== 200) {
|
||||
console.error('Received response status', response.status);
|
||||
continue;
|
||||
}
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error("Error storing data:", error);
|
||||
if (axios.isAxiosError(error) && error.response?.status === 409) {
|
||||
return null; // 409 Conflict (Key already exists)
|
||||
}
|
||||
console.error('Error storing data:', error);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function retrieveData(
|
||||
servers: string[],
|
||||
key: string
|
||||
): Promise<ArrayBuffer | null> {
|
||||
// Fonction retrieveData (inchangée, elle était correcte)
|
||||
export async function retrieveData(servers: string[], key: string): Promise<ArrayBuffer | null> {
|
||||
for (const server of servers) {
|
||||
try {
|
||||
const url = server.startsWith("/")
|
||||
const url = server.startsWith('/')
|
||||
? `${server}/retrieve/${key}`
|
||||
: new URL(`${server}/retrieve/${key}`).toString();
|
||||
|
||||
console.log("Retrieving data", key, " from:", url);
|
||||
|
||||
const response = await fetch(url, { method: "GET" });
|
||||
|
||||
if (response.ok) {
|
||||
// Transformation en ArrayBuffer
|
||||
return await response.arrayBuffer();
|
||||
} else {
|
||||
if (response.status === 404) {
|
||||
console.log(`Data not found on server ${server} for key ${key}`);
|
||||
: new URL(`${server}/retrieve/${key}`).toString();
|
||||
|
||||
console.log('Retrieving data', key,' from:', url);
|
||||
|
||||
const response = await axios.get(url, {
|
||||
responseType: 'arraybuffer'
|
||||
});
|
||||
|
||||
if (response.status === 200) {
|
||||
if (response.data instanceof ArrayBuffer) {
|
||||
return response.data;
|
||||
} else {
|
||||
console.error(
|
||||
`Server ${server} returned status ${response.status}: ${response.statusText}`
|
||||
);
|
||||
console.error('Server returned non-ArrayBuffer data:', typeof response.data);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
console.error(`Server ${server} returned status ${response.status}`);
|
||||
continue;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Unexpected error retrieving data from ${server}:`, error);
|
||||
continue;
|
||||
if (axios.isAxiosError(error)) {
|
||||
if (error.response?.status === 404) {
|
||||
// C'est normal si la donnée n'existe pas
|
||||
console.log(`Data not found on server ${server} for key ${key}`);
|
||||
continue;
|
||||
} else if (error.response?.status) {
|
||||
console.error(`Server ${server} error ${error.response.status}:`, error.response.statusText);
|
||||
continue;
|
||||
} else {
|
||||
console.error(`Network error connecting to ${server}:`, error.message);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
console.error(`Unexpected error retrieving data from ${server}:`, error);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
interface TestResponse {
|
||||
key: string;
|
||||
value: boolean;
|
||||
}
|
||||
|
||||
// Elle prend 'server' au lieu de 'url' et construit sa propre URL '/test/...'
|
||||
export async function testData(server: string, key: string): Promise<boolean> {
|
||||
try {
|
||||
const testUrl = server.startsWith("/")
|
||||
// Construit l'URL /test/...
|
||||
const testUrl = server.startsWith('/')
|
||||
? `${server}/test/${encodeURIComponent(key)}`
|
||||
: new URL(`${server}/test/${encodeURIComponent(key)}`).toString();
|
||||
|
||||
// On utilise fetch ici aussi
|
||||
const response = await fetch(testUrl, { method: "GET" });
|
||||
const response = await axios.get(testUrl); // Fait un GET sur /test/...
|
||||
|
||||
// 200 OK = la donnée existe
|
||||
return response.status === 200;
|
||||
|
||||
// 200 OK = existe
|
||||
return response.status === 200;
|
||||
} catch (error) {
|
||||
// Erreur réseau
|
||||
console.error("Error testing data:", error);
|
||||
return false;
|
||||
if (axios.isAxiosError(error) && error.response?.status === 404) {
|
||||
// 404 Not Found = la donnée n'existe pas. C'est une réponse valide.
|
||||
return false;
|
||||
}
|
||||
// Toute autre erreur (serveur offline, 500, etc.)
|
||||
console.error('Error testing data:', error);
|
||||
return false; // On considère que le test a échoué
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user