- 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)
328 lines
11 KiB
JavaScript
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);
|
|
});
|