# 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

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

### Étape 2 : Configurer les variables d'environnement

Ajouter dans `.env.local` :

```bash
# 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

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

Vérifier que Gotenberg fonctionne :
```bash
curl http://localhost:3001/health
```

### Étape 4 : Tester l'API

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

```typescript
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é

```bash
# 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é :

```typescript
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 :

```typescript
case 'nouveau-type':
  templatePath = path.join(process.cwd(), 'templates-contrats', 'nouveau-type.html');
  break;
```

## Monitoring et Logs

### Vérifier les logs Gotenberg

```bash
docker logs odentas-gotenberg --tail 100 -f
```

### Health check

```bash
curl http://localhost:3001/health
```

### Métriques de performance

Ajouter dans l'API route :

```typescript
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 :
- [Documentation Gotenberg](https://gotenberg.dev/)
- [Documentation Handlebars](https://handlebarsjs.com/)
- Logs Docker : `docker logs odentas-gotenberg`
- Logs Next.js : Console Vercel

---

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