- Remplacement de DocuSeal par solution souveraine Odentas Sign - Système d'authentification OTP pour signataires (bcryptjs + JWT) - 8 routes API: send-otp, verify-otp, sign, pdf-url, positions, status, webhook, signers - Interface moderne avec canvas de signature et animations (framer-motion, confetti) - Système de templates pour auto-détection des positions de signature (CDDU, RG, avenants) - PDF viewer avec @react-pdf-viewer (compatible Next.js) - Stockage S3: source/, signatures/, evidence/, signed/, certs/ - Tables Supabase: sign_requests, signers, sign_positions, sign_events, sign_assets - Evidence bundle automatique (JSON metadata + timestamps) - Templates emails: OTP et completion - Scripts Lambda prêts: pades-sign (KMS seal) et tsaStamp (RFC3161) - Mode test détecté automatiquement (emails whitelist) - Tests complets avec PDF CDDU réel (2 signataires)
251 lines
6 KiB
TypeScript
251 lines
6 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
|
import { supabaseAdmin, logSignEvent } from '@/lib/odentas-sign/supabase';
|
|
import { generateRequestRef } from '@/lib/odentas-sign/crypto';
|
|
import { uploadToS3, S3_PREFIXES } from '@/lib/odentas-sign/s3';
|
|
import type { CreateSignRequestInput } from '@/lib/odentas-sign/types';
|
|
|
|
/**
|
|
* POST /api/odentas-sign/test/create-mock
|
|
*
|
|
* Crée une demande de signature de test sans PDF réel
|
|
* Utilisé pour tester le workflow sans avoir de vrai contrat
|
|
*/
|
|
export async function POST(request: NextRequest) {
|
|
try {
|
|
const body = await request.json();
|
|
const { title, signerName, signerEmail } = body;
|
|
|
|
// Générer un PDF de test simple
|
|
const testPdfContent = generateMockPDF(title || 'Document de test');
|
|
|
|
// Upload du PDF de test vers S3
|
|
const testRef = `TEST-${Date.now()}`;
|
|
const testPdfKey = `${S3_PREFIXES.SOURCE}test/${testRef}.pdf`;
|
|
|
|
await uploadToS3({
|
|
key: testPdfKey,
|
|
body: Buffer.from(testPdfContent),
|
|
contentType: 'application/pdf',
|
|
metadata: {
|
|
test: 'true',
|
|
created_by: 'test-api',
|
|
},
|
|
});
|
|
|
|
console.log(`[TEST] PDF de test créé: ${testPdfKey}`);
|
|
|
|
// Créer la demande de signature
|
|
const ref = generateRequestRef(`TEST-${testRef}`);
|
|
|
|
const { data: signRequest, error: requestError } = await supabaseAdmin
|
|
.from('sign_requests')
|
|
.insert({
|
|
ref,
|
|
title: title || 'Document de test - Odentas Sign',
|
|
source_s3_key: testPdfKey,
|
|
status: 'pending',
|
|
})
|
|
.select()
|
|
.single();
|
|
|
|
if (requestError || !signRequest) {
|
|
console.error('[TEST] Erreur création sign_request:', requestError);
|
|
return NextResponse.json(
|
|
{ error: 'Erreur lors de la création de la demande de test', details: requestError },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
|
|
// Créer 2 signataires par défaut (Employeur + Salarié)
|
|
const signersData = [
|
|
{
|
|
request_id: signRequest.id,
|
|
role: 'Employeur',
|
|
name: 'Jean Dupont (Test)',
|
|
email: 'employeur-test@example.com',
|
|
otp_attempts: 0,
|
|
},
|
|
{
|
|
request_id: signRequest.id,
|
|
role: 'Salarié',
|
|
name: signerName || 'Marie Martin (Test)',
|
|
email: signerEmail || 'salarie-test@example.com',
|
|
otp_attempts: 0,
|
|
},
|
|
];
|
|
|
|
const { data: createdSigners, error: signersError } = await supabaseAdmin
|
|
.from('signers')
|
|
.insert(signersData)
|
|
.select();
|
|
|
|
if (signersError || !createdSigners) {
|
|
console.error('[TEST] Erreur création signers:', signersError);
|
|
await supabaseAdmin.from('sign_requests').delete().eq('id', signRequest.id);
|
|
return NextResponse.json(
|
|
{ error: 'Erreur lors de la création des signataires de test', details: signersError },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
|
|
// Créer des positions de signature standard
|
|
const positionsData = [
|
|
{
|
|
request_id: signRequest.id,
|
|
role: 'Employeur',
|
|
page: 1,
|
|
x: 100,
|
|
y: 650,
|
|
w: 200,
|
|
h: 60,
|
|
kind: 'signature',
|
|
label: 'Signature Employeur',
|
|
},
|
|
{
|
|
request_id: signRequest.id,
|
|
role: 'Salarié',
|
|
page: 1,
|
|
x: 350,
|
|
y: 650,
|
|
w: 200,
|
|
h: 60,
|
|
kind: 'signature',
|
|
label: 'Signature Salarié',
|
|
},
|
|
];
|
|
|
|
await supabaseAdmin.from('sign_positions').insert(positionsData);
|
|
|
|
// Logger l'événement
|
|
await logSignEvent({
|
|
requestId: signRequest.id,
|
|
event: 'test_request_created',
|
|
metadata: {
|
|
test: true,
|
|
pdf_key: testPdfKey,
|
|
},
|
|
});
|
|
|
|
// Générer les URLs
|
|
const baseUrl = process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000';
|
|
const signerUrls = createdSigners.map(signer => ({
|
|
signerId: signer.id,
|
|
role: signer.role,
|
|
name: signer.name,
|
|
email: signer.email,
|
|
signatureUrl: `${baseUrl}/signer/${signRequest.id}/${signer.id}`,
|
|
}));
|
|
|
|
console.log(`[TEST] ✅ Demande de test créée: ${ref}`);
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
message: 'Demande de signature de test créée',
|
|
test_mode: true,
|
|
request: {
|
|
id: signRequest.id,
|
|
ref: signRequest.ref,
|
|
title: signRequest.title,
|
|
status: signRequest.status,
|
|
created_at: signRequest.created_at,
|
|
},
|
|
signers: signerUrls,
|
|
instructions: {
|
|
step1: 'Utilisez les URLs ci-dessus pour tester la signature',
|
|
step2: 'Le code OTP sera affiché dans les logs serveur (console)',
|
|
step3: 'Après signature, le webhook ne déclenchera PAS Odentas Seal',
|
|
note: 'Ceci est un environnement de test - aucun document légal ne sera créé',
|
|
},
|
|
}, { status: 201 });
|
|
|
|
} catch (error) {
|
|
console.error('[TEST] Erreur:', error);
|
|
return NextResponse.json(
|
|
{ error: 'Erreur serveur', details: error instanceof Error ? error.message : String(error) },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Génère un PDF simple de test
|
|
*/
|
|
function generateMockPDF(title: string): string {
|
|
// PDF minimal valide (version 1.4)
|
|
const pdfContent = `%PDF-1.4
|
|
1 0 obj
|
|
<<
|
|
/Type /Catalog
|
|
/Pages 2 0 R
|
|
>>
|
|
endobj
|
|
|
|
2 0 obj
|
|
<<
|
|
/Type /Pages
|
|
/Kids [3 0 R]
|
|
/Count 1
|
|
>>
|
|
endobj
|
|
|
|
3 0 obj
|
|
<<
|
|
/Type /Page
|
|
/Parent 2 0 R
|
|
/MediaBox [0 0 612 792]
|
|
/Contents 4 0 R
|
|
/Resources <<
|
|
/Font <<
|
|
/F1 <<
|
|
/Type /Font
|
|
/Subtype /Type1
|
|
/BaseFont /Helvetica
|
|
>>
|
|
>>
|
|
>>
|
|
>>
|
|
endobj
|
|
|
|
4 0 obj
|
|
<<
|
|
/Length 200
|
|
>>
|
|
stream
|
|
BT
|
|
/F1 18 Tf
|
|
50 700 Td
|
|
(${title}) Tj
|
|
0 -30 Td
|
|
/F1 12 Tf
|
|
(Document de test - Odentas Sign) Tj
|
|
0 -20 Td
|
|
(Ce document est genere automatiquement pour tester) Tj
|
|
0 -20 Td
|
|
(le systeme de signature electronique.) Tj
|
|
0 -100 Td
|
|
(Signature Employeur: ___________________) Tj
|
|
0 -30 Td
|
|
(Signature Salarie: ___________________) Tj
|
|
ET
|
|
endstream
|
|
endobj
|
|
|
|
xref
|
|
0 5
|
|
0000000000 65535 f
|
|
0000000009 00000 n
|
|
0000000058 00000 n
|
|
0000000115 00000 n
|
|
0000000317 00000 n
|
|
trailer
|
|
<<
|
|
/Size 5
|
|
/Root 1 0 R
|
|
>>
|
|
startxref
|
|
566
|
|
%%EOF
|
|
`;
|
|
|
|
return pdfContent;
|
|
}
|