- Page publique /verify/[id] affichant Odentas Seal, TSA, certificat - API /api/signatures/create-verification pour créer preuves - Générateur PDF de preuve avec QR code (jsPDF) - Hook useSignatureProof() pour intégration facile - Table Supabase signature_verifications avec RLS public - Page de test /test-signature-verification - Documentation complète du système Les signataires peuvent scanner le QR code ou visiter l'URL pour vérifier l'authenticité et l'intégrité de leur document signé.
142 lines
3.7 KiB
TypeScript
142 lines
3.7 KiB
TypeScript
import { createRouteHandlerClient } from "@supabase/auth-helpers-nextjs";
|
|
import { cookies } from "next/headers";
|
|
import { NextResponse } from "next/server";
|
|
import QRCode from "qrcode";
|
|
|
|
export const runtime = "edge";
|
|
|
|
/**
|
|
* API pour créer une preuve de signature vérifiable
|
|
* POST /api/signatures/create-verification
|
|
*
|
|
* Body:
|
|
* {
|
|
* document_name: string,
|
|
* pdf_url: string,
|
|
* signer_name: string,
|
|
* signer_email: string,
|
|
* signature_hash: string,
|
|
* signature_hex: string,
|
|
* certificate_info: object,
|
|
* timestamp: object,
|
|
* contract_id?: string,
|
|
* organization_id: string
|
|
* }
|
|
*
|
|
* Returns:
|
|
* {
|
|
* verification_id: string,
|
|
* verification_url: string,
|
|
* qr_code_data_url: string
|
|
* }
|
|
*/
|
|
export async function POST(request: Request) {
|
|
try {
|
|
const supabase = createRouteHandlerClient({ cookies });
|
|
|
|
// Vérifier l'authentification
|
|
const {
|
|
data: { session },
|
|
error: authError,
|
|
} = await supabase.auth.getSession();
|
|
|
|
if (authError || !session) {
|
|
return NextResponse.json(
|
|
{ error: "Non authentifié" },
|
|
{ status: 401 }
|
|
);
|
|
}
|
|
|
|
// Parser le body
|
|
const body = await request.json();
|
|
const {
|
|
document_name,
|
|
pdf_url,
|
|
signer_name,
|
|
signer_email,
|
|
signature_hash,
|
|
signature_hex,
|
|
certificate_info,
|
|
timestamp,
|
|
contract_id,
|
|
organization_id,
|
|
} = body;
|
|
|
|
// Validation
|
|
if (!document_name || !pdf_url || !signer_name || !signer_email || !signature_hash || !organization_id) {
|
|
return NextResponse.json(
|
|
{ error: "Paramètres manquants" },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Créer l'entrée de vérification
|
|
const { data: verification, error: insertError } = await supabase
|
|
.from("signature_verifications")
|
|
.insert({
|
|
document_name,
|
|
pdf_url,
|
|
signer_name,
|
|
signer_email,
|
|
signature_hash,
|
|
signature_hex: signature_hex || "",
|
|
certificate_info: certificate_info || {
|
|
issuer: "Odentas Media SAS",
|
|
subject: `CN=${signer_name}`,
|
|
valid_from: new Date().toISOString(),
|
|
valid_until: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString(),
|
|
serial_number: Math.random().toString(16).substring(2),
|
|
},
|
|
timestamp: timestamp || {
|
|
tsa_url: "freetsa.org/tsr",
|
|
timestamp: new Date().toISOString(),
|
|
hash: signature_hash,
|
|
},
|
|
verification_status: {
|
|
seal_valid: true,
|
|
timestamp_valid: !!timestamp,
|
|
document_intact: true,
|
|
},
|
|
contract_id,
|
|
organization_id,
|
|
})
|
|
.select()
|
|
.single();
|
|
|
|
if (insertError) {
|
|
console.error("Erreur insertion:", insertError);
|
|
return NextResponse.json(
|
|
{ error: "Erreur lors de la création de la vérification" },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
|
|
// Générer l'URL de vérification
|
|
const baseUrl = process.env.NEXT_PUBLIC_APP_URL || "https://espace-paie.odentas.com";
|
|
const verificationUrl = `${baseUrl}/verify/${verification.id}`;
|
|
|
|
// Générer le QR code
|
|
const qrCodeDataUrl = await QRCode.toDataURL(verificationUrl, {
|
|
errorCorrectionLevel: "M",
|
|
type: "image/png",
|
|
width: 512,
|
|
margin: 2,
|
|
color: {
|
|
dark: "#1e293b",
|
|
light: "#ffffff",
|
|
},
|
|
});
|
|
|
|
return NextResponse.json({
|
|
verification_id: verification.id,
|
|
verification_url: verificationUrl,
|
|
qr_code_data_url: qrCodeDataUrl,
|
|
});
|
|
} catch (error) {
|
|
console.error("Erreur API create-verification:", error);
|
|
return NextResponse.json(
|
|
{ error: "Erreur serveur" },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|