feat(sw-controller): implement Service Worker Controller for managing SW registration and communication, including message handling and periodic scanning
This commit is contained in:
parent
77019896e5
commit
8a6e30d226
194
src/services/sw-controller.service.ts
Normal file
194
src/services/sw-controller.service.ts
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
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<SWController> {
|
||||||
|
if (!SWController.instance) {
|
||||||
|
SWController.instance = new SWController();
|
||||||
|
}
|
||||||
|
return SWController.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async init(): Promise<void> {
|
||||||
|
await this.registerServiceWorker("/data.worker.js");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async registerServiceWorker(path: string): Promise<void> {
|
||||||
|
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<ServiceWorker | null> {
|
||||||
|
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<void> {
|
||||||
|
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<void> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user