espace-paie-odentas/MIGRATION_PDFMONKEY_GOTENBERG.md
odentas c6faceb038 feat: Préparation migration PDFMonkey vers Gotenberg
- 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
2025-12-27 21:40:27 +01:00

11 KiB

Migration PDFMonkey → Gotenberg

Contexte

Ce guide explique comment migrer la génération de PDF des contrats de travail de PDFMonkey (SaaS avec templates Liquid) vers Gotenberg (auto-hébergé).

Avantages de Gotenberg

  • Auto-hébergé : contrôle total, pas de dépendance externe
  • Gratuit et open-source
  • Rapide et performant
  • Supporte HTML/CSS nativement
  • API simple et bien documentée
  • Déployable sur Vercel, Railway, Coolify, etc.

Architecture de la Solution

┌─────────────────┐
│  Next.js App    │
│  (Formulaire)   │
└────────┬────────┘
         │ 1. Envoie données JSON
         ↓
┌─────────────────────────┐
│ API Route Next.js       │
│ /api/generate-contract  │
│                         │
│ - Compile template      │
│   Handlebars            │
│ - Génère HTML           │
└────────┬────────────────┘
         │ 2. Envoie HTML
         ↓
┌─────────────────────────┐
│   Gotenberg Service     │
│   (Docker/Auto-hébergé) │
│                         │
│ - Convertit HTML → PDF  │
│ - Retourne le PDF       │
└────────┬────────────────┘
         │ 3. Reçoit PDF
         ↓
┌─────────────────────────┐
│  Supabase Storage       │
│  (Stockage PDF)         │
└─────────────────────────┘

Fichiers Créés

1. Helpers Handlebars

Fichier : lib/handlebars-helpers.ts

Remplace les filtres Liquid par des helpers Handlebars :

  • removeFirst : équivalent de remove_first
  • contains : vérifier si une chaîne contient un pattern
  • split : diviser une chaîne
  • eq, ne, gte, gt : comparaisons
  • isEmpty, isNotEmpty : vérifier les valeurs vides
  • includesAny : vérifier plusieurs valeurs (pour les CCN)

2. Template Handlebars

Fichier : templates-contrats/cddu-handlebars.html

Template HTML/Handlebars qui remplace le template Liquid PDFMonkey.

Principales conversions :

Liquid Handlebars
{% if condition %} {{#if condition}}
{% elsif %} {{else if}}
{% assign var = value %} Variables pré-calculées en JS
{{ var | filter }} {{helper var}}
{% for item in array %} {{#each array}}

3. API Route Gotenberg

Fichier : app/api/generate-contract-pdf/route.ts

Route API qui :

  1. Authentifie l'utilisateur
  2. Charge le template Handlebars
  3. Compile le template avec les données
  4. Envoie le HTML à Gotenberg
  5. Reçoit le PDF
  6. Upload sur Supabase Storage
  7. Retourne l'URL du PDF

4. Docker Compose

Fichier : docker-compose.gotenberg.yml

Configuration Docker pour auto-héberger Gotenberg.

Installation

Étape 1 : Installer les dépendances

npm install handlebars
npm install --save-dev @types/handlebars

Étape 2 : Configurer les variables d'environnement

Ajouter dans .env.local :

# URL de Gotenberg (local ou auto-hébergé)
GOTENBERG_URL=http://localhost:3001

# Ou en production (exemple avec Railway/Coolify)
# GOTENBERG_URL=https://gotenberg.votre-domaine.com

Étape 3 : Déployer Gotenberg localement

docker-compose -f docker-compose.gotenberg.yml up -d

Vérifier que Gotenberg fonctionne :

curl http://localhost:3001/health

Étape 4 : Tester l'API

Créer un fichier de test test-generate-pdf.ts :

const response = await fetch('/api/generate-contract-pdf', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    contractId: 'abc123',
    contractType: 'cddu',
    data: {
      structure_name: 'Association Compagnie Lazara',
      employee_firstname: 'Jean',
      employee_lastname: 'GOLTIER',
      // ... autres données
    },
  }),
});

const result = await response.json();
console.log('PDF généré:', result.pdfUrl);

Déploiement en Production

Option 1 : Railway (recommandé)

  1. Créer un nouveau service sur Railway
  2. Déployer l'image Docker : gotenberg/gotenberg:8
  3. Exposer le port 3000
  4. Récupérer l'URL publique
  5. Mettre à jour GOTENBERG_URL dans Vercel

Option 2 : Coolify

  1. Créer un nouveau service
  2. Source : Docker Image
  3. Image : gotenberg/gotenberg:8
  4. Port : 3000
  5. Générer un domaine public
  6. Mettre à jour GOTENBERG_URL

Option 3 : VPS auto-hébergé

# Sur le VPS
git clone <votre-repo>
cd Projet\ Nouvel\ Espace\ Paie
docker-compose -f docker-compose.gotenberg.yml up -d

# Configurer Nginx reverse proxy
sudo nano /etc/nginx/sites-available/gotenberg

# Contenu :
server {
    listen 80;
    server_name gotenberg.votre-domaine.com;

    location / {
        proxy_pass http://localhost:3001;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

# Activer le site
sudo ln -s /etc/nginx/sites-available/gotenberg /etc/nginx/sites-enabled/
sudo systemctl reload nginx

Intégration dans le Code Existant

Modifier le formulaire de création de contrat

Dans app/staff/contrats/[id]/page.tsx ou le composant concerné :

const handleGeneratePDF = async () => {
  setIsGenerating(true);
  
  try {
    const response = await fetch('/api/generate-contract-pdf', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        contractId: contract.id,
        contractType: contract.regime === 'CDDU' ? 'cddu' : 'rg',
        data: prepareContractData(contract),
      }),
    });

    const result = await response.json();
    
    if (result.success) {
      toast.success('PDF généré avec succès');
      // Ouvrir le PDF dans un nouvel onglet
      window.open(result.pdfUrl, '_blank');
    } else {
      toast.error('Erreur lors de la génération du PDF');
    }
  } catch (error) {
    console.error('Erreur:', error);
    toast.error('Erreur lors de la génération du PDF');
  } finally {
    setIsGenerating(false);
  }
};

