Replace handleSource by registeringAllListeners

This commit is contained in:
NicolasCantu 2025-04-27 16:38:35 +02:00
parent 824a0b88f6
commit eca4d4de85
4 changed files with 361 additions and 89 deletions

View File

@ -1,2 +1,4 @@
export { default as Services } from './services/service';
export { default as Database } from './services/database.service';
export { MessageType } from './models/process.model';
export type { ProfileData, ProfileMessage } from './models/process.model';

View File

@ -21,3 +21,77 @@ export interface INotification {
sendToNotificationPage?: boolean;
path?: string;
}
export enum MessageType {
LISTENING = 'LISTENING',
REQUEST_LINK = 'REQUEST_LINK',
LINK_ACCEPTED = 'LINK_ACCEPTED',
CREATE_PROFILE = 'CREATE_PROFILE',
PROFILE_CREATED = 'PROFILE_CREATED',
CREATE_FOLDER = 'CREATE_FOLDER',
FOLDER_CREATED = 'FOLDER_CREATED',
RETRIEVE_DATA = 'RETRIEVE_DATA',
DATA_RETRIEVED = 'DATA_RETRIEVED',
ERROR = 'ERROR',
RENEW_TOKEN = 'RENEW_TOKEN',
}
export interface ProfileData {
name: string;
surname: string;
email: string;
phone: string;
address: string;
postalCode: string;
city: string;
country: string;
idDocument?: string;
}
export interface ProfileMessage {
type: MessageType.CREATE_PROFILE;
data: ProfileData;
token: string;
}
export interface FolderData {
folderNumber: string;
name: string;
deedType: string;
description: string;
archived_description: string;
status: string;
created_at: string;
updated_at: string;
customers: string[];
documents: string[];
motes: string[];
stakeholders: string[];
}
export interface FolderMessage {
type: MessageType.CREATE_FOLDER;
data: FolderData;
token: string;
}
export interface RetrieveMessage {
type: MessageType.RETRIEVE_DATA;
processId: string,
stateId: string,
token: string;
}
export interface DecryptedDataMessage {
type: MessageType.DATA_RETRIEVED;
data: string,
token: string;
}
export interface TokenData {
token: string;
origin: string;
expiration: number;
createdAt: number;
}

View File

