espace-paie-odentas/app/api/odentas-sign/requests/[id]/pdf-to-images/route.ts

181 lines
5.5 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server';
import { GetObjectCommand, HeadObjectCommand, S3Client } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { supabaseAdmin } from '@/lib/odentas-sign/supabase';
import { verifySignatureSession } from '@/lib/odentas-sign/jwt';
// Client S3
const s3Client = new S3Client({
region: process.env.AWS_REGION || 'eu-west-3',
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
});
// Bucket où sont stockées les images converties
const IMAGES_BUCKET = process.env.AWS_S3_BUCKET || 'odentas-docs';
interface PageImage {
pageNumber: number;
imageUrl: string;
width: number;
height: number;
}
/**
* Récupère les images JPEG pré-converties depuis S3
* (converties automatiquement par la Lambda lors de l'upload du PDF)
*/
async function getPreconvertedImagesFromS3(requestId: string): Promise<PageImage[]> {
const pageImages: PageImage[] = [];
let pageNum = 1;
while (true) {
const s3Key = `odentas-sign-images/${requestId}/page-${pageNum}.jpg`;
try {
// Vérifier si la page existe
await s3Client.send(
new HeadObjectCommand({
Bucket: IMAGES_BUCKET,
Key: s3Key,
})
);
// Générer l'URL presignée (valide 24h)
const command = new GetObjectCommand({
Bucket: IMAGES_BUCKET,
Key: s3Key,
});
const s3Url = await getSignedUrl(s3Client, command, { expiresIn: 86400 });
pageImages.push({
pageNumber: pageNum,
imageUrl: s3Url,
width: 1400,
height: Math.round(1400 * 1.414), // Ratio A4
});
pageNum++;
} catch (error) {
// Plus de pages, on sort de la boucle
break;
}
}
return pageImages;
}
export async function POST(
request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
console.log('[PDF to Images API] Nouvelle requête', { requestId: params.id });
// Verify authorization header
const authHeader = request.headers.get('authorization');
console.log('[PDF to Images API] Auth header:', {
present: !!authHeader,
startsWithBearer: authHeader?.startsWith('Bearer '),
length: authHeader?.length,
});
if (!authHeader?.startsWith('Bearer ')) {
console.error('[PDF to Images API] Header Authorization manquant ou invalide');
return NextResponse.json({ error: 'Non autorisé' }, { status: 401 });
}
const sessionToken = authHeader.substring(7);
console.log('[PDF to Images API] Token extrait:', {
length: sessionToken.length,
preview: sessionToken.substring(0, 20) + '...',
});
// Vérifier et décoder le JWT
const session = verifySignatureSession(sessionToken);
console.log('[PDF to Images API] Session vérifiée:', {
valid: !!session,
signerId: session?.signerId,
requestId: session?.requestId,
});
if (!session) {
console.error('[PDF to Images API] Session JWT invalide');
return NextResponse.json({ error: 'Session invalide' }, { status: 401 });
}
// Verify request ID matches
if (session.requestId !== params.id) {
console.error('[PDF to Images API] RequestId mismatch:', {
sessionRequestId: session.requestId,
paramsId: params.id,
});
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
}
console.log('[PDF to Images API] Requête Supabase pour requestId:', params.id);
// Get signature request avec le ref (utilisé par la Lambda pour nommer les images)
const { data: requestData, error: requestError } = await supabaseAdmin
.from('sign_requests')
.select('id, ref')
.eq('id', params.id)
.single();
console.log('[PDF to Images API] Résultat Supabase:', {
hasData: !!requestData,
hasError: !!requestError,
error: requestError,
ref: requestData?.ref,
});
if (requestError || !requestData) {
console.error('[PDF to Images API] Demande non trouvée dans Supabase:', {
requestId: params.id,
error: requestError,
});
return NextResponse.json({ error: 'Demande non trouvée' }, { status: 404 });
}
console.log('[PDF to Images API] Récupération des images pré-converties depuis S3...');
// Récupérer les images JPEG pré-converties depuis S3
// La Lambda utilise le REF (ex: TEST-1234567) comme nom de dossier, pas le UUID
const pages = await getPreconvertedImagesFromS3(requestData.ref);
if (pages.length === 0) {
console.error('[PDF to Images API] Aucune image trouvée dans S3');
return NextResponse.json(
{
error: 'Images non disponibles',
details: 'Les images du PDF n\'ont pas encore été converties. Veuillez réessayer dans quelques instants.',
},
{ status: 404 }
);
}
console.log(`[PDF to Images API] ✅ ${pages.length} page(s) récupérées depuis S3`);
return NextResponse.json({
success: true,
pages: pages.map((page: PageImage) => ({
pageNumber: page.pageNumber,
imageUrl: page.imageUrl,
width: page.width,
height: page.height,
})),
totalPages: pages.length,
});
} catch (error) {
console.error('[PDF to Images API] Erreur:', error);
return NextResponse.json(
{
error: 'Erreur lors de la conversion PDF',
details: error instanceof Error ? error.message : 'Erreur inconnue',
},
{ status: 500 }
);
}
}