import Button, { EButtonSize, EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button"; import DragAndDrop from "@Front/Components/DesignSystem/DragAndDrop"; import Typography, { ETypo, ETypoColor } from "@Front/Components/DesignSystem/Typography"; import Module from "@Front/Config/Module"; import PdfService from "@Front/Services/PdfService"; import { FileBlob } from "@Front/Api/Entities/types"; import { ShieldCheckIcon } from "@heroicons/react/24/outline"; import { useRouter } from "next/router"; import React, { useState } from "react"; import MessageBus from "src/sdk/MessageBus"; import classes from "./classes.module.scss"; import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService"; import FileService from "src/common/Api/LeCoffreApi/sdk/FileService"; type IProps = { folderUid: string; }; /** * Convert a File object to FileBlob * @param file - The File object to convert * @returns Promise - The converted FileBlob */ const convertFileToFileBlob = async (file: File): Promise => { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => { const arrayBuffer = reader.result as ArrayBuffer; const uint8Array = new Uint8Array(arrayBuffer); resolve({ type: file.type, data: uint8Array }); }; reader.onerror = () => reject(new Error('Failed to read file')); reader.readAsArrayBuffer(file); }); }; export default function DocumentVerification(props: IProps) { const { folderUid } = props; const router = useRouter(); const [documentToVerify, setDocumentToVerify] = useState(null); const [validationCertificate, setValidationCertificate] = useState(null); const [isVerifying, setIsVerifying] = useState(false); const [verificationResult, setVerificationResult] = useState<{ success: boolean; message: string; details?: string; merkleProof?: string; } | null>(null); const handleDocumentToVerifyChange = (files: File[]) => { if (files.length > 0 && files[0]) { setDocumentToVerify(files[0]); } else { setDocumentToVerify(null); } }; const handleValidationCertificateChange = (files: File[]) => { if (files.length > 0 && files[0]) { setValidationCertificate(files[0]); } else { setValidationCertificate(null); } }; const handleVerifyDocuments = async () => { if (!documentToVerify || !validationCertificate) { console.error("Both documents are required for verification"); return; } setIsVerifying(true); setVerificationResult(null); const messageBus = MessageBus.getInstance(); try { // Here the things we need to verify: // - we can produce the same hash from the document provided than what is in the validation certificate // - the merkle proof is valid with that hash // - the root of the merkle tree is a state id from a commited state in the process // - that process is a file process linked to the right folder // Step 1: Parse the validation certificate const validationData = await PdfService.getInstance().parseCertificate(validationCertificate); // Step 2: Convert File to FileBlob and hash the document using MessageBus const fileBlob = await convertFileToFileBlob(documentToVerify); await messageBus.isReady(); const documentHash = await messageBus.hashDocument(fileBlob, validationData.commitmentId); // Step 3: Compare hashes const hashesMatch = documentHash.toLowerCase() === validationData.documentHash.toLowerCase(); if (!hashesMatch) { throw new Error('Hash du document invalide, le document fourni n\'est pas celui qui a été certifié'); } // Step 4: Verify the merkle proof const merkleProof = validationData.merkleProof; const merkleProofValid = await messageBus.verifyMerkleProof(merkleProof, documentHash); if (!merkleProofValid) { throw new Error('Preuve de Merkle invalide, le document n\'a pas été certifié là où le certificat le prétend'); } // Step 5: Verify that this file process depends on the right folder process // First pin all the validated documents related to the folder const documentProcesses = await DocumentService.getDocuments(); const documents = documentProcesses.filter((process: any) => process.processData.document_status === "VALIDATED" && process.processData.folder.uid === folderUid ); if (!documents || documents.length === 0) { throw new Error(`Aucune demande de document trouvé pour le dossier ${folderUid}`); } // Step 6: verify that the merkle proof match the last commited state for the file process const stateId = JSON.parse(validationData.merkleProof)['root']; let stateIdExists = false; for (const doc of documents) { const processData = doc.processData; for (const file of processData.files) { const fileUid = file.uid; const fileProcess = await FileService.getFileByUid(fileUid); const lastUpdatedStateId = fileProcess.lastUpdatedFileState.state_id; stateIdExists = lastUpdatedStateId === stateId; // we assume that last state is the validated document, that seems reasonable if (stateIdExists) break; } if (stateIdExists) break; } if (!stateIdExists) { throw new Error('La preuve fournie ne correspond à aucun document demandé pour ce dossier.'); } setVerificationResult({ success: true, message: "✅ Vérification réussie ! Le document est authentique et intègre.", }); } catch (error) { console.error("Verification failed:", error); setVerificationResult({ success: false, message: `❌ Erreur lors de la vérification: ${error}`, }); } finally { setIsVerifying(false); } }; const handleBackToFolder = () => { const folderPath = Module.getInstance() .get() .modules.pages.Folder.pages.FolderInformation.props.path.replace("[folderUid]", folderUid); router.push(folderPath); }; const bothDocumentsPresent = documentToVerify && validationCertificate; return (
Vérification de Documents Vérifiez l'intégrité et l'authenticité de vos documents
{documentToVerify && (
✓ {documentToVerify.name}
)}
{validationCertificate && (
✓ {validationCertificate.name}
)}
{!bothDocumentsPresent && (
⚠️ Veuillez sélectionner les deux documents pour procéder à la vérification
)} {verificationResult && (
{verificationResult.message}
)}
); }