@ -4,7 +4,7 @@ import { addSubscription } from '../../utils/subscription.utils';
import { displayEmojis, generateQRCode, generateCreateBtn, addressToEmoji} from '../../utils/sp-address.utils';
import { getCorrectDOM } from '../../utils/html.utils';
import QrScannerComponent from '../../components/qrcode-scanner/qrcode-scanner-component';
import { navigate, handleSource } from '../../router';
import { navigate, registerAllListeners } from '../../router';
export { QrScannerComponent };
export async function initHomePage(): Promise<void> {
@ -29,17 +29,7 @@ export async function initHomePage(): Promise<void> {
// Add this line to populate the select when the page loads
await populateMemberSelect();
const endpoint = window.location.pathname;
console.log('endpoint', endpoint);
if (endpoint && endpoint !== '/') {
try {
await handleSource(endpoint);
console.log('handleSource', endpoint);
} catch (error) {
console.error('Error handling endpoint:', error);
}
}
await registerAllListeners();
}
//// Modal

View File

@ -3,11 +3,12 @@ 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 { LoginComponent } from './pages/home/home-component';
import { prepareAndSendPairingTx } from './utils/sp-address.utils';
import ModalService from './services/modal.service';
export { Services };
import { MessageType, ProfileData, FolderData } from './models/process.model';
const routes: { [key: string]: string } = {
home: '/src/pages/home/home.html',
process: '/src/pages/process/process.html',
@ -147,14 +148,8 @@ export async function init(): Promise<void> {
await services.restoreProcessesFromDB();
await services.restoreSecretsFromDB();
// If we have a service redirection flag, we intercept it here
const endpoint = window.location.pathname;
if (endpoint) {
await handleSource(endpoint);
} else {
console.log('No endpoint provided');
}
// We register all the event listeners
await registerAllListeners();
if (services.isPaired()) {
await navigate('account');
@ -167,82 +162,293 @@ export async function init(): Promise<void> {
}
}
export async function handleSource(endpoint: string) {
export async function registerAllListeners() {
const services = await Services.getInstance();
const errorResponse = (errorMsg: string, origin: string) => {
window.parent.postMessage(
{
type: MessageType.ERROR,
error: errorMsg
},
origin
);
}
const handleRequestLink = async (event: MessageEvent) => {
if (event.data.type !== MessageType.REQUEST_LINK) {
return;
}
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'
});
try {
if (!result) {
throw new Error('User refused to link');
}
const tokenService = await TokenService.getInstance();
const sessionToken = tokenService.generateSessionToken(event.origin);
switch (endpoint) {
case '/link-service':
window.addEventListener('message', async (event: MessageEvent) => {
console.log("Received message:", event.data);
if (event.data.type === 'REQUEST_LINK') {
console.log("Received link request, showing confirmation modal");
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 votre identité 4NK avec ${event.origin}.</p>
<p>Cette action permettra à ${event.origin} d'intéragir avec votre identité 4NK.</p>
<p>Voulez-vous continuer ?</p>
</div>
`,
confirmText: 'Ajouter un service',
cancelText: 'Annuler'
});
if (services.isPaired()) {
// Device already paired - just renew token
window.parent.postMessage(
{
type: MessageType.RENEW_TOKEN,
sessionToken
},
event.origin
);
} else {
// New device - do pairing process
await prepareAndSendPairingTx();
await services.confirmPairing();
const neuteredDevice = services.dumpNeuteredDevice();
if (!neuteredDevice) {
throw new Error('Failed to get neutered device');
}
window.parent.postMessage(
{
type: MessageType.LINK_ACCEPTED,
payload: JSON.stringify(neuteredDevice),
sessionToken
},
event.origin
);
}
} catch (error) {
const errorMsg = `Failed to link service: ${error}`;
errorResponse(errorMsg, event.origin);
}
}
if (result) {
try {
if (!services.isPaired()) {
await prepareAndSendPairingTx();
await services.confirmPairing();
}
const handleAddProfile = async (event: MessageEvent) => {
if (event.data.type !== MessageType.CREATE_PROFILE) {
return;
}
const tokenService = await TokenService.getInstance();
const services = await Services.getInstance();
const neuteredDevice = services.dumpNeuteredDevice();
if (neuteredDevice) {
window.parent.postMessage(
{
type: 'RESPONSE',
success: true,
payload: JSON.stringify(neuteredDevice)
},
event.origin
);
} else {
throw new Error('Failed to get a neutered device');
}
} catch (error) {
window.parent.postMessage(
{
type: 'RESPONSE',
success: false,
error: 'Failed to complete pairing process'
},
event.origin
);
}
} else {
// The user canceled the link request
console.log("User canceled the linking process");
window.parent.postMessage(
{
type: 'RESPONSE',
success: false,
error: 'User canceled linking'
},
event.origin
);
if (!services.isPaired()) {
const errorMsg = 'Device not paired';
errorResponse(errorMsg, event.origin);
return;
}
try {
const { profileData, token } = event.data;
// Validate the session token
if (!token || !tokenService.validateToken(token, event.origin)) {
throw new Error('Invalid or expired session token');
}
// Create profile
await services.createAndSendProfileTx(profileData);
window.parent.postMessage(
{
type: MessageType.PROFILE_CREATED,
token // Resend the same token
},
event.origin
);
} catch (e) {
const errorMsg = `Failed to create profile: ${e}`;
errorResponse(errorMsg, event.origin);
}
}
const handleAddFolder = async (event: MessageEvent) => {
if (event.data.type !== MessageType.CREATE_FOLDER) {
return;
}
const tokenService = await TokenService.getInstance();
const services = await Services.getInstance();
if (!services.isPaired()) {
const errorMsg = 'Device not paired';
errorResponse(errorMsg, event.origin);
return;
}
try {
const { data, token } = event.data;
if (!token || !tokenService.validateToken(token, event.origin)) {
throw new Error('Invalid or expired session token');
}
await services.createAndSendFolderTx(data);
window.parent.postMessage(
{
type: MessageType.FOLDER_CREATED,
token
},
event.origin
);
} catch (e) {
const errorMsg = `Failed to create folder: ${e}`;
errorResponse(errorMsg, event.origin);
}
}
/// We got a state for some process and return as many clear attributes as we can
const handleDecryptState = async (event: MessageEvent) => {
if (event.data.type !== MessageType.RETRIEVE_DATA) {
return;
}
const tokenService = await TokenService.getInstance();
const services = await Services.getInstance();
if (!services.isPaired()) {
const errorMsg = 'Device not paired';
errorResponse(errorMsg, event.origin);
return;
}
try {
const { processId, stateId, token } = event.data;
if (!token || !tokenService.validateToken(token, event.origin)) {
throw new Error('Invalid or expired session token');
}
// Retrieve the state for the process
const process = await services.getProcess(processId);
const state = services.getStateFromId(process, stateId);
let res: Record<string, string> = {};
if (state) {
// Decrypt all the data we have the key for
for (const attribute of Object.keys(state.pcd_commitment)) {
const decryptedAttribute = await services.decryptAttribute(processId, state, attribute);
if (decryptedAttribute) {
res[attribute] = decryptedAttribute;
}
}
});
console.log('sending message from link-service');
window.parent.postMessage({ type: 'LISTENING', endpoint: endpoint }, '*');
break;
} else {
throw new Error('Unknown state for process', processId);
}
default:
console.log('Unknown endpoint:', endpoint);
window.parent.postMessage(
{
type: MessageType.DATA_RETRIEVED,
data: JSON.stringify(res),
token
},
event.origin
);
} catch (e) {
const errorMsg = `Failed to retrieve data: ${e}`;
errorResponse(errorMsg, event.origin);
}
}
const handleGetProfile = async (event: MessageEvent) => {
if (event.data.type !== MessageType.RETRIEVE_DATA) {
return;
}
const tokenService = await TokenService.getInstance();
const services = await Services.getInstance();
if (!services.isPaired()) {
const errorMsg = 'Device not paired';
errorResponse(errorMsg, event.origin);
return;
}
try {
console.log('📥 [GetProfile] Réception de la demande');
const { sessionToken } = event.data;
if (!sessionToken || !tokenService.validateToken(sessionToken, event.origin)) {
throw new Error('Invalid or expired session token');
}
console.log('🔍 [GetProfile] Récupération des profils');
const processes = await services.getProcesses();
console.log('Processes après résolution:', processes);
let decryptedData: Record<string, string> = {};
const processArray = Array.isArray(processes) ? processes : Object.values(processes);
if (processArray && processArray.length > 0) {
console.log(`📋 Traitement de ${processArray.length} processus`);
for (const process of processArray) {
try {
console.log('Process en cours:', process);
if (process && process.id && !process.certified) {
console.log(`🔄 Traitement du processus non certifié ${process.id}`);
const states = process.states;
if (states && states.length > 0) {
for (const state of states) {
if (state && state.pcd_commitment) {
console.log(`📝 Attributs trouvés dans l'état:`, Object.keys(state.pcd_commitment));
for (const attribute of Object.keys(state.pcd_commitment)) {
try {
const decryptedValue = await services.decryptAttribute(process.id, state, attribute);
if (decryptedValue) {
decryptedData[attribute] = decryptedValue;
console.log(`✅ Attribut déchiffré: ${attribute} = ${decryptedValue}`);
}
} catch (e) {
console.warn(`⚠️ Échec du déchiffrement de ${attribute}:`, e);
}
}
}
}
}
}
} catch (processError) {
console.error(`❌ Erreur lors du traitement du processus:`, processError);
}
}
} else {
console.warn('❌ Aucun processus trouvé ou tableau vide');
}
console.log('📤 [GetProfile] Données déchiffrées:', decryptedData);
window.parent.postMessage(
{
type: MessageType.DATA_RETRIEVED,
data: decryptedData,
sessionToken
},
event.origin
);
} catch (e) {
console.error('❌ Erreur:', e);
errorResponse(`Failed to get profile: ${e}`, event.origin);
}
};
window.addEventListener('message', handleRequestLink);
window.addEventListener('message', handleAddProfile);
window.addEventListener('message', handleAddFolder);
window.addEventListener('message', handleDecryptState);
window.addEventListener('message', handleGetProfile);
window.parent.postMessage({ type: 'LISTENING' }, '*');
}
async function cleanPage() {