// Fonction pour préparer les données
function prepareContractData(contract: Contract) {
  return {
    structure_name: contract.organization.name,
    structure_adresse: contract.organization.address,
    structure_cpville: contract.organization.postal_code,
    structure_ville: contract.organization.city,
    structure_siret: contract.organization.siret,
    employee_firstname: contract.employee.first_name,
    employee_lastname: contract.employee.last_name,
    employee_dob: formatDate(contract.employee.birth_date),
    // ... mapper toutes les données
    imageUrl: contract.organization.logo_data_uri,
  };
}

Différences avec PDFMonkey

Aspect PDFMonkey Gotenberg
Hébergement SaaS externe Auto-hébergé
Coût Payant (par document) Gratuit
Template Liquid (éditeur en ligne) Handlebars (code local)
Latence Variable (réseau externe) Faible (interne)
Contrôle Limité Total
Maintenance Aucune Docker + monitoring

Gestion des Templates

Modifier un template

  1. Éditer templates-contrats/cddu-handlebars.html
  2. Tester localement
  3. Commit + push
  4. Déploiement automatique via Vercel

Créer un nouveau template

  1. Dupliquer cddu-handlebars.html
  2. Adapter le contenu
  3. Ajouter le cas dans l'API route :
case 'nouveau-type':
  templatePath = path.join(process.cwd(), 'templates-contrats', 'nouveau-type.html');
  break;

Monitoring et Logs

Vérifier les logs Gotenberg

docker logs odentas-gotenberg --tail 100 -f

Health check

curl http://localhost:3001/health

Métriques de performance

Ajouter dans l'API route :

const startTime = Date.now();
// ... génération PDF
const duration = Date.now() - startTime;
console.log(`PDF généré en ${duration}ms`);

Troubleshooting

Erreur : Gotenberg inaccessible

Symptôme : fetch failed ou timeout

Solutions :

  1. Vérifier que Gotenberg est démarré : docker ps
  2. Vérifier le health check : curl http://localhost:3001/health
  3. Vérifier les logs : docker logs odentas-gotenberg
  4. Vérifier la variable GOTENBERG_URL

Erreur : Template non trouvé

Symptôme : ENOENT: no such file or directory

Solutions :

  1. Vérifier le chemin du template
  2. Vérifier que le template existe bien dans templates-contrats/
  3. En production Vercel, vérifier que les templates sont inclus dans le build

Erreur : Rendu incorrect du PDF

Symptôme : Mise en page cassée, polices manquantes

Solutions :

  1. Vérifier le CSS dans le template
  2. Utiliser des polices web-safe ou inclure les fonts en base64
  3. Tester le HTML seul dans un navigateur
  4. Ajuster les marges dans l'API route

Performance lente

Symptôme : Génération de PDF > 5 secondes

Solutions :

  1. Augmenter les ressources Docker
  2. Optimiser les images (compression, taille)
  3. Réduire la complexité du HTML/CSS
  4. Utiliser un cache pour les templates compilés

Checklist de Migration

  • Installer les dépendances npm
  • Créer les helpers Handlebars
  • Convertir le template CDDU
  • Créer l'API route
  • Déployer Gotenberg localement
  • Tester la génération de PDF en local
  • Déployer Gotenberg en production
  • Configurer la variable GOTENBERG_URL sur Vercel
  • Intégrer dans le formulaire de contrat
  • Tester en production
  • Migrer les autres templates (RG, Avenants)
  • Désactiver PDFMonkey

Rollback

Si besoin de revenir à PDFMonkey :

  1. Réactiver les appels à l'API PDFMonkey
  2. Commenter les appels à /api/generate-contract-pdf
  3. Garder le code Gotenberg en standby

Next Steps

Une fois la migration terminée :

  1. Migrer les autres templates : RG, Avenants, etc.
  2. Optimiser les performances : cache de templates, parallélisation
  3. Ajouter des analytics : temps de génération, taux de succès
  4. Backup automatique : sauvegarder les PDFs générés
  5. Versioning des templates : Git + tags pour suivre les changements

Support

En cas de problème, consulter :


Auteur : Équipe Odentas Date : Décembre 2025 Version : 1.0