espace-paie-odentas/test-gotenberg-local.js
odentas 04e9d54292 feat: Migrer génération PDF de PDFMonkey vers Gotenberg
- Remplacer PDFMonkey par Gotenberg (auto-hébergé sur VPS)
- Créer template Handlebars pour contrats (lib/templates/contract.hbs)
- Créer formateur de données (lib/contract-data-formatter.ts)
- Ajouter helpers Handlebars pour conditions et transformations
- Mettre à jour API route generate-pdf pour utiliser Gotenberg
- Ajouter GOTENBERG_URL dans .env.local (http://localhost:3001 pour dev)
- Créer script tunnel-gotenberg.sh pour dev local
- Ajouter documentation MIGRATION_GOTENBERG.md

Avantages:
- Souveraineté des données (pas d'API externe)
- Performance (génération locale)
- Coût (0€ vs abonnement PDFMonkey)
- Sécurité (Gotenberg non exposé publiquement)
2025-12-27 22:22:45 +01:00

328 lines
11 KiB
JavaScript

/**
* Script de test pour Gotenberg sur VPS
* Usage: node test-gotenberg-local.js
*/
const fs = require('fs');
const path = require('path');
// ========================================
// CONFIGURATION - À ADAPTER
// ========================================
const CONFIG = {
// URL de Gotenberg
// Option A : Domaine Coolify (production) → https://gotenberg.odnt.fr
// Option B : Tunnel SSH (test local) → http://localhost:3001
gotenbergUrl: process.env.GOTENBERG_URL || 'http://localhost:3001',
// URL de votre Next.js local
nextjsUrl: 'http://localhost:3000',
// ID de contrat de test
testContractId: 'test-' + Date.now(),
};
console.log('🧪 Test Gotenberg - Configuration:');
console.log(' Gotenberg:', CONFIG.gotenbergUrl);
console.log(' Next.js:', CONFIG.nextjsUrl);
console.log('');
// ========================================
// TEST 1 : Vérifier que Gotenberg est accessible
// ========================================
async function testGotenbergHealth() {
console.log('📡 Test 1: Connexion à Gotenberg...');
try {
const response = await fetch(`${CONFIG.gotenbergUrl}/health`);
if (response.ok) {
const data = await response.json();
console.log('✅ Gotenberg est accessible et fonctionne');
console.log(' Status:', data.status || 'up');
return true;
} else {
console.error('❌ Gotenberg répond avec une erreur:', response.status);
return false;
}
} catch (error) {
console.error('❌ Impossible de contacter Gotenberg');
console.error(' Erreur:', error.message);
console.error('');
console.error('💡 Solutions possibles:');
console.error(' 1. Vérifier que Gotenberg tourne sur le VPS:');
console.error(' ssh votre-vps "docker ps | grep gotenberg"');
console.error(' 2. Vérifier que le port est ouvert:');
console.error(' ssh votre-vps "sudo ufw status | grep 3001"');
console.error(' 3. Vérifier la configuration dans .env.local');
return false;
}
}
// ========================================
// TEST 2 : Conversion HTML → PDF directe avec Gotenberg
// ========================================
async function testGotenbergDirect() {
console.log('');
console.log('📄 Test 2: Conversion HTML → PDF direct...');
const htmlContent = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body {
font-family: Arial, sans-serif;
padding: 40px;
max-width: 800px;
margin: 0 auto;
}
h1 {
color: #4f46e5;
border-bottom: 3px solid #4f46e5;
padding-bottom: 10px;
}
.info-box {
background: #f3f4f6;
padding: 20px;
border-radius: 8px;
margin: 20px 0;
}
.success {
color: #10b981;
font-weight: bold;
}
</style>
</head>
<body>
<h1>Test Gotenberg - ${new Date().toLocaleString('fr-FR')}</h1>
<div class="info-box">
<p class="success">✅ Gotenberg fonctionne correctement !</p>
<p>Ce PDF a été généré depuis votre machine locale en utilisant Gotenberg sur le VPS.</p>
<p><strong>Configuration:</strong></p>
<ul>
<li>Gotenberg URL: ${CONFIG.gotenbergUrl}</li>
<li>Test ID: ${CONFIG.testContractId}</li>
</ul>
</div>
</body>
</html>
`;
try {
// Créer le FormData manuellement
const boundary = '----WebKitFormBoundary' + Math.random().toString(36).substr(2);
const bodyParts = [];
bodyParts.push(`--${boundary}\r\n`);
bodyParts.push(`Content-Disposition: form-data; name="files"; filename="index.html"\r\n`);
bodyParts.push(`Content-Type: text/html\r\n\r\n`);
bodyParts.push(`${htmlContent}\r\n`);
bodyParts.push(`--${boundary}--\r\n`);
const body = bodyParts.join('');
const response = await fetch(`${CONFIG.gotenbergUrl}/forms/chromium/convert/html`, {
method: 'POST',
headers: {
'Content-Type': `multipart/form-data; boundary=${boundary}`,
},
body: body,
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Erreur ${response.status}: ${errorText}`);
}
const pdfBuffer = Buffer.from(await response.arrayBuffer());
const outputPath = path.join(__dirname, 'test-gotenberg-output.pdf');
fs.writeFileSync(outputPath, pdfBuffer);
console.log('✅ PDF généré avec succès');
console.log(' Fichier:', outputPath);
console.log(' Taille:', (pdfBuffer.length / 1024).toFixed(2), 'KB');
return true;
} catch (error) {
console.error('❌ Erreur lors de la conversion PDF');
console.error(' Erreur:', error.message);
return false;
}
}
// ========================================
// TEST 3 : Générer un PDF via l'API Next.js
// ========================================
async function testNextjsApiRoute() {
console.log('');
console.log('🚀 Test 3: API Next.js /api/generate-contract-pdf...');
// Données de test pour un contrat CDDU
const contractData = {
structure_name: "Association Test Gotenberg",
structure_adresse: "123 Rue de Test",
structure_cpville: "75001",
structure_ville: "Paris",
structure_siret: "123 456 789 00012",
structure_licence: "TEST-2024",
structure_signataire: "Jean TESTEUR",
structure_signatairequalite: "Directeur",
structure_spectacle: "Oui",
delegation: "Non",
mineur1618: "Non",
employee_civ: "Monsieur",
employee_firstname: "Pierre",
employee_lastname: "DURAND",
employee_birthname: "DURAND",
employee_dob: "15/03/1990",
employee_cob: "Lyon",
employee_address: "456 Avenue Test, 69000 LYON",
employee_ss: "190037500123456",
employee_cs: "T123456",
employee_profession: "Comédien",
employee_codeprofession: "COM010",
employee_catpro: "Artiste",
employee_pseudo: "n/a",
spectacle: "Pièce de Test",
numobjet: "TEST-2024-001",
type_numobjet: "Spectacle",
date_debut: "01/01/2024",
date_fin: "31/01/2024",
dates_travaillees: "01/01/2024; 15/01/2024; 31/01/2024",
details_cachets: "",
salaire_brut: "2500,00",
date_signature: new Date().toLocaleDateString('fr-FR'),
CCN: ["Convention Collective Nationale des Entreprises Artistiques et Culturelles"],
precisions_salaire: "",
panierrepas: "",
panierrepasccn: "Non",
montantpanierrepas: "",
hebergement: "",
hebergementccn: "Non",
montanthebergement: "",
autreprecision_duree: "Test de génération via Gotenberg",
autreprecision_salaire: "",
cachets: {
representations: 3,
repetitions: 2,
heures: 40,
heuresparjour: 8
},
forme_juridique: "Association Loi 1901",
nom_responsable_traitement: "Jean TESTEUR",
qualite_responsable_traitement: "Directeur",
email_responsable_traitement: "test@example.com",
// Logo en base64 minimal (pixel transparent)
imageUrl: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
};
try {
console.log(' Envoi de la requête...');
const response = await fetch(`${CONFIG.nextjsUrl}/api/generate-contract-pdf`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
contractId: CONFIG.testContractId,
contractType: 'cddu',
data: contractData
})
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(`Erreur ${response.status}: ${JSON.stringify(errorData)}`);
}
const result = await response.json();
console.log('✅ API Next.js a répondu avec succès');
console.log(' Success:', result.success);
console.log(' Filename:', result.fileName);
console.log(' PDF URL:', result.pdfUrl);
if (result.pdfUrl) {
console.log('');
console.log('🎉 Le PDF est disponible sur Supabase Storage');
console.log(' Vous pouvez l\'ouvrir dans votre navigateur:');
console.log(' ', result.pdfUrl);
}
return true;
} catch (error) {
console.error('❌ Erreur lors de l\'appel à l\'API Next.js');
console.error(' Erreur:', error.message);
console.error('');
console.error('💡 Vérifications:');
console.error(' 1. Next.js est-il démarré ? (npm run dev)');
console.error(' 2. Le template existe-t-il ? templates-contrats/cddu-handlebars.html');
console.error(' 3. Les helpers sont-ils chargés ? lib/handlebars-helpers.ts');
console.error(' 4. La variable GOTENBERG_URL est-elle définie dans .env.local ?');
return false;
}
}
// ========================================
// EXÉCUTION DES TESTS
// ========================================
async function runAllTests() {
console.log('╔════════════════════════════════════════════════════════════╗');
console.log('║ TEST GOTENBERG - MIGRATION PDFMONKEY → GOTENBERG ║');
console.log('╚════════════════════════════════════════════════════════════╝');
console.log('');
let allPassed = true;
// Test 1: Santé de Gotenberg
const test1 = await testGotenbergHealth();
if (!test1) {
allPassed = false;
console.log('');
console.log('⚠️ Impossible de continuer sans accès à Gotenberg');
process.exit(1);
}
// Test 2: Conversion directe
const test2 = await testGotenbergDirect();
if (!test2) {
allPassed = false;
}
// Test 3: API Next.js
const test3 = await testNextjsApiRoute();
if (!test3) {
allPassed = false;
}
// Résumé
console.log('');
console.log('═══════════════════════════════════════════════════════════');
if (allPassed) {
console.log('✅ TOUS LES TESTS SONT PASSÉS AVEC SUCCÈS !');
console.log('');
console.log('🎯 Prochaines étapes:');
console.log(' 1. Modifier ContractEditor.tsx pour utiliser la nouvelle API');
console.log(' 2. Tester dans l\'interface staff/contrats/[id]');
console.log(' 3. Migrer les autres templates (RG, Avenants)');
} else {
console.log('⚠️ CERTAINS TESTS ONT ÉCHOUÉ');
console.log('');
console.log('Consultez les messages d\'erreur ci-dessus pour plus de détails.');
}
console.log('═══════════════════════════════════════════════════════════');
}
// Lancer les tests
runAllTests().catch(error => {
console.error('💥 Erreur fatale:', error);
process.exit(1);
});