181 lines
5.5 KiB
TypeScript
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 }
|
|
);
|
|
}
|
|
}
|