ihm_client/src/router.ts

885 lines
34 KiB
TypeScript
Executable File
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// @ts-nocheck
import './4nk.css';
import { initHeader } from '../src/components/header/header';
/*import { initChat } from '../src/pages/chat/chat';*/
import Database from './services/database.service';
import Services from './services/service';
import TokenService from './services/token';
import { cleanSubscriptions } from './utils/subscription.utils';
import { prepareAndSendPairingTx } from './utils/sp-address.utils';
import ModalService from './services/modal.service';
import { MessageType } from './models/process.model';
import { splitPrivateData, isValid32ByteHex } from './utils/service.utils';
import { MerkleProofResult } from 'pkg/sdk_client';
// ===================================================================================
// ## 🧭 1. Routage de Page (Navigation)
// ===================================================================================
const routes: { [key: string]: string } = {
home: '/src/pages/home/home.html',
process: '/src/pages/process/process.html',
'process-element': '/src/pages/process-element/process-element.html',
account: '/src/pages/account/account.html',
chat: '/src/pages/chat/chat.html',
signature: '/src/pages/signature/signature.html',
};
export let currentRoute = '';
export async function navigate(path: string) {
console.log(`[Router] 🧭 Navigation vers: ${path}`);
cleanSubscriptions();
cleanPage();
path = path.replace(/^\//, ''); // Retire le slash de début
// Gère les chemins simples ou avec paramètres (ex: 'process-element/123_456')
if (path.includes('/')) {
const parsedPath = path.split('/')[0];
if (!routes[parsedPath]) {
console.warn(`[Router] ⚠️ Route inconnue "${parsedPath}", redirection vers 'home'.`);
path = 'home';
}
} else if (!routes[path]) {
console.warn(`[Router] ⚠️ Route inconnue "${path}", redirection vers 'home'.`);
path = 'home';
}
await handleLocation(path);
}
async function handleLocation(path: string) {
// 1. Log de démarrage
console.log(`[Router:handleLocation] 🧭 Gestion de la nouvelle route: ${path}`);
const parsedPath = path.split('/');
const baseRoute = parsedPath[0];
currentRoute = baseRoute;
const routeHtml = routes[baseRoute] || routes['home'];
const content = document.getElementById('containerId');
if (!content) {
console.error('[Router] 💥 Erreur critique: div #containerId non trouvée !');
return;
} else {
// console.debug('[Router:handleLocation] ✅ conteneur #containerId trouvé.');
}
// --- Injection de Contenu ---
if (baseRoute === 'home') {
console.log('[Router:handleLocation] 🏠 Route "home" détectée. Importation dynamique de <login-4nk-component>...');
const { LoginComponent } = await import('./pages/home/home-component');
if (!customElements.get('login-4nk-component')) {
customElements.define('login-4nk-component', LoginComponent);
console.log('[Router:handleLocation] <login-4nk-component> défini.');
}
const container = document.querySelector('#containerId');
const accountComponent = document.createElement('login-4nk-component');
accountComponent.setAttribute('style', 'width: 100vw; height: 100vh; position: relative; grid-row: 2;');
if (container) {
container.appendChild(accountComponent);
// 4. Log de succès (le plus important pour ton bug)
console.log('[Router:handleLocation] ✅ <login-4nk-component> ajouté au DOM.');
}
} else if (baseRoute !== 'process') {
// 2. Log pour les autres pages
console.log(`[Router:handleLocation] 📄 Fetching et injection de HTML pour: ${routeHtml}`);
const html = await fetch(routeHtml).then((data) => data.text());
content.innerHTML = html;
console.log(`[Router:handleLocation] ✅ HTML pour "${baseRoute}" injecté.`);
} // (Le cas 'process' est géré plus bas dans le switch)
await new Promise(requestAnimationFrame);
console.log('[Router:handleLocation] 💉 Injection du header...');
injectHeader(); // --- Logique Spécifique à la Page (Lazy Loading) ---
console.log(`[Router] Initialisation de la logique pour la route: ${baseRoute}`);
switch (baseRoute) {
case 'process':
console.log('[Router:switch] 📦 Chargement de ProcessListComponent...');
const { ProcessListComponent } = await import('./pages/process/process-list-component');
const container2 = document.querySelector('#containerId');
const processListComponent = document.createElement('process-list-4nk-component');
if (!customElements.get('process-list-4nk-component')) {
console.log('[Router:switch] Définition de <process-list-4nk-component>...');
customElements.define('process-list-4nk-component', ProcessListComponent);
}
processListComponent.setAttribute('style', 'height: 100vh; position: relative; grid-row: 2; grid-column: 4;');
if (container2) {
container2.appendChild(processListComponent);
console.log('[Router:switch] ✅ <process-list-4nk-component> ajouté au DOM.');
}
break;
case 'process-element':
console.log(`[Router:switch] 📦 Chargement de <process-4nk-component>...`);
if (parsedPath[1]) {
// 1. Importe le composant (juste pour être sûr qu'il est défini)
const { ProcessElementComponent } = await import('./pages/process-element/process-component');
if (!customElements.get('process-4nk-component')) {
customElements.define('process-4nk-component', ProcessElementComponent);
}
// 2. Sépare les IDs
const [processId, stateId] = parsedPath[1].split('_');
// 3. Crée l'élément et passe les attributs
const container = document.querySelector('#containerId');
const processElement = document.createElement('process-4nk-component');
processElement.setAttribute('process-id', processId);
processElement.setAttribute('state-id', stateId);
if (container) {
container.appendChild(processElement);
console.log(`[Router:switch] ✅ <process-4nk-component> ajouté au DOM pour ${processId}_${stateId}`);
}
} else {
console.error('[Router] 💥 Route process-element appelée sans ID (ex: process-element/processId_stateId)');
navigate('process');
}
break;
case 'account':
console.log('[Router:switch] 📦 Chargement de AccountComponent...');
const { AccountComponent } = await import('./pages/account/account-component');
const accountContainer = document.querySelector('.parameter-list');
if (accountContainer) {
if (!customElements.get('account-component')) {
console.log('[Router:switch] Définition de <account-component>...');
customElements.define('account-component', AccountComponent);
}
const accountComponent = document.createElement('account-component');
accountContainer.appendChild(accountComponent);
console.log('[Router:switch] ✅ <account-component> ajouté au DOM.');
} else {
console.warn('[Router:switch] ⚠️ Impossible de trouver ".parameter-list" pour injecter AccountComponent.');
}
break;
case 'signature':
console.log('[Router:switch] 📦 Chargement de SignatureComponent...');
const { SignatureComponent } = await import('./pages/signature/signature-component');
const container = document.querySelector('.group-list');
if (container) {
if (!customElements.get('signature-component')) {
console.log('[Router:switch] Définition de <signature-component>...');
customElements.define('signature-component', SignatureComponent);
}
const signatureComponent = document.createElement('signature-component');
container.appendChild(signatureComponent);
console.log('[Router:switch] ✅ <signature-component> ajouté au DOM.');
} else {
console.warn('[Router:switch] ⚠️ Impossible de trouver ".group-list" pour injecter SignatureComponent.');
}
break;
// Log pour les cas non gérés
case 'home':
console.log('[Router:switch] Route "home". La logique a déjà été gérée avant le switch.');
break;
default:
console.log(`[Router:switch] Route "${baseRoute}" n'a pas de logique d'initialisation spécifique dans le switch.`);
}
}
window.onpopstate = async () => {
console.log('[Router] 🔄 Changement d\'état "popstate" (bouton retour/avant)');
const services = await Services.getInstance();
if (!services.isPaired()) {
handleLocation('home');
} else {
handleLocation('process');
}
};
// --- Fin de la section Routage de Page ---
// ===================================================================================
// ===================================================================================
// ## 🚀 2. Initialisation de l'Application
// ===================================================================================
export async function init(): Promise<void> {
console.log("[Router:Init] 🚀 Démarrage de l'application...");
try {
const services = await Services.getInstance();
(window as any).myService = services; // Pour débogage manuel
console.log('[Router:Init] 📦 Initialisation de la base de données (IndexedDB)...');
const db = await Database.getInstance();
db.registerServiceWorker('/src/service-workers/database.worker.js');
console.log("[Router:Init] 📱 Vérification de l'appareil (device)...");
const device = await services.getDeviceFromDatabase();
console.log('🚀 ~ setTimeout ~ device:', device); // Log original gardé
if (!device) {
console.log("[Router:Init] ✨ Aucun appareil trouvé. Création d'un nouvel appareil...");
await services.createNewDevice();
} else {
console.log("[Router:Init] 🔄 Restauration de l'appareil depuis la BDD...");
services.restoreDevice(device);
}
console.log("[Router:Init] 💾 Restauration de l'état (processus et secrets) depuis la BDD...");
await services.restoreProcessesFromDB();
await services.restoreSecretsFromDB();
console.log('[Router:Init] 🔌 Connexion à tous les relais...');
await services.connectAllRelays();
// S'enregistre comme "serveur" API si nous sommes dans une iframe
if (window.self !== window.top) {
console.log('[Router:Init] 📡 Nous sommes dans une iframe. Enregistrement des listeners API...');
await registerAllListeners();
} else {
console.log('[Router:Init] Exécution en mode standalone (pas dans une iframe).');
}
console.log("[Router:Init] 🧭 Vérification du statut d'appairage pour la navigation...");
if (services.isPaired()) {
console.log('[Router:Init] ✅ Appairé. Navigation vers "process".');
await navigate('process');
} else {
console.log('[Router:Init] ❌ Non appairé. Navigation vers "home".');
await navigate('home');
}
} catch (error) {
console.error("[Router:Init] 💥 ERREUR CRITIQUE PENDANT L'INITIALISATION:", error);
await navigate('home');
}
}
// --- Fin de la section Initialisation ---
// ===================================================================================
// ===================================================================================
// ## 📡 3. API (Message Listeners pour Iframe)
// ===================================================================================
export async function registerAllListeners() {
console.log('[Router:API] 🎧 Enregistrement des gestionnaires de messages (postMessage)...');
const services = await Services.getInstance();
const tokenService = await TokenService.getInstance();
/**
* Fonction centralisée pour envoyer des réponses d'erreur à la fenêtre parente (l'application A).
*/
const errorResponse = (errorMsg: string, origin: string, messageId?: string) => {
console.error(`[Router:API] 📤 Envoi Erreur: ${errorMsg} (Origine: ${origin}, MsgID: ${messageId})`);
window.parent.postMessage(
{
type: MessageType.ERROR,
error: errorMsg,
messageId,
},
origin,
);
};
// --- Définitions des gestionnaires (Handlers) ---
const handleRequestLink = async (event: MessageEvent) => {
console.log(`[Router:API] 📨 Message ${MessageType.REQUEST_LINK} reçu de ${event.origin}`);
const modalService = await ModalService.getInstance();
const result = await modalService.showConfirmationModal(
{
title: 'Confirmation de liaison',
content: `
<div class="modal-confirmation">
<h3>Liaison avec ${event.origin}</h3>
<p>Vous êtes sur le point de lier l'identité numérique de la clé securisée propre à votre appareil avec ${event.origin}.</p>
<p>Cette action permettra à ${event.origin} d'intéragir avec votre appareil.</p>
<p>Voulez-vous continuer ?</p>
</div>
`,
confirmText: 'Ajouter un service',
cancelText: 'Annuler',
},
true,
);
if (!result) {
throw new Error('Failed to pair device: User refused to link');
}
const tokens = await tokenService.generateSessionToken(event.origin);
window.parent.postMessage(
{
type: MessageType.LINK_ACCEPTED,
accessToken: tokens.accessToken,
refreshToken: tokens.refreshToken,
messageId: event.data.messageId,
},
event.origin,
);
console.log(`[Router:API] ✅ ${MessageType.REQUEST_LINK} accepté et jetons envoyés.`);
};
const handleCreatePairing = async (event: MessageEvent) => {
console.log(`[Router:API] 📨 Message ${MessageType.CREATE_PAIRING} reçu`);
if (services.isPaired()) {
throw new Error('Device already paired — ignoring CREATE_PAIRING request');
}
const { accessToken } = event.data;
if (!accessToken || !(await tokenService.validateToken(accessToken, event.origin))) {
throw new Error('Invalid or expired session token');
}
console.log("[Router:API] 🚀 Démarrage du processus d'appairage...");
const myAddress = services.getDeviceAddress();
console.log('[Router:API] 1/7: Création du processus de pairing...');
const createPairingProcessReturn = await services.createPairingProcess('', [myAddress]);
const pairingId = createPairingProcessReturn.updated_process?.process_id;
const stateId = createPairingProcessReturn.updated_process?.current_process?.states[0]?.state_id as string;
if (!pairingId || !stateId) {
throw new Error('Pairing process creation failed to return valid IDs');
}
console.log(`[Router:API] 2/7: Processus ${pairingId} créé.`);
console.log("[Router:API] 3/7: Enregistrement local de l'appareil...");
services.pairDevice(pairingId, [myAddress]);
console.log('[Router:API] 4/7: Traitement du retour (handleApiReturn)...');
await services.handleApiReturn(createPairingProcessReturn);
console.log('[Router:API] 5/7: Création de la mise à jour PRD...');
const createPrdUpdateReturn = await services.createPrdUpdate(pairingId, stateId);
await services.handleApiReturn(createPrdUpdateReturn);
console.log('[Router:API] 6/7: Approbation du changement...');
const approveChangeReturn = await services.approveChange(pairingId, stateId);
await services.handleApiReturn(approveChangeReturn);
console.log('[Router:API] 7/7: Confirmation finale du pairing...');
await services.confirmPairing();
console.log('[Router:API] 🎉 Appairage terminé avec succès !');
const successMsg = {
type: MessageType.PAIRING_CREATED,
pairingId,
messageId: event.data.messageId,
};
window.parent.postMessage(successMsg, event.origin);
};
const handleGetMyProcesses = async (event: MessageEvent) => {
console.log(`[Router:API] 📨 Message ${MessageType.GET_MY_PROCESSES} reçu`);
if (!services.isPaired()) throw new Error('Device not paired');
const { accessToken } = event.data;
if (!accessToken || !(await tokenService.validateToken(accessToken, event.origin))) {
throw new Error('Invalid or expired session token');
}
const myProcesses = await services.getMyProcesses();
window.parent.postMessage(
{
type: MessageType.GET_MY_PROCESSES,
myProcesses,
messageId: event.data.messageId,
},
event.origin,
);
};
const handleGetProcesses = async (event: MessageEvent) => {
console.log(`[Router:API] 📨 Message ${MessageType.GET_PROCESSES} reçu`);
if (!services.isPaired()) throw new Error('Device not paired');
const { accessToken } = event.data;
if (!accessToken || !(await tokenService.validateToken(accessToken, event.origin))) {
throw new Error('Invalid or expired session token');
}
const processes = await services.getProcesses();
window.parent.postMessage(
{
type: MessageType.PROCESSES_RETRIEVED,
processes,
messageId: event.data.messageId,
},
event.origin,
);
};
const handleDecryptState = async (event: MessageEvent) => {
console.log(`[Router:API] 📨 Message ${MessageType.RETRIEVE_DATA} reçu`);
if (!services.isPaired()) throw new Error('Device not paired');
const { processId, stateId, accessToken } = event.data;
if (!accessToken || !(await tokenService.validateToken(accessToken, event.origin))) {
throw new Error('Invalid or expired session token');
}
const process = await services.getProcess(processId);
if (!process) throw new Error("Can't find process");
const state = services.getStateFromId(process, stateId);
if (!state) throw new Error(`Unknown state ${stateId} for process ${processId}`);
console.log(`[Router:API] 🔐 Démarrage du déchiffrement pour ${processId}:${stateId}`);
await services.ensureConnections(process, stateId);
const res: Record<string, any> = {};
for (const attribute of Object.keys(state.pcd_commitment)) {
if (attribute === 'roles' || (state.public_data && state.public_data[attribute])) {
continue;
}
const decryptedAttribute = await services.decryptAttribute(processId, state, attribute);
if (decryptedAttribute) {
res[attribute] = decryptedAttribute;
}
}
console.log(`[Router:API] ✅ Déchiffrement terminé pour ${processId}:${stateId}. ${Object.keys(res).length} attribut(s) déchiffré(s).`);
window.parent.postMessage(
{
type: MessageType.DATA_RETRIEVED,
data: res,
messageId: event.data.messageId,
},
event.origin,
);
};
const handleValidateToken = async (event: MessageEvent) => {
console.log(`[Router:API] 📨 Message ${MessageType.VALIDATE_TOKEN} reçu`);
const accessToken = event.data.accessToken;
const refreshToken = event.data.refreshToken;
if (!accessToken || !refreshToken) {
throw new Error('Missing access, refresh token or both');
}
const isValid = await tokenService.validateToken(accessToken, event.origin);
console.log(`[Router:API] 🔑 Validation Jeton: ${isValid}`);
window.parent.postMessage(
{
type: MessageType.VALIDATE_TOKEN,
accessToken: accessToken,
refreshToken: refreshToken,
isValid: isValid,
messageId: event.data.messageId,
},
event.origin,
);
};
const handleRenewToken = async (event: MessageEvent) => {
console.log(`[Router:API] 📨 Message ${MessageType.RENEW_TOKEN} reçu`);
const refreshToken = event.data.refreshToken;
if (!refreshToken) throw new Error('No refresh token provided');
const newAccessToken = await tokenService.refreshAccessToken(refreshToken, event.origin);
if (!newAccessToken) throw new Error('Failed to refresh token (invalid refresh token)');
console.log(`[Router:API] 🔑 Jeton d'accès renouvelé.`);
window.parent.postMessage(
{
type: MessageType.RENEW_TOKEN,
accessToken: newAccessToken,
refreshToken: refreshToken,
messageId: event.data.messageId,
},
event.origin,
);
};
const handleGetPairingId = async (event: MessageEvent) => {
console.log(`[Router:API] 📨 Message ${MessageType.GET_PAIRING_ID} reçu`);
if (!services.isPaired()) throw new Error('Device not paired');
const { accessToken } = event.data;
if (!accessToken || !(await tokenService.validateToken(accessToken, event.origin))) {
throw new Error('Invalid or expired session token');
}
const userPairingId = services.getPairingProcessId();
window.parent.postMessage(
{
type: MessageType.GET_PAIRING_ID,
userPairingId,
messageId: event.data.messageId,
},
event.origin,
);
};
const handleCreateProcess = async (event: MessageEvent) => {
console.log(`[Router:API] 📨 Message ${MessageType.CREATE_PROCESS} reçu`);
if (!services.isPaired()) throw new Error('Device not paired');
const { processData, privateFields, roles, accessToken } = event.data;
if (!accessToken || !(await tokenService.validateToken(accessToken, event.origin))) {
throw new Error('Invalid or expired session token');
}
console.log('[Router:API] 🚀 Démarrage de la création de processus standard...');
const { privateData, publicData } = splitPrivateData(processData, privateFields);
console.log('[Router:API] 1/2: Création du processus...');
const createProcessReturn = await services.createProcess(privateData, publicData, roles);
if (!createProcessReturn.updated_process) {
throw new Error('Empty updated_process in createProcessReturn');
}
const processId = createProcessReturn.updated_process.process_id;
const process = createProcessReturn.updated_process.current_process;
const stateId = process.states[0].state_id;
console.log(`[Router:API] 2/2: Processus ${processId} créé. Traitement...`);
await services.handleApiReturn(createProcessReturn);
console.log(`[Router:API] 🎉 Processus ${processId} créé.`);
const res = {
processId,
process,
processData,
};
window.parent.postMessage(
{
type: MessageType.PROCESS_CREATED,
processCreated: res,
messageId: event.data.messageId,
},
event.origin,
);
};
const handleNotifyUpdate = async (event: MessageEvent) => {
console.log(`[Router:API] 📨 Message ${MessageType.NOTIFY_UPDATE} reçu`);
if (!services.isPaired()) throw new Error('Device not paired');
const { processId, stateId, accessToken } = event.data;
if (!accessToken || !(await tokenService.validateToken(accessToken, event.origin))) {
throw new Error('Invalid or expired session token');
}
if (!isValid32ByteHex(stateId)) throw new Error('Invalid state id');
const res = await services.createPrdUpdate(processId, stateId);
await services.handleApiReturn(res);
window.parent.postMessage(
{
type: MessageType.UPDATE_NOTIFIED,
messageId: event.data.messageId,
},
event.origin,
);
};
const handleValidateState = async (event: MessageEvent) => {
console.log(`[Router:API] 📨 Message ${MessageType.VALIDATE_STATE} reçu`);
if (!services.isPaired()) throw new Error('Device not paired');
const { processId, stateId, accessToken } = event.data;
if (!accessToken || !(await tokenService.validateToken(accessToken, event.origin))) {
throw new Error('Invalid or expired session token');
}
const res = await services.approveChange(processId, stateId);
await services.handleApiReturn(res);
window.parent.postMessage(
{
type: MessageType.STATE_VALIDATED,
validatedProcess: res.updated_process,
messageId: event.data.messageId,
},
event.origin,
);
};
const handleUpdateProcess = async (event: MessageEvent) => {
console.log(`[Router:API] 📨 Message ${MessageType.UPDATE_PROCESS} reçu`);
if (!services.isPaired()) throw new Error('Device not paired');
const { processId, newData, privateFields, roles, accessToken } = event.data;
if (!accessToken || !(await tokenService.validateToken(accessToken, event.origin))) {
throw new Error('Invalid or expired session token');
}
const process = await services.getProcess(processId);
if (!process) throw new Error('Process not found');
// --- Logique complexe de gestion d'état ---
// (Cette logique est très dense et pourrait être déplacée dans le service)
console.log(`[Router:API] 🔄 Calcul de la diff pour la mise à jour de ${processId}...`);
let lastState = services.getLastCommitedState(process);
if (!lastState) {
console.warn(`[Router:API] ⚠️ Processus ${processId} n'a pas d'état "commited". Tentative d'auto-approbation du 1er état...`);
const firstState = process.states[0];
const roles = firstState.roles;
if (services.rolesContainsUs(roles)) {
const approveChangeRes = await services.approveChange(processId, firstState.state_id);
await services.handleApiReturn(approveChangeRes);
const prdUpdateRes = await services.createPrdUpdate(processId, firstState.state_id);
await services.handleApiReturn(prdUpdateRes);
} else {
if (firstState.validation_tokens.length > 0) {
const res = await services.createPrdUpdate(processId, firstState.state_id);
await services.handleApiReturn(res);
}
}
await new Promise((resolve) => setTimeout(resolve, 2000)); // Attente arbitraire
lastState = services.getLastCommitedState(process);
if (!lastState) {
throw new Error("Process doesn't have a commited state yet");
}
}
const lastStateIndex = services.getLastCommitedStateIndex(process);
if (lastStateIndex === null) throw new Error("Process doesn't have a commited state yet");
const privateData: Record<string, any> = {};
const publicData: Record<string, any> = {};
for (const field of Object.keys(newData)) {
if (lastState.public_data[field]) {
publicData[field] = newData[field];
continue;
}
if (privateFields.includes(field)) {
privateData[field] = newData[field];
continue;
}
for (let i = lastStateIndex; i >= 0; i--) {
const state = process.states[i];
if (state.pcd_commitment[field]) {
privateData[field] = newData[field];
break;
} else {
continue;
}
}
if (privateData[field]) continue;
publicData[field] = newData[field];
}
console.log(`[Router:API] 🔄 Envoi de la mise à jour à services.updateProcess...`);
const res = await services.updateProcess(process, privateData, publicData, roles);
await services.handleApiReturn(res);
window.parent.postMessage(
{
type: MessageType.PROCESS_UPDATED,
updatedProcess: res.updated_process,
messageId: event.data.messageId,
},
event.origin,
);
};
const handleDecodePublicData = async (event: MessageEvent) => {
console.log(`[Router:API] 📨 Message ${MessageType.DECODE_PUBLIC_DATA} reçu`);
if (!services.isPaired()) throw new Error('Device not paired');
const { accessToken, encodedData } = event.data;
if (!accessToken || !(await tokenService.validateToken(accessToken, event.origin))) {
throw new Error('Invalid or expired session token');
}
const decodedData = services.decodeValue(encodedData);
window.parent.postMessage(
{
type: MessageType.PUBLIC_DATA_DECODED,
decodedData,
messageId: event.data.messageId,
},
event.origin,
);
};
const handleHashValue = async (event: MessageEvent) => {
console.log(`[Router:API] 📨 Message ${MessageType.HASH_VALUE} reçu`);
const { accessToken, commitedIn, label, fileBlob } = event.data;
if (!accessToken || !(await tokenService.validateToken(accessToken, event.origin))) {
throw new Error('Invalid or expired session token');
}
const hash = services.getHashForFile(commitedIn, label, fileBlob);
window.parent.postMessage(
{
type: MessageType.VALUE_HASHED,
hash,
messageId: event.data.messageId,
},
event.origin,
);
};
const handleGetMerkleProof = async (event: MessageEvent) => {
console.log(`[Router:API] 📨 Message ${MessageType.GET_MERKLE_PROOF} reçu`);
const { accessToken, processState, attributeName } = event.data;
if (!accessToken || !(await tokenService.validateToken(accessToken, event.origin))) {
throw new Error('Invalid or expired session token');
}
const proof = services.getMerkleProofForFile(processState, attributeName);
window.parent.postMessage(
{
type: MessageType.MERKLE_PROOF_RETRIEVED,
proof,
messageId: event.data.messageId,
},
event.origin,
);
};
const handleValidateMerkleProof = async (event: MessageEvent) => {
console.log(`[Router:API] 📨 Message ${MessageType.VALIDATE_MERKLE_PROOF} reçu`);
const { accessToken, merkleProof, documentHash } = event.data;
if (!accessToken || !(await tokenService.validateToken(accessToken, event.origin))) {
throw new Error('Invalid or expired session token');
}
let parsedMerkleProof: MerkleProofResult;
try {
parsedMerkleProof = JSON.parse(merkleProof);
} catch (e) {
throw new Error('Provided merkleProof is not a valid json object');
}
const res = services.validateMerkleProof(parsedMerkleProof, documentHash);
window.parent.postMessage(
{
type: MessageType.MERKLE_PROOF_VALIDATED,
isValid: res,
messageId: event.data.messageId,
},
event.origin,
);
};
// --- Le "Switchyard" : il reçoit tous les messages et les dispatche ---
window.removeEventListener('message', handleMessage);
window.addEventListener('message', handleMessage);
async function handleMessage(event: MessageEvent) {
try {
switch (event.data.type) {
case MessageType.REQUEST_LINK:
await handleRequestLink(event);
break;
case MessageType.CREATE_PAIRING:
await handleCreatePairing(event);
break;
case MessageType.GET_MY_PROCESSES:
await handleGetMyProcesses(event);
break;
case MessageType.GET_PROCESSES:
await handleGetProcesses(event);
break;
case MessageType.RETRIEVE_DATA:
await handleDecryptState(event);
break;
case MessageType.VALIDATE_TOKEN:
await handleValidateToken(event);
break;
case MessageType.RENEW_TOKEN:
await handleRenewToken(event);
break;
case MessageType.GET_PAIRING_ID:
await handleGetPairingId(event);
break;
case MessageType.CREATE_PROCESS:
await handleCreateProcess(event);
break;
case MessageType.NOTIFY_UPDATE:
await handleNotifyUpdate(event);
break;
case MessageType.VALIDATE_STATE:
await handleValidateState(event);
break;
case MessageType.UPDATE_PROCESS:
await handleUpdateProcess(event);
break;
case MessageType.DECODE_PUBLIC_DATA:
await handleDecodePublicData(event);
break;
case MessageType.HASH_VALUE:
await handleHashValue(event);
break;
case MessageType.GET_MERKLE_PROOF:
await handleGetMerkleProof(event);
break;
case MessageType.VALIDATE_MERKLE_PROOF:
await handleValidateMerkleProof(event);
break;
default:
console.warn('[Router:API] ⚠️ Message non géré reçu:', event.data);
}
} catch (error) {
const errorMsg = `[Router:API] 💥 Erreur de haut niveau: ${error.message || error}`;
errorResponse(errorMsg, event.origin, event.data.messageId);
}
}
window.parent.postMessage(
{
type: MessageType.LISTENING,
},
'*',
);
console.log('[Router:API] ✅ Tous les listeners sont actifs. Envoi du message LISTENING au parent.');
}
// --- Fonctions utilitaires de la page ---
async function cleanPage() {
const container = document.querySelector('#containerId');
if (container) container.innerHTML = '';
}
async function injectHeader() {
const headerContainer = document.getElementById('header-container');
if (headerContainer) {
const headerHtml = await fetch('/src/components/header/header.html').then((res) => res.text());
headerContainer.innerHTML = headerHtml;
const script = document.createElement('script');
script.src = '/src/components/header/header.ts';
script.type = 'module';
document.head.appendChild(script);
initHeader();
}
}
(window as any).navigate = navigate;
// Gère les événements de navigation personnalisés (ex: depuis le header)
document.addEventListener('navigate', (e: Event) => {
const event = e as CustomEvent<{ page: string; processId?: string }>;
console.log(`[Router] 🧭 Événement de navigation personnalisé reçu: ${event.detail.page}`);
if (event.detail.page === 'chat') {
// Logique spécifique pour 'chat'
const container = document.querySelector('.container');
if (container) container.innerHTML = '';
//initChat();
const chatElement = document.querySelector('chat-element');
if (chatElement) {
chatElement.setAttribute('process-id', event.detail.processId || '');
}
} else {
// Gère les autres navigations personnalisées
navigate(event.detail.page);
}
});
// --- Fin de la section API ---
// ===================================================================================