255 lines
8.2 KiB
TypeScript
255 lines
8.2 KiB
TypeScript
import { createRouteHandlerClient } from "@supabase/auth-helpers-nextjs";
|
|
import { cookies } from "next/headers";
|
|
import { NextRequest, NextResponse } from "next/server";
|
|
import { S3Client, PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3";
|
|
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
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!,
|
|
},
|
|
});
|
|
|
|
// Fonction de polling pour attendre la génération du PDF
|
|
async function pollDocumentStatus(
|
|
documentUrl: string,
|
|
apiKey: string,
|
|
maxAttempts = 15,
|
|
intervalMs = 2000
|
|
): Promise<{ status: string; download_url?: string }> {
|
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
console.log(`[NAA Regenerate] Poll attempt ${attempt}/${maxAttempts}`);
|
|
|
|
const res = await fetch(documentUrl, {
|
|
headers: { Authorization: `Bearer ${apiKey}` }
|
|
});
|
|
|
|
if (!res.ok) {
|
|
throw new Error(`Failed to poll document status: ${res.statusText}`);
|
|
}
|
|
|
|
const data = await res.json();
|
|
const status = data.document?.status;
|
|
|
|
console.log(`[NAA Regenerate] Poll attempt ${attempt}/${maxAttempts}, status: ${status}`);
|
|
|
|
if (status === "success" && data.document?.download_url) {
|
|
return { status, download_url: data.document.download_url };
|
|
}
|
|
|
|
if (status === "error" || status === "failure") {
|
|
throw new Error(`PDF generation failed with status: ${status}`);
|
|
}
|
|
|
|
if (attempt < maxAttempts) {
|
|
await new Promise(resolve => setTimeout(resolve, intervalMs));
|
|
}
|
|
}
|
|
|
|
return { status: "timeout" };
|
|
}
|
|
|
|
export async function POST(
|
|
request: NextRequest,
|
|
{ params }: { params: { id: string } }
|
|
) {
|
|
try {
|
|
const cookieStore = cookies();
|
|
const supabase = createRouteHandlerClient({ cookies: () => cookieStore });
|
|
|
|
// Vérifier l'authentification staff
|
|
const { data: { user } } = await supabase.auth.getUser();
|
|
if (!user) {
|
|
return NextResponse.json({ error: "Non authentifié" }, { status: 401 });
|
|
}
|
|
|
|
const { data: staffUser } = await supabase
|
|
.from("staff_users")
|
|
.select("user_id")
|
|
.eq("user_id", user.id)
|
|
.single();
|
|
|
|
if (!staffUser) {
|
|
return NextResponse.json({ error: "Accès non autorisé" }, { status: 403 });
|
|
}
|
|
|
|
console.log(`[NAA Regenerate] Starting regeneration for NAA ID: ${params.id}`);
|
|
|
|
// Récupérer le document NAA avec l'apporteur
|
|
const { data: naaDoc, error: naaError } = await supabase
|
|
.from("naa_documents")
|
|
.select(`
|
|
*,
|
|
referrers (*)
|
|
`)
|
|
.eq("id", params.id)
|
|
.single();
|
|
|
|
if (naaError || !naaDoc) {
|
|
return NextResponse.json({ error: "NAA non trouvée" }, { status: 404 });
|
|
}
|
|
|
|
// Récupérer les prestations
|
|
const { data: prestations } = await supabase
|
|
.from("naa_prestations")
|
|
.select("*")
|
|
.eq("naa_id", params.id)
|
|
.order("created_at");
|
|
|
|
// Récupérer les line items (commissions)
|
|
const { data: lineItems } = await supabase
|
|
.from("naa_line_items")
|
|
.select("*")
|
|
.eq("naa_id", params.id)
|
|
.order("created_at");
|
|
|
|
const referrer = Array.isArray(naaDoc.referrers) ? naaDoc.referrers[0] : naaDoc.referrers;
|
|
|
|
// Préparer le payload pour PDFMonkey
|
|
const pdfMonkeyPayload = {
|
|
apporteur_address: referrer?.address || "",
|
|
apporteur_cp: referrer?.postal_code || "",
|
|
apporteur_city: referrer?.city || "",
|
|
apporteur_code: referrer?.code || naaDoc.referrer_code,
|
|
apporteur_name: referrer?.name || "",
|
|
apporteur_contact: referrer?.contact_name || "",
|
|
callsheet_date: new Date(naaDoc.callsheet_date).toLocaleDateString("fr-FR"),
|
|
limit_date: naaDoc.limit_date ? new Date(naaDoc.limit_date).toLocaleDateString("fr-FR") : "",
|
|
callsheet_number: naaDoc.naa_number,
|
|
periode: naaDoc.periode,
|
|
total_commission: naaDoc.total_commission || 0,
|
|
solde_compte_apporteur: naaDoc.solde_compte_apporteur || 0,
|
|
total_facture: naaDoc.total_facture || 0,
|
|
deposit: naaDoc.deposit || 0,
|
|
nbre_clients: naaDoc.nbre_clients || 0,
|
|
nbre_prestations: naaDoc.nbre_prestations || 0,
|
|
transfer_reference: naaDoc.transfer_reference || "",
|
|
logo_odentas: "",
|
|
lineItems: (lineItems || []).map(item => ({
|
|
id: item.client_code,
|
|
client: item.client_name,
|
|
code: item.client_code,
|
|
comactuelle: item.commission_rate,
|
|
caht: item.ca_ht,
|
|
commission: item.commission
|
|
})),
|
|
prestations: (prestations || []).map(p => ({
|
|
client: p.client_name,
|
|
code: p.client_code,
|
|
type_prestation: p.type_prestation,
|
|
quantite: p.quantite,
|
|
tarif: p.tarif,
|
|
total: p.total
|
|
}))
|
|
};
|
|
|
|
console.log("[NAA Regenerate] Calling PDFMonkey API...");
|
|
|
|
const pdfMonkeyApiKey = process.env.PDFMONKEY_API_KEY;
|
|
const pdfMonkeyUrl = "https://api.pdfmonkey.io/api/v1/documents";
|
|
const templateId = process.env.PDFMONKEY_NAA_TEMPLATE_ID || "422DA8A6-69E1-4798-B4A3-DF75D892BF2D";
|
|
|
|
const pdfMonkeyRes = await fetch(pdfMonkeyUrl, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
Authorization: `Bearer ${pdfMonkeyApiKey}`,
|
|
},
|
|
body: JSON.stringify({
|
|
document: {
|
|
document_template_id: templateId,
|
|
status: "pending",
|
|
payload: pdfMonkeyPayload,
|
|
},
|
|
}),
|
|
});
|
|
|
|
if (!pdfMonkeyRes.ok) {
|
|
const errorText = await pdfMonkeyRes.text();
|
|
console.error("[NAA Regenerate] PDFMonkey API error:", errorText);
|
|
return NextResponse.json(
|
|
{ error: "Erreur lors de l'appel à PDFMonkey" },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
|
|
const pdfMonkeyData = await pdfMonkeyRes.json();
|
|
console.log("[NAA Regenerate] PDFMonkey response:", pdfMonkeyData);
|
|
|
|
const documentId = pdfMonkeyData.document?.id;
|
|
if (!documentId) {
|
|
return NextResponse.json({ error: "No document ID returned from PDFMonkey" }, { status: 500 });
|
|
}
|
|
|
|
const documentUrl = `${pdfMonkeyUrl}/${documentId}`;
|
|
console.log("[NAA Regenerate] Polling document status...");
|
|
|
|
const { status, download_url } = await pollDocumentStatus(documentUrl, pdfMonkeyApiKey!, 15, 2000);
|
|
|
|
if (status !== "success" || !download_url) {
|
|
return NextResponse.json({ error: "PDF generation failed or timed out" }, { status: 500 });
|
|
}
|
|
|
|
// Télécharger le PDF
|
|
console.log("[NAA Regenerate] Downloading PDF...");
|
|
const pdfRes = await fetch(download_url);
|
|
if (!pdfRes.ok) {
|
|
return NextResponse.json({ error: "Failed to download PDF" }, { status: 500 });
|
|
}
|
|
const pdfBuffer = Buffer.from(await pdfRes.arrayBuffer());
|
|
console.log("[NAA Regenerate] PDF downloaded, size:", pdfBuffer.length, "bytes");
|
|
|
|
// Uploader sur S3
|
|
console.log("[NAA Regenerate] Uploading to S3...");
|
|
const bucketName = process.env.AWS_S3_BUCKET_NAME || "odentas-docs";
|
|
const s3Key = `naa/${naaDoc.naa_number}.pdf`;
|
|
|
|
const uploadCommand = new PutObjectCommand({
|
|
Bucket: bucketName,
|
|
Key: s3Key,
|
|
Body: pdfBuffer,
|
|
ContentType: "application/pdf",
|
|
});
|
|
|
|
await s3Client.send(uploadCommand);
|
|
console.log("[NAA Regenerate] S3 upload successful");
|
|
|
|
const s3Url = `https://${bucketName}.s3.${process.env.AWS_REGION || "eu-west-3"}.amazonaws.com/${s3Key}`;
|
|
|
|
// Générer une URL présignée
|
|
const getObjectCommand = new GetObjectCommand({
|
|
Bucket: bucketName,
|
|
Key: s3Key,
|
|
});
|
|
const presignedUrl = await getSignedUrl(s3Client, getObjectCommand, { expiresIn: 3600 });
|
|
|
|
// Mettre à jour le document NAA
|
|
await supabase
|
|
.from("naa_documents")
|
|
.update({
|
|
pdf_url: s3Url,
|
|
s3_key: s3Key,
|
|
updated_at: new Date().toISOString()
|
|
})
|
|
.eq("id", params.id);
|
|
|
|
console.log("[NAA Regenerate] ✅ NAA regeneration completed successfully");
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
pdf_url: s3Url,
|
|
presigned_url: presignedUrl
|
|
});
|
|
|
|
} catch (error: any) {
|
|
console.error("[NAA Regenerate] ❌ Error:", error);
|
|
return NextResponse.json(
|
|
{ error: error.message || "Erreur serveur" },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|