253 lines
7.9 KiB
TypeScript
253 lines
7.9 KiB
TypeScript
import { jsPDF } from "jspdf";
|
|
|
|
interface SignatureProofData {
|
|
document_name: string;
|
|
signer_name: string;
|
|
signer_email: string;
|
|
signed_at: string;
|
|
signature_hash: string;
|
|
verification_url: string;
|
|
qr_code_data_url: string;
|
|
certificate_info: {
|
|
issuer: string;
|
|
subject: string;
|
|
valid_from: string;
|
|
valid_until: string;
|
|
serial_number: string;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Génère un PDF "Preuve de Signature" avec QR code
|
|
*/
|
|
export async function generateSignatureProofPDF(data: SignatureProofData): Promise<Blob> {
|
|
const doc = new jsPDF({
|
|
orientation: "portrait",
|
|
unit: "mm",
|
|
format: "a4",
|
|
});
|
|
|
|
const pageWidth = doc.internal.pageSize.getWidth();
|
|
const pageHeight = doc.internal.pageSize.getHeight();
|
|
const margin = 20;
|
|
|
|
// Couleurs Odentas
|
|
const primaryColor: [number, number, number] = [99, 102, 241]; // Indigo
|
|
const successColor: [number, number, number] = [34, 197, 94]; // Green-500
|
|
const textColor: [number, number, number] = [30, 41, 59]; // Slate-800
|
|
const lightGray: [number, number, number] = [248, 250, 252]; // Slate-50
|
|
const borderGray: [number, number, number] = [226, 232, 240]; // Slate-200
|
|
|
|
// En-tête avec logo - Version professionnelle
|
|
doc.setFillColor(...primaryColor);
|
|
doc.rect(0, 0, pageWidth, 45, "F");
|
|
|
|
doc.setTextColor(255, 255, 255);
|
|
doc.setFontSize(28);
|
|
doc.setFont("helvetica", "bold");
|
|
doc.text("Odentas Sign", pageWidth / 2, 22, { align: "center" });
|
|
|
|
doc.setFontSize(11);
|
|
doc.setFont("helvetica", "normal");
|
|
doc.text("Certificat de Signature Électronique", pageWidth / 2, 32, { align: "center" });
|
|
|
|
// Badge "VALIDE"
|
|
doc.setFillColor(...successColor);
|
|
const badgeWidth = 30;
|
|
const badgeX = (pageWidth - badgeWidth) / 2;
|
|
doc.roundedRect(badgeX, 36, badgeWidth, 6, 1.5, 1.5, "F");
|
|
doc.setFontSize(8);
|
|
doc.setFont("helvetica", "bold");
|
|
doc.text("SIGNATURE VALIDE", pageWidth / 2, 40, { align: "center" });
|
|
|
|
// Titre principal
|
|
doc.setTextColor(...textColor);
|
|
doc.setFontSize(20);
|
|
doc.setFont("helvetica", "bold");
|
|
doc.text("Preuve d'Authenticité et d'Intégrité", margin, 62);
|
|
|
|
// Zone d'information document - Version professionnelle
|
|
let y = 72;
|
|
doc.setFillColor(...lightGray);
|
|
doc.setDrawColor(...borderGray);
|
|
doc.setLineWidth(0.5);
|
|
doc.roundedRect(margin, y, pageWidth - 2 * margin, 50, 3, 3, "FD");
|
|
|
|
doc.setFontSize(9);
|
|
doc.setFont("helvetica", "bold");
|
|
doc.setTextColor(...primaryColor);
|
|
doc.text("INFORMATIONS DU DOCUMENT", margin + 5, y + 8);
|
|
|
|
doc.setFont("helvetica", "normal");
|
|
doc.setTextColor(...textColor);
|
|
doc.setFontSize(8.5);
|
|
|
|
doc.text(`Document :`, margin + 5, y + 17);
|
|
doc.setFont("helvetica", "bold");
|
|
doc.text(data.document_name, margin + 28, y + 17);
|
|
|
|
doc.setFont("helvetica", "normal");
|
|
doc.text(`Signé le :`, margin + 5, y + 25);
|
|
doc.setFont("helvetica", "bold");
|
|
doc.text(
|
|
new Date(data.signed_at).toLocaleString("fr-FR", {
|
|
day: "2-digit",
|
|
month: "long",
|
|
year: "numeric",
|
|
hour: "2-digit",
|
|
minute: "2-digit",
|
|
}),
|
|
margin + 28,
|
|
y + 25
|
|
);
|
|
|
|
doc.setFont("helvetica", "normal");
|
|
doc.text(`Signataire :`, margin + 5, y + 33);
|
|
doc.setFont("helvetica", "bold");
|
|
doc.text(data.signer_name, margin + 28, y + 33);
|
|
|
|
doc.setFont("helvetica", "normal");
|
|
doc.text(`Email :`, margin + 5, y + 41);
|
|
doc.setFont("helvetica", "bold");
|
|
doc.text(data.signer_email, margin + 28, y + 41);
|
|
|
|
// QR Code (centré et encadré)
|
|
y = 132;
|
|
const qrSize = 70;
|
|
const qrX = (pageWidth - qrSize) / 2;
|
|
|
|
// Fond et bordure pour le QR code
|
|
doc.setFillColor(255, 255, 255);
|
|
doc.setDrawColor(...borderGray);
|
|
doc.setLineWidth(1);
|
|
doc.roundedRect(qrX - 5, y - 5, qrSize + 10, qrSize + 10, 3, 3, "FD");
|
|
|
|
doc.addImage(data.qr_code_data_url, "PNG", qrX, y, qrSize, qrSize);
|
|
|
|
doc.setFontSize(11);
|
|
doc.setFont("helvetica", "bold");
|
|
doc.setTextColor(...primaryColor);
|
|
doc.text("Vérifiez cette signature", pageWidth / 2, y + qrSize + 12, { align: "center" });
|
|
|
|
doc.setFontSize(8);
|
|
doc.setFont("helvetica", "normal");
|
|
doc.setTextColor(...textColor);
|
|
doc.text("Scannez ce QR code ou visitez l'URL ci-dessous", pageWidth / 2, y + qrSize + 18, { align: "center" });
|
|
|
|
doc.setTextColor(...primaryColor);
|
|
doc.setFont("helvetica", "bold");
|
|
doc.setFontSize(7);
|
|
// Tronquer l'URL si trop longue
|
|
const urlDisplay = data.verification_url.length > 65
|
|
? data.verification_url.substring(0, 62) + "..."
|
|
: data.verification_url;
|
|
doc.text(urlDisplay, pageWidth / 2, y + qrSize + 23, { align: "center" });
|
|
|
|
// Détails techniques - Version professionnelle
|
|
y = 225;
|
|
doc.setFontSize(13);
|
|
doc.setFont("helvetica", "bold");
|
|
doc.setTextColor(...textColor);
|
|
doc.text("Contrôles de Conformité", margin, y);
|
|
|
|
y += 8;
|
|
doc.setFontSize(8);
|
|
doc.setFont("helvetica", "normal");
|
|
|
|
// Grille de validation
|
|
const checks = [
|
|
"Format : PAdES-BASELINE-B (ETSI EN 319 102-1)",
|
|
"Algorithme : RSASSA-PSS avec SHA-256",
|
|
"Chiffrement : RSA 2048 bits",
|
|
"Intégrité : Document non modifié"
|
|
];
|
|
|
|
checks.forEach((check, index) => {
|
|
doc.setFillColor(240, 253, 244); // green-50
|
|
doc.setDrawColor(187, 247, 208); // green-200
|
|
doc.setLineWidth(0.3);
|
|
doc.roundedRect(margin, y + (index * 8), pageWidth - 2 * margin, 6, 1, 1, "FD");
|
|
|
|
doc.setTextColor(21, 128, 61); // green-700
|
|
doc.text("✓", margin + 3, y + (index * 8) + 4);
|
|
doc.setTextColor(...textColor);
|
|
doc.text(check, margin + 8, y + (index * 8) + 4);
|
|
});
|
|
|
|
y += 40;
|
|
|
|
// Empreinte cryptographique
|
|
doc.setFont("helvetica", "bold");
|
|
doc.setFontSize(9);
|
|
doc.text("Empreinte cryptographique SHA-256 :", margin, y);
|
|
y += 5;
|
|
doc.setFont("courier", "normal");
|
|
doc.setFontSize(7);
|
|
doc.setTextColor(71, 85, 105); // slate-600
|
|
const hashLines = doc.splitTextToSize(data.signature_hash, pageWidth - 2 * margin);
|
|
doc.text(hashLines, margin, y);
|
|
|
|
y += 10;
|
|
|
|
// Certificat
|
|
doc.setFont("helvetica", "bold");
|
|
doc.setFontSize(9);
|
|
doc.setTextColor(...textColor);
|
|
doc.text("Certificat de signature :", margin, y);
|
|
y += 5;
|
|
doc.setFont("helvetica", "normal");
|
|
doc.setFontSize(7.5);
|
|
doc.text(`Émetteur : ${data.certificate_info.issuer}`, margin + 3, y);
|
|
y += 4;
|
|
doc.text(`Sujet : ${data.certificate_info.subject}`, margin + 3, y);
|
|
y += 4;
|
|
doc.text(
|
|
`Validité : du ${new Date(data.certificate_info.valid_from).toLocaleDateString("fr-FR")} au ${new Date(
|
|
data.certificate_info.valid_until
|
|
).toLocaleDateString("fr-FR")}`,
|
|
margin + 3,
|
|
y
|
|
);
|
|
y += 4;
|
|
doc.text(`N° de série : ${data.certificate_info.serial_number}`, margin + 3, y);
|
|
|
|
// Note informative professionnelle
|
|
y = pageHeight - 35;
|
|
doc.setFillColor(241, 245, 249); // slate-100
|
|
doc.setDrawColor(...borderGray);
|
|
doc.setLineWidth(0.5);
|
|
doc.roundedRect(margin, y, pageWidth - 2 * margin, 18, 2, 2, "FD");
|
|
|
|
doc.setFontSize(7);
|
|
doc.setFont("helvetica", "bold");
|
|
doc.setTextColor(...textColor);
|
|
doc.text("À PROPOS DE CETTE SIGNATURE", margin + 3, y + 5);
|
|
|
|
doc.setFont("helvetica", "normal");
|
|
doc.setTextColor(71, 85, 105); // slate-600
|
|
const noteText = `Cette signature électronique est conforme aux standards techniques PAdES-BASELINE-B (ETSI EN 319 102-1), garantissant l'authenticité du signataire et l'intégrité du document. Le système utilise un chiffrement RSA 2048 bits avec SHA-256, offrant un niveau de sécurité élevé conforme aux recommandations de l'ANSSI.`;
|
|
|
|
const splitNote = doc.splitTextToSize(noteText, pageWidth - 2 * margin - 6);
|
|
doc.text(splitNote, margin + 3, y + 10);
|
|
|
|
// Footer
|
|
doc.setFontSize(8);
|
|
doc.setTextColor(100, 116, 139); // Slate-500
|
|
doc.setFont("helvetica", "normal");
|
|
doc.text(
|
|
`Odentas Media SAS - Signature électronique sécurisée`,
|
|
pageWidth / 2,
|
|
pageHeight - 10,
|
|
{ align: "center" }
|
|
);
|
|
|
|
doc.setFontSize(7);
|
|
doc.text(
|
|
`Document généré le ${new Date().toLocaleString("fr-FR")}`,
|
|
pageWidth / 2,
|
|
pageHeight - 5,
|
|
{ align: "center" }
|
|
);
|
|
|
|
return doc.output("blob");
|
|
}
|