163 lines
5 KiB
TypeScript
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 }
|
|
);
|
|
}
|
|
}
|