espace-paie-odentas/app/api/odentas-sign/seal-document/route.ts

163 lines
5 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server';
import { supabaseAdmin } from '@/lib/odentas-sign/supabase';
import { LambdaClient, InvokeCommand } from '@aws-sdk/client-lambda';
const lambdaClient = new LambdaClient({
region: process.env.AWS_REGION || 'eu-west-3',
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
});
export const runtime = 'nodejs';
/**
* POST /api/odentas-sign/seal-document
*
* Scelle le document avec toutes les signatures PAdES
* Chaque signataire est signé par Odentas avec /Name et /Reason personnalisés
*/
export async function POST(request: NextRequest) {
try {
const { requestId } = await request.json();
if (!requestId) {
return NextResponse.json(
{ error: 'requestId manquant' },
{ status: 400 }
);
}
// 1. Récupérer la demande et tous les signataires
const { data: signRequest, error: requestError } = await supabaseAdmin
.from('sign_requests')
.select('*, signers(*)')
.eq('id', requestId)
.single();
if (requestError || !signRequest) {
return NextResponse.json(
{ error: 'Demande non trouvée' },
{ status: 404 }
);
}
// 2. Vérifier que tous les signataires ont signé
const allSigned = signRequest.signers.every((s: any) => s.signed_at !== null);
if (!allSigned) {
return NextResponse.json(
{ error: 'Tous les signataires doivent avoir signé' },
{ status: 400 }
);
}
// 3. Trier les signataires par ordre de signature
const sortedSigners = [...signRequest.signers].sort((a: any, b: any) => {
return new Date(a.signed_at).getTime() - new Date(b.signed_at).getTime();
});
console.log(`[SEAL] Scellement de ${sortedSigners.length} signatures pour ${requestId}`);
// 4. Appliquer séquentiellement les signatures PAdES
let currentS3Key = signRequest.source_s3_key;
for (let i = 0; i < sortedSigners.length; i++) {
const signer = sortedSigners[i];
const isLastSignature = i === sortedSigners.length - 1;
console.log(`[SEAL] Signature ${i + 1}/${sortedSigners.length}: ${signer.name} (${signer.role})`);
// Préparer les métadonnées de signature
const signatureMetadata = {
name: signer.signature_name || signer.name,
reason: signer.signature_reason || `Signature en tant que ${signer.role}`,
location: signer.signature_location || 'France',
contactInfo: signer.signature_contact_info || signer.email,
};
// Appeler la Lambda de signature PAdES
const lambdaPayload = {
s3Key: currentS3Key,
signatureMetadata,
signerOrder: i + 1,
requestId,
signerId: signer.id,
};
console.log('[SEAL] Appel Lambda:', lambdaPayload);
const lambdaCommand = new InvokeCommand({
FunctionName: process.env.AWS_LAMBDA_SIGN_FUNCTION || 'odentas-pades-sign',
Payload: Buffer.from(JSON.stringify(lambdaPayload)),
});
const lambdaResponse = await lambdaClient.send(lambdaCommand);
const lambdaResult = JSON.parse(
Buffer.from(lambdaResponse.Payload!).toString()
);
if (!lambdaResult.success) {
console.error('[SEAL] Erreur Lambda:', lambdaResult);
return NextResponse.json(
{ error: `Erreur lors de la signature PAdES de ${signer.name}` },
{ status: 500 }
);
}
// Utiliser le PDF signé comme entrée pour la prochaine signature
currentS3Key = lambdaResult.signedS3Key;
console.log(`[SEAL] ✅ Signature ${i + 1} appliquée: ${currentS3Key}`);
}
// 5. Enregistrer le document final dans sign_assets
const { error: assetError } = await supabaseAdmin
.from('sign_assets')
.upsert({
request_id: requestId,
signed_pdf_s3_key: currentS3Key,
pdf_sha256: 'to-compute', // À calculer si nécessaire
sealed_at: new Date().toISOString(),
seal_algo: 'RSASSA_PSS_SHA_256',
});
if (assetError) {
console.error('[SEAL] Erreur enregistrement asset:', assetError);
}
// 6. Mettre à jour le statut de la demande
await supabaseAdmin
.from('sign_requests')
.update({
status: 'completed',
updated_at: new Date().toISOString(),
})
.eq('id', requestId);
// 7. Logger l'événement
await supabaseAdmin.from('sign_events').insert({
request_id: requestId,
event: 'request_completed',
metadata: {
signatures_count: sortedSigners.length,
final_s3_key: currentS3Key,
},
});
console.log(`[SEAL] ✅ Document scellé avec ${sortedSigners.length} signatures`);
return NextResponse.json({
success: true,
signed_s3_key: currentS3Key,
signatures_count: sortedSigners.length,
message: 'Document scellé avec succès',
});
} catch (error) {
console.error('[SEAL] Erreur:', error);
return NextResponse.json(
{ error: 'Erreur interne du serveur' },
{ status: 500 }
);
}
}