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"
|
"vite-plugin-static-copy": "^1.0.6"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"axios": "^1.7.8",
|
||||||
"comlink": "^4.4.2",
|
"comlink": "^4.4.2",
|
||||||
"jose": "^6.0.11",
|
"jose": "^6.0.11",
|
||||||
"vite-plugin-wasm": "^3.3.0"
|
"vite-plugin-wasm": "^3.3.0"
|
||||||
|
|||||||
@ -9,10 +9,6 @@ export class NetworkService {
|
|||||||
// Cache local
|
// Cache local
|
||||||
private localRelays: Record<string, string> = {};
|
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[]) {
|
constructor(private bootstrapUrls: string[]) {
|
||||||
this.workerInstance = new Worker(
|
this.workerInstance = new Worker(
|
||||||
new URL("../../workers/network.worker.ts", import.meta.url),
|
new URL("../../workers/network.worker.ts", import.meta.url),
|
||||||
@ -46,47 +42,36 @@ export class NetworkService {
|
|||||||
await this.worker.sendMessage(flag as any, content);
|
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) {
|
public updateRelay(url: string, spAddress: string) {
|
||||||
this.localRelays[url] = spAddress;
|
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() {
|
public getAllRelays() {
|
||||||
return this.localRelays;
|
return this.localRelays;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 🔥 CORRECTION ICI : Ajout d'une boucle d'attente (Polling)
|
||||||
public async getAvailableRelayAddress(): Promise<string> {
|
public async getAvailableRelayAddress(): Promise<string> {
|
||||||
// 1. Vérification immédiate (Fast path)
|
const maxRetries = 20; // 20 tentatives
|
||||||
const existing = Object.values(this.localRelays).find(
|
const interval = 500; // toutes les 500ms = 10 secondes max
|
||||||
(addr) => addr && addr !== ""
|
|
||||||
);
|
|
||||||
if (existing) return existing;
|
|
||||||
|
|
||||||
// 2. Si pas encore là, on crée une "barrière" (Promise)
|
for (let i = 0; i < maxRetries; i++) {
|
||||||
if (!this.relayReadyPromise) {
|
// On demande au worker
|
||||||
console.log("[NetworkService] ⏳ Attente d'un événement Handshake...");
|
const addr = await this.worker.getAvailableRelay();
|
||||||
this.relayReadyPromise = new Promise<string>((resolve, reject) => {
|
|
||||||
this.relayReadyResolver = resolve;
|
|
||||||
|
|
||||||
// Timeout de sécurité (10s) pour ne pas bloquer indéfiniment
|
if (addr && addr !== "") {
|
||||||
setTimeout(() => {
|
return addr; // Trouvé !
|
||||||
if (this.relayReadyResolver) {
|
}
|
||||||
reject(new Error("Timeout: Aucun relais reçu après 10s"));
|
|
||||||
this.relayReadyResolver = null;
|
// Pas encore là ? On attend un peu...
|
||||||
this.relayReadyPromise = null;
|
if (i === 0)
|
||||||
}
|
console.log("[NetworkService] ⏳ Attente du Handshake relais...");
|
||||||
}, 10000);
|
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 ---
|
// --- INTERNES ---
|
||||||
@ -102,8 +87,7 @@ export class NetworkService {
|
|||||||
spAddress?: string
|
spAddress?: string
|
||||||
) {
|
) {
|
||||||
if (status === "OPEN" && spAddress) {
|
if (status === "OPEN" && spAddress) {
|
||||||
// Met à jour et déclenche potentiellement le resolve()
|
this.localRelays[url] = spAddress;
|
||||||
this.updateRelay(url, spAddress);
|
|
||||||
} else if (status === "CLOSED") {
|
} else if (status === "CLOSED") {
|
||||||
this.localRelays[url] = "";
|
this.localRelays[url] = "";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,112 +1,135 @@
|
|||||||
export async function storeData(
|
import axios, { AxiosResponse } from 'axios';
|
||||||
servers: string[],
|
|
||||||
key: string,
|
export async function storeData(servers: string[], key: string, value: Blob, ttl: number | null): Promise<AxiosResponse | null> {
|
||||||
value: Blob,
|
|
||||||
ttl: number | null
|
|
||||||
): Promise<Response | null> {
|
|
||||||
for (const server of servers) {
|
for (const server of servers) {
|
||||||
try {
|
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);
|
const dataExists = await testData(server, key);
|
||||||
|
|
||||||
if (dataExists) {
|
if (dataExists) {
|
||||||
console.log("Data already stored:", key);
|
console.log('Data already stored:', key);
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
console.log(
|
console.log('Data not stored for server, proceeding to POST:', key, server);
|
||||||
"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;
|
let url: string;
|
||||||
if (server.startsWith("/")) {
|
if (server.startsWith('/')) {
|
||||||
|
// Relative path
|
||||||
url = `${server}/store/${encodeURIComponent(key)}`;
|
url = `${server}/store/${encodeURIComponent(key)}`;
|
||||||
if (ttl !== null) url += `?ttl=${ttl}`;
|
if (ttl !== null) {
|
||||||
|
url += `?ttl=${ttl}`;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Absolute URL
|
||||||
const urlObj = new URL(`${server}/store/${encodeURIComponent(key)}`);
|
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();
|
url = urlObj.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Envoi (POST) avec Fetch
|
// La ligne ci-dessous a été supprimée car le test est fait au-dessus
|
||||||
const response = await fetch(url, {
|
// const testResponse = await testData(url, key); // <-- LIGNE BOGUÉE SUPPRIMÉE
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/octet-stream",
|
|
||||||
},
|
|
||||||
body: value,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
// Send the encrypted data as the raw request body.
|
||||||
// Status 200-299
|
const response = await axios.post(url, value, { // Note: c'est bien un POST sur 'url'
|
||||||
console.log("Data stored successfully:", key);
|
headers: {
|
||||||
return response;
|
'Content-Type': 'application/octet-stream'
|
||||||
} else if (response.status === 409) {
|
},
|
||||||
// Conflit (déjà existant), on retourne null comme avant
|
});
|
||||||
return null;
|
console.log('Data stored successfully:', key);
|
||||||
} else {
|
if (response.status !== 200) {
|
||||||
console.error("Received response status", response.status);
|
console.error('Received response status', response.status);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
return response;
|
||||||
} catch (error) {
|
} 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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function retrieveData(
|
// Fonction retrieveData (inchangée, elle était correcte)
|
||||||
servers: string[],
|
export async function retrieveData(servers: string[], key: string): Promise<ArrayBuffer | null> {
|
||||||
key: string
|
|
||||||
): Promise<ArrayBuffer | null> {
|
|
||||||
for (const server of servers) {
|
for (const server of servers) {
|
||||||
try {
|
try {
|
||||||
const url = server.startsWith("/")
|
const url = server.startsWith('/')
|
||||||
? `${server}/retrieve/${key}`
|
? `${server}/retrieve/${key}`
|
||||||
: new URL(`${server}/retrieve/${key}`).toString();
|
: new URL(`${server}/retrieve/${key}`).toString();
|
||||||
|
|
||||||
console.log("Retrieving data", key, " from:", url);
|
console.log('Retrieving data', key,' from:', url);
|
||||||
|
|
||||||
const response = await fetch(url, { method: "GET" });
|
const response = await axios.get(url, {
|
||||||
|
responseType: 'arraybuffer'
|
||||||
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.status === 200) {
|
||||||
// Transformation en ArrayBuffer
|
if (response.data instanceof ArrayBuffer) {
|
||||||
return await response.arrayBuffer();
|
return response.data;
|
||||||
} else {
|
|
||||||
if (response.status === 404) {
|
|
||||||
console.log(`Data not found on server ${server} for key ${key}`);
|
|
||||||
} else {
|
} else {
|
||||||
console.error(
|
console.error('Server returned non-ArrayBuffer data:', typeof response.data);
|
||||||
`Server ${server} returned status ${response.status}: ${response.statusText}`
|
continue;
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
console.error(`Server ${server} returned status ${response.status}`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Unexpected error retrieving data from ${server}:`, error);
|
if (axios.isAxiosError(error)) {
|
||||||
continue;
|
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;
|
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> {
|
export async function testData(server: string, key: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const testUrl = server.startsWith("/")
|
// Construit l'URL /test/...
|
||||||
|
const testUrl = server.startsWith('/')
|
||||||
? `${server}/test/${encodeURIComponent(key)}`
|
? `${server}/test/${encodeURIComponent(key)}`
|
||||||
: new URL(`${server}/test/${encodeURIComponent(key)}`).toString();
|
: new URL(`${server}/test/${encodeURIComponent(key)}`).toString();
|
||||||
|
|
||||||
// On utilise fetch ici aussi
|
const response = await axios.get(testUrl); // Fait un GET sur /test/...
|
||||||
const response = await fetch(testUrl, { method: "GET" });
|
|
||||||
|
|
||||||
// 200 OK = existe
|
// 200 OK = la donnée existe
|
||||||
return response.status === 200;
|
return response.status === 200;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Erreur réseau
|
if (axios.isAxiosError(error) && error.response?.status === 404) {
|
||||||
console.error("Error testing data:", error);
|
// 404 Not Found = la donnée n'existe pas. C'est une réponse valide.
|
||||||
return false;
|
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