import Services from "./service"; import Database from "./database.service"; /** * Service Worker Controller - Manages SW registration and communication */ export class SWController { private static instance: SWController; private serviceWorkerRegistration: ServiceWorkerRegistration | null = null; private serviceWorkerCheckIntervalId: number | null = null; private constructor() { // Singleton } public static async getInstance(): Promise { if (!SWController.instance) { SWController.instance = new SWController(); } return SWController.instance; } public async init(): Promise { await this.registerServiceWorker("/data.worker.js"); } private async registerServiceWorker(path: string): Promise { if (!("serviceWorker" in navigator)) return; console.log("[SWController] Initializing Service Worker:", path); try { // Nettoyage des anciens workers si nécessaire (logique conservée) const registrations = await navigator.serviceWorker.getRegistrations(); for (const registration of registrations) { const scriptURL = registration.active?.scriptURL || registration.installing?.scriptURL || registration.waiting?.scriptURL; const scope = registration.scope; if ( scope.includes("/src/service-workers/") || (scriptURL && scriptURL.includes("/src/service-workers/")) ) { console.warn(`[SWController] Removing old Service Worker (${scope})`); await registration.unregister(); } } const existingValidWorker = registrations.find((r) => { const url = r.active?.scriptURL || r.installing?.scriptURL || r.waiting?.scriptURL; return url && url.endsWith(path.replace(/^\//, "")); }); if (!existingValidWorker) { console.log("[SWController] Registering new Service Worker"); this.serviceWorkerRegistration = await navigator.serviceWorker.register( path, { type: "module", scope: "/" } ); } else { console.log("[SWController] Service Worker already active"); this.serviceWorkerRegistration = existingValidWorker; await this.serviceWorkerRegistration.update(); } navigator.serviceWorker.addEventListener("message", async (event) => { await this.handleServiceWorkerMessage(event.data); }); // Boucle de scan périodique if (this.serviceWorkerCheckIntervalId) clearInterval(this.serviceWorkerCheckIntervalId); this.serviceWorkerCheckIntervalId = window.setInterval(async () => { const activeWorker = this.serviceWorkerRegistration?.active || (await this.waitForServiceWorkerActivation( this.serviceWorkerRegistration! )); // On récupère les processus via le proxy Services const service = await Services.getInstance(); const payload = await service.getMyProcesses(); if (payload && Object.keys(payload).length !== 0) { activeWorker?.postMessage({ type: "SCAN", payload }); } }, 5000); } catch (error) { console.error("[SWController] Service Worker error:", error); } } private async waitForServiceWorkerActivation( registration: ServiceWorkerRegistration ): Promise { return new Promise((resolve) => { if (registration.active) { resolve(registration.active); } else { const listener = () => { if (registration.active) { navigator.serviceWorker.removeEventListener( "controllerchange", listener ); resolve(registration.active); } }; navigator.serviceWorker.addEventListener("controllerchange", listener); } }); } // ============================================ // MESSAGE HANDLERS // ============================================ private async handleServiceWorkerMessage(message: any) { switch (message.type) { case "TO_DOWNLOAD": await this.handleDownloadList(message.data); break; case "DIFFS_TO_CREATE": await this.handleDiffsToCreate(message.data); break; default: console.warn("[SWController] Unknown message type received:", message); } } private async handleDiffsToCreate(diffs: any[]): Promise { console.log( `[SWController] Creating ${diffs.length} diffs from Service Worker scan` ); try { const db = await Database.getInstance(); await db.saveDiffs(diffs); console.log("[SWController] Diffs created successfully"); } catch (error) { console.error("[SWController] Error creating diffs:", error); } } private async handleDownloadList(downloadList: string[]): Promise { let requestedStateId: string[] = []; // On a besoin de Services pour la logique métier (fetch, network) const service = await Services.getInstance(); for (const hash of downloadList) { const diff = await service.getDiffByValue(hash); if (!diff) { console.warn(`[SWController] Missing a diff for hash ${hash}`); continue; } const processId = diff.process_id; const stateId = diff.state_id; const roles = diff.roles; try { const valueBytes = await service.fetchValueFromStorage(hash); if (valueBytes) { const blob = new Blob([valueBytes], { type: "application/octet-stream", }); await service.saveBlobToDb(hash, blob); document.dispatchEvent( new CustomEvent("newDataReceived", { detail: { processId, stateId, hash }, }) ); } else { console.log( "[SWController] Request data from managers of the process" ); if (!requestedStateId.includes(stateId)) { await service.requestDataFromPeers(processId, [stateId], [roles]); requestedStateId.push(stateId); } } } catch (e) { console.error(e); } } } }