- Ajout helpers Handlebars pour remplacer filtres Liquid - Conversion template CDDU de Liquid vers Handlebars - Nouvelle API route /api/generate-contract-pdf pour Gotenberg - Configuration Docker Compose pour auto-héberger Gotenberg - Documentation complète de migration - Variables d'environnement exemple Note: Le bouton 'Créer PDF' utilise encore PDFMonkey. Pour activer Gotenberg, modifier l'appel dans ContractEditor.tsx
163 lines
4.8 KiB
TypeScript
163 lines
4.8 KiB
TypeScript
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs';
|
|
import { cookies } from 'next/headers';
|
|
import { NextRequest, NextResponse } from 'next/server';
|
|
import Handlebars from 'handlebars';
|
|
import fs from 'fs/promises';
|
|
import path from 'path';
|
|
import { registerHandlebarsHelpers } from '@/lib/handlebars-helpers';
|
|
|
|
registerHandlebarsHelpers();
|
|
|
|
/**
|
|
* API Route pour générer un contrat PDF via Gotenberg
|
|
* POST /api/generate-contract-pdf
|
|
*
|
|
* Body attendu :
|
|
* {
|
|
* contractId: string,
|
|
* contractType: "cddu" | "rg" | "avenant",
|
|
* data: object (données du contrat)
|
|
* }
|
|
*/
|
|
export async function POST(request: NextRequest) {
|
|
try {
|
|
const supabase = createRouteHandlerClient({ cookies });
|
|
|
|
const {
|
|
data: { session },
|
|
} = await supabase.auth.getSession();
|
|
|
|
if (!session) {
|
|
return NextResponse.json(
|
|
{ error: 'Non authentifié' },
|
|
{ status: 401 }
|
|
);
|
|
}
|
|
|
|
const body = await request.json();
|
|
const { contractId, contractType, data } = body;
|
|
|
|
if (!contractId || !contractType || !data) {
|
|
return NextResponse.json(
|
|
{ error: 'Données manquantes (contractId, contractType, data)' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Vérifier que l'utilisateur a accès à ce contrat
|
|
const { data: contract, error: contractError } = await supabase
|
|
.from('contracts')
|
|
.select('*')
|
|
.eq('id', contractId)
|
|
.single();
|
|
|
|
if (contractError || !contract) {
|
|
return NextResponse.json(
|
|
{ error: 'Contrat introuvable' },
|
|
{ status: 404 }
|
|
);
|
|
}
|
|
|
|
// Charger le template Handlebars selon le type de contrat
|
|
let templatePath: string;
|
|
switch (contractType) {
|
|
case 'cddu':
|
|
templatePath = path.join(process.cwd(), 'templates-contrats', 'cddu-handlebars.html');
|
|
break;
|
|
case 'rg':
|
|
templatePath = path.join(process.cwd(), 'templates-contrats', 'rg-handlebars.html');
|
|
break;
|
|
case 'avenant':
|
|
templatePath = path.join(process.cwd(), 'templates-contrats', 'avenant-handlebars.html');
|
|
break;
|
|
default:
|
|
return NextResponse.json(
|
|
{ error: 'Type de contrat invalide' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
const templateContent = await fs.readFile(templatePath, 'utf-8');
|
|
const template = Handlebars.compile(templateContent);
|
|
|
|
// Générer le HTML avec les données
|
|
const html = template(data);
|
|
|
|
// Envoyer le HTML à Gotenberg pour conversion en PDF
|
|
const gotenbergUrl = process.env.GOTENBERG_URL || 'http://localhost:3001';
|
|
|
|
const formData = new FormData();
|
|
formData.append('files', new Blob([html], { type: 'text/html' }), 'index.html');
|
|
|
|
// Options Gotenberg
|
|
formData.append('marginTop', '1');
|
|
formData.append('marginBottom', '1');
|
|
formData.append('marginLeft', '1');
|
|
formData.append('marginRight', '1');
|
|
formData.append('paperWidth', '21');
|
|
formData.append('paperHeight', '29.7');
|
|
formData.append('preferCssPageSize', 'false');
|
|
|
|
const gotenbergResponse = await fetch(`${gotenbergUrl}/forms/chromium/convert/html`, {
|
|
method: 'POST',
|
|
body: formData,
|
|
});
|
|
|
|
if (!gotenbergResponse.ok) {
|
|
const errorText = await gotenbergResponse.text();
|
|
console.error('Erreur Gotenberg:', errorText);
|
|
return NextResponse.json(
|
|
{ error: 'Erreur lors de la génération du PDF', details: errorText },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
|
|
// Récupérer le PDF généré
|
|
const pdfBuffer = await gotenbergResponse.arrayBuffer();
|
|
const pdfBlob = new Blob([pdfBuffer], { type: 'application/pdf' });
|
|
|
|
// Uploader le PDF sur Supabase Storage
|
|
const fileName = `contract_${contractId}_${Date.now()}.pdf`;
|
|
const { data: uploadData, error: uploadError } = await supabase.storage
|
|
.from('contracts')
|
|
.upload(fileName, pdfBlob, {
|
|
contentType: 'application/pdf',
|
|
upsert: false,
|
|
});
|
|
|
|
if (uploadError) {
|
|
console.error('Erreur upload Supabase:', uploadError);
|
|
return NextResponse.json(
|
|
{ error: 'Erreur lors de l\'upload du PDF' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
|
|
// Mettre à jour le contrat avec l'URL du PDF
|
|
const { data: publicUrl } = supabase.storage
|
|
.from('contracts')
|
|
.getPublicUrl(fileName);
|
|
|
|
const { error: updateError } = await supabase
|
|
.from('contracts')
|
|
.update({ pdf_url: publicUrl.publicUrl })
|
|
.eq('id', contractId);
|
|
|
|
if (updateError) {
|
|
console.error('Erreur mise à jour contrat:', updateError);
|
|
}
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
pdfUrl: publicUrl.publicUrl,
|
|
fileName,
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Erreur génération PDF:', error);
|
|
return NextResponse.json(
|
|
{ error: 'Erreur interne du serveur' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|