espace-paie-odentas/app/api/odentas-sign/requests/[id]/positions/route.ts
odentas b790faf12c feat: Implémentation complète du système Odentas Sign
- 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)
2025-10-27 19:03:07 +01:00

71 lines
2 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server';
import { verifySignatureSession } from '@/lib/odentas-sign/jwt';
import { supabaseAdmin } from '@/lib/odentas-sign/supabase';
/**
* GET /api/odentas-sign/requests/:id/positions
* Récupère les positions de signature pour toutes les parties
*/
export async function GET(
request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const requestId = params.id;
// Vérifier le token JWT
const authHeader = request.headers.get('Authorization');
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return NextResponse.json(
{ error: 'Token manquant ou invalide' },
{ status: 401 }
);
}
const token = authHeader.split(' ')[1];
const payload = verifySignatureSession(token);
if (!payload || payload.requestId !== requestId) {
return NextResponse.json(
{ error: 'Token invalide ou expiré' },
{ status: 401 }
);
}
// Récupérer toutes les positions de signature
const { data: positions, error } = await supabaseAdmin
.from('sign_positions')
.select('page, x, y, w, h, role')
.eq('request_id', requestId)
.order('role');
if (error) {
console.error('Erreur DB lors de la récupération des positions:', error);
return NextResponse.json(
{ error: 'Erreur lors de la récupération des positions' },
{ status: 500 }
);
}
// Transformer pour correspondre à l'interface frontend
const transformedPositions = positions.map(p => ({
page: p.page,
x: p.x,
y: p.y,
width: p.w,
height: p.h,
role: p.role,
}));
return NextResponse.json({
positions: transformedPositions,
});
} catch (error) {
console.error('Erreur lors de la récupération des positions:', error);
return NextResponse.json(
{ error: 'Erreur serveur lors de la récupération des positions' },
{ status: 500 }
);
}
}