Envoi mail salarié esign en interne depuis AWS Lambda

This commit is contained in:
odentas 2025-10-15 20:19:39 +02:00
parent 44c1238730
commit 2b945dec70
8 changed files with 1231 additions and 0 deletions

View file

@ -37,3 +37,8 @@ DOCUSEAL_PROXY_BASE=https://your-api-gateway.amazonaws.com/default/docuseal
# Development only
AUTH_BYPASS=0
DEBUG_UPSTREAM=0
# Lambda API Authentication
# Used by AWS Lambda to authenticate API calls to Espace Paie
# Generate with: openssl rand -hex 32
LAMBDA_API_KEY=your-lambda-api-key-64-chars-hex

229
ACTIONS_A_FAIRE.md Normal file
View file

@ -0,0 +1,229 @@
# 🎯 Actions à effectuer - Migration Email Signature Salarié
## ✅ Ce qui est DÉJÀ fait dans le code
- ✅ Route API créée : `app/api/emails/signature-salarie/route.ts`
- ✅ Type d'email ajouté au système universel v2
- ✅ Configuration du template email
- ✅ Code Lambda modifié (fichier fourni)
- ✅ Documentation complète
---
## 🔧 Ce que VOUS devez faire
### 1⃣ Générer une API Key sécurisée
```bash
# Exécuter cette commande pour générer une clé aléatoire
openssl rand -hex 32
```
**Exemple de résultat :**
```
a7f3d8c2b1e9f4a6d8c3b2e1f5a9d7c4b8e2f6a3d9c5b7e4f1a8d6c3b9e5f2a7
```
⚠️ **Sauvegardez cette clé dans un endroit sûr** (vous en aurez besoin 2 fois)
---
### 2⃣ Ajouter la clé dans l'Espace Paie (Local)
```bash
# Dans le terminal, depuis le dossier du projet
echo "LAMBDA_API_KEY=<collez_votre_clé_ici>" >> .env.local
```
**Exemple :**
```bash
echo "LAMBDA_API_KEY=a7f3d8c2b1e9f4a6d8c3b2e1f5a9d7c4b8e2f6a3d9c5b7e4f1a8d6c3b9e5f2a7" >> .env.local
```
---
### 3⃣ Ajouter la clé sur Vercel (Production)
#### Option A : Via interface Vercel
1. Aller sur https://vercel.com/votre-projet
2. Settings → Environment Variables
3. Ajouter une nouvelle variable :
- **Name** : `LAMBDA_API_KEY`
- **Value** : `<collez_votre_clé>`
- **Environments** : Cocher Production, Preview, Development
4. Cliquer sur "Save"
#### Option B : Via CLI Vercel
```bash
# Installer Vercel CLI si nécessaire
npm i -g vercel
# Se connecter
vercel login
# Ajouter la variable
vercel env add LAMBDA_API_KEY
# Coller la clé quand demandé
# Sélectionner : Production, Preview, Development
```
#### Redéployer
```bash
vercel --prod
```
---
### 4⃣ Configurer AWS Lambda
#### Via AWS Console
1. Aller sur https://console.aws.amazon.com/lambda/
2. Région : **eu-west-3** (Paris)
3. Trouver la fonction : **postDocuSealSalarie**
4. Onglet **Configuration** → **Environment variables**
5. Cliquer sur **Edit**
#### Ajouter ces 2 variables :
| Key | Value | Description |
|-----|-------|-------------|
| `ESPACE_PAIE_URL` | `https://staging.paie.odentas.fr` | URL de l'Espace Paie (staging ou prod) |
| `ESPACE_PAIE_API_KEY` | `<votre_clé_générée>` | **La même clé qu'à l'étape 1** |
6. Cliquer sur **Save**
#### (Optionnel) Supprimer les anciennes variables
Ces variables ne sont plus utilisées, vous pouvez les supprimer :
- `AWS_SES_FROM`
- `S3_BUCKET_NAME_EMAILS`
- `AIRTABLE_API_KEY`
---
### 5⃣ Mettre à jour le code de la Lambda
1. Toujours dans AWS Lambda → **postDocuSealSalarie**
2. Onglet **Code**
3. Ouvrir le fichier `index.js`
4. **Remplacer tout le contenu** par le code du fichier `LAMBDA_SIGNATURE_SALARIE_UPDATED.js` (fourni dans ce projet)
5. Cliquer sur **Deploy**
---
### 6⃣ Tester
#### Test 1 : Vérifier que l'API répond
```bash
# Depuis votre terminal local
curl -X POST https://paie.odentas.fr/api/emails/signature-salarie \
-H "X-API-Key: <votre_clé>" \
-H "Content-Type: application/json" \
-d '{
"employeeEmail": "votre-email@example.com",
"signatureLink": "https://test.com",
"reference": "TEST-001",
"organizationName": "Test",
"firstName": "Test",
"matricule": "TEST001"
}'
```
**Réponse attendue :**
```json
{
"success": true,
"messageId": "01000192abc...",
"recipient": "votre-email@example.com",
"reference": "TEST-001"
}
```
**Si erreur 401** : Vérifiez que la clé API est correcte
#### Test 2 : Déclencher un vrai webhook
1. Faire signer un contrat par l'employeur dans DocuSeal
2. Observer les logs Lambda dans CloudWatch
3. Vérifier l'email reçu par le salarié
4. Vérifier dans l'interface : `/staff/email-logs`
---
### 7⃣ Vérifier les logs
#### Dans AWS CloudWatch
1. AWS Console → CloudWatch → Log groups
2. Chercher : `/aws/lambda/postDocuSealSalarie`
3. Logs à chercher :
```
📤 Appel de l'API Espace Paie pour envoi email...
✅ E-mail envoyé via l'API Espace Paie
```
#### Dans l'Espace Paie
1. Aller sur : `https://staging.paie.odentas.fr/staff/email-logs`
2. Filtrer par type : **signature-request-salarie**
3. Vérifier le statut : **sent**
4. Cliquer sur une ligne pour voir le contenu HTML
---
## 🎉 C'est terminé !
Si tout est OK :
- ✅ Les emails de signature sont envoyés via le système universel v2
- ✅ Tous les emails sont loggés dans la base de données
- ✅ Vous pouvez voir les logs dans l'interface Staff
- ✅ Le template est cohérent avec les autres emails
---
## 🆘 En cas de problème
### Erreur 401 "Unauthorized"
➡️ **Solution** : Vérifiez que `LAMBDA_API_KEY` (Espace Paie) et `ESPACE_PAIE_API_KEY` (Lambda) ont exactement la même valeur.
### Erreur 500 "Configuration Error"
➡️ **Solution** : La variable `LAMBDA_API_KEY` n'est pas définie sur Vercel. Vérifiez l'étape 3.
### Lambda : Erreur "ESPACE_PAIE_URL is not defined"
➡️ **Solution** : Les variables d'environnement ne sont pas configurées dans Lambda. Vérifiez l'étape 4.
### Email non reçu
➡️ **Solutions** :
1. Vérifier CloudWatch logs pour voir si l'API a été appelée
2. Vérifier dans `/staff/email-logs` si l'email apparaît
3. Vérifier les credentials AWS SES (normalement OK si les autres emails fonctionnent)
4. Vérifier que l'email du salarié est valide
### L'API ne répond pas
➡️ **Solutions** :
1. Vérifier que l'Espace Paie est déployé sur Vercel
2. Vérifier que la route existe : `app/api/emails/signature-salarie/route.ts`
3. Regarder les logs Vercel
---
## 📞 Support
- **Documentation complète** : `LAMBDA_EMAIL_SIGNATURE_SALARIE_GUIDE.md`
- **Résumé des changements** : `LAMBDA_EMAIL_SIGNATURE_SALARIE_SUMMARY.md`
- **Code Lambda mis à jour** : `LAMBDA_SIGNATURE_SALARIE_UPDATED.js`
---
*Bonne chance ! 🚀*

View file

@ -0,0 +1,357 @@
# Configuration Lambda → Espace Paie : Email Signature Salarié
## 📋 Vue d'ensemble
Cette documentation détaille la configuration nécessaire pour faire communiquer la Lambda AWS `postDocuSealSalarie` avec l'Espace Paie pour l'envoi d'emails de signature aux salariés.
### Architecture
```
DocuSeal Webhook → Lambda postDocuSealSalarie → API Espace Paie → Système Email Universel v2 → AWS SES
Logs emails (Supabase)
```
### Avantages de cette architecture
**Centralisation** : Tous les emails passent par le système universel v2
**Logs automatiques** : Tous les envois sont enregistrés dans `email_logs`
**Cohérence** : Même template et même système que les autres emails
**Maintenance simplifiée** : Un seul endroit pour gérer les emails
**Debugging facilité** : Logs accessibles dans l'interface Staff
---
## 🔧 Configuration AWS Lambda
### Variables d'environnement à ajouter/modifier
Dans la configuration de la Lambda `postDocuSealSalarie` :
```env
# Existantes (à conserver)
DOCUSEAL_API_TOKEN=xxxxx
SUPABASE_URL=https://xxxxx.supabase.co
SUPABASE_SERVICE_ROLE=xxxxx
ZAPIER_WEBHOOK_URL=https://hooks.zapier.com/xxxxx
# NOUVELLES variables à ajouter
ESPACE_PAIE_URL=https://staging.paie.odentas.fr
ESPACE_PAIE_API_KEY=<générer une clé secrète forte>
```
### ⚠️ Variables à SUPPRIMER
Les variables suivantes ne sont plus nécessaires car l'envoi d'email est géré par l'Espace Paie :
```env
# À SUPPRIMER
AWS_SES_FROM=paie@odentas.fr
S3_BUCKET_NAME_EMAILS=odentas-emails
AIRTABLE_API_KEY=xxxxx
```
### Génération de l'API Key
Pour générer une API Key sécurisée :
```bash
# Méthode 1 : OpenSSL
openssl rand -hex 32
# Méthode 2 : Node.js
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# Méthode 3 : Python
python3 -c "import secrets; print(secrets.token_hex(32))"
```
**Important** : Utilisez la même clé dans AWS Lambda et dans l'Espace Paie.
---
## 🌐 Configuration Espace Paie
### Variables d'environnement à ajouter
Dans `.env.local` (dev) et dans Vercel (production) :
```env
# API Key pour authentifier les appels depuis la Lambda
LAMBDA_API_KEY=<même clé que ESPACE_PAIE_API_KEY de la Lambda>
```
### Déploiement de la variable sur Vercel
```bash
# Ajouter la variable d'environnement sur Vercel
vercel env add LAMBDA_API_KEY
# Saisir la valeur : <la même que dans AWS Lambda>
# Environnements : Production, Preview, Development
# Redéployer pour prendre en compte la nouvelle variable
vercel --prod
```
---
## 📝 Fichiers modifiés/créés
### 1. Espace Paie (Next.js)
#### ✨ Nouveau fichier créé
- **`app/api/emails/signature-salarie/route.ts`**
- Route API POST pour recevoir les demandes d'envoi d'email
- Authentification par API Key
- Utilise `sendUniversalEmailV2()`
- Logs automatiques dans `email_logs`
#### 📝 Fichiers modifiés
- **`lib/emailTemplateService.ts`**
- Ajout du type `'signature-request-salarie'` dans `EmailTypeV2`
- Ajout des champs `matricule` et `typecontrat` dans `EmailDataV2`
- Configuration du template email avec cartes info et détails
- **`lib/cleanEnv.ts`**
- Ajout de `LAMBDA_API_KEY` dans l'objet `ENV`
### 2. Lambda AWS
#### 📝 Fichier modifié
- **`index.js`** (version mise à jour fournie : `LAMBDA_SIGNATURE_SALARIE_UPDATED.js`)
- ❌ Suppression de `sendSignatureEmail()` (envoi direct SES)
- ❌ Suppression de `uploadEmailToS3()` (stockage S3)
- ❌ Suppression de `logToAirtable()` (logging Airtable)
- ❌ Suppression de `generateEmailHtml()` (template HTML)
- ✅ Ajout de l'appel API vers l'Espace Paie
- ✅ Headers d'authentification avec API Key
- ✅ Gestion d'erreurs améliorée
---
## 🚀 Déploiement
### Étape 1 : Déployer l'Espace Paie
```bash
# En local, vérifier que la variable existe
grep LAMBDA_API_KEY .env.local
# Si absente, l'ajouter
echo "LAMBDA_API_KEY=votre_cle_secrete_ici" >> .env.local
# Sur Vercel (production)
vercel env add LAMBDA_API_KEY
# Puis redéployer
vercel --prod
```
### Étape 2 : Mettre à jour la Lambda AWS
1. **Via AWS Console** :
- Aller dans AWS Lambda → `postDocuSealSalarie`
- Onglet "Configuration" → "Environment variables"
- Ajouter :
- `ESPACE_PAIE_URL` = `https://staging.paie.odentas.fr` (ou prod)
- `ESPACE_PAIE_API_KEY` = `<votre clé générée>`
- Supprimer les anciennes variables (optionnel mais recommandé)
2. **Mettre à jour le code** :
- Remplacer le contenu de `index.js` par celui de `LAMBDA_SIGNATURE_SALARIE_UPDATED.js`
- Cliquer sur "Deploy"
### Étape 3 : Tester
```bash
# Test depuis la Lambda (déclencher un webhook DocuSeal)
# Vérifier les logs CloudWatch pour :
# - "📤 Appel de l'API Espace Paie pour envoi email..."
# - "✅ E-mail envoyé via l'API Espace Paie"
# Vérifier dans l'Espace Paie
# - Aller sur /staff/email-logs
# - Chercher le type "signature-request-salarie"
# - Vérifier le statut "sent"
```
---
## 🔍 Debug & Monitoring
### Logs Lambda (CloudWatch)
```
📤 Appel de l'API Espace Paie pour envoi email...
✅ E-mail envoyé via l'API Espace Paie: { success: true, messageId: '...' }
```
### Logs Espace Paie (Console serveur)
```
=== API Email Signature Salarié ===
✅ Authentication successful
📦 Données reçues: { employeeEmail: '...', reference: '...', ... }
📧 Préparation de l'envoi de l'email: { to: '...', type: 'signature-request-salarie' }
✅ Email envoyé avec succès: { messageId: '...', recipient: '...', reference: '...' }
```
### Vérification dans l'interface
1. **Interface Staff** : `/staff/email-logs`
- Filtrer par type : `signature-request-salarie`
- Vérifier le statut et le contenu HTML
2. **Base de données** : Table `email_logs`
```sql
SELECT * FROM email_logs
WHERE email_type = 'signature-request-salarie'
ORDER BY created_at DESC
LIMIT 10;
```
---
## 🛡️ Sécurité
### API Key
- ✅ Utilisez une clé de minimum 32 caractères hexadécimaux (256 bits)
- ✅ Ne commitez JAMAIS la clé dans le code
- ✅ Utilisez des variables d'environnement différentes par environnement
- ✅ Renouvelez la clé périodiquement (tous les 6 mois)
### HTTPS
- ✅ L'API Espace Paie doit être en HTTPS uniquement
- ✅ Vérifiez le certificat SSL (fait automatiquement par axios)
### Rate Limiting
À considérer pour le futur :
- Limiter le nombre de requêtes par minute depuis une IP
- Implémenter un système de throttling
---
## 📊 Données envoyées
### Payload de la Lambda vers l'API
```json
{
"employeeEmail": "salarie@example.com",
"signatureLink": "https://staging.paie.odentas.fr/odentas-sign?docuseal_id=abc123",
"reference": "CDDU-2025-001",
"firstName": "Jean",
"organizationName": "Théâtre National",
"matricule": "SAL001",
"typecontrat": "CDDU",
"startDate": "15/01/2025",
"profession": "Comédien",
"productionName": "Le Mariage de Figaro",
"organizationId": "uuid-org",
"contractId": "uuid-contract"
}
```
### Réponse de l'API
```json
{
"success": true,
"messageId": "01000192...",
"recipient": "salarie@example.com",
"reference": "CDDU-2025-001"
}
```
---
## ❌ Gestion des erreurs
### Erreurs possibles
| Code | Erreur | Cause | Solution |
|------|--------|-------|----------|
| 401 | Unauthorized | API Key invalide ou manquante | Vérifier `LAMBDA_API_KEY` et `ESPACE_PAIE_API_KEY` |
| 400 | Bad Request | Champs requis manquants | Vérifier le payload envoyé |
| 500 | Server Error | Erreur lors de l'envoi SES | Vérifier les credentials AWS SES |
| 500 | Configuration Error | `LAMBDA_API_KEY` non définie | Ajouter la variable dans Vercel |
### En cas d'échec
La Lambda retourne une erreur 500 mais **ne bloque pas** :
- Le contrat reste signé par l'employeur dans Supabase
- Le webhook Zapier est appelé quand même
- L'erreur est loggée dans CloudWatch
---
## 🔄 Rollback
Si vous devez revenir en arrière :
1. **Restaurer l'ancien code Lambda**
- Utiliser le fichier `/tmp/aws-toolkit-vscode/lambda/eu-west-3/postDocuSealSalarie/index.js`
2. **Rétablir les variables d'environnement**
- Réajouter `AWS_SES_FROM`, `S3_BUCKET_NAME_EMAILS`, `AIRTABLE_API_KEY`
- Supprimer `ESPACE_PAIE_URL` et `ESPACE_PAIE_API_KEY`
3. **L'Espace Paie reste compatible**
- La route API peut rester en place (elle ne sera simplement pas appelée)
- Le nouveau type d'email `signature-request-salarie` ne causera pas d'erreur
---
## 📚 Ressources
- [Système Email Universel v2](./EMAIL_TEMPLATE_SYSTEM.md)
- [Logs Emails](./DEBUG_EMAIL_LOGS.md)
- [DocuSeal API](https://docs.docuseal.co/)
- [AWS Lambda Configuration](https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html)
---
## ✅ Checklist de déploiement
### Avant déploiement
- [ ] Générer une API Key sécurisée (32 caractères hex minimum)
- [ ] Tester localement la route API avec un faux payload
- [ ] Vérifier que le template email s'affiche correctement
### Espace Paie
- [ ] Ajouter `LAMBDA_API_KEY` dans `.env.local`
- [ ] Ajouter `LAMBDA_API_KEY` sur Vercel (tous les environnements)
- [ ] Déployer sur Vercel
- [ ] Vérifier que la route `/api/emails/signature-salarie` répond
### Lambda AWS
- [ ] Ajouter `ESPACE_PAIE_URL` dans les variables d'env
- [ ] Ajouter `ESPACE_PAIE_API_KEY` dans les variables d'env (même valeur que `LAMBDA_API_KEY`)
- [ ] Mettre à jour le code de la Lambda
- [ ] Déployer
### Tests
- [ ] Déclencher un webhook DocuSeal (signature employeur)
- [ ] Vérifier les logs CloudWatch de la Lambda
- [ ] Vérifier les logs de l'API Espace Paie
- [ ] Vérifier que l'email a été reçu par le salarié
- [ ] Vérifier l'entrée dans `/staff/email-logs`
- [ ] Vérifier le contenu HTML de l'email
### Post-déploiement
- [ ] Monitorer les logs pendant 24h
- [ ] Vérifier le taux de succès des emails
- [ ] Documenter tout problème rencontré
- [ ] Célébrer ! 🎉
---
*Dernière mise à jour : 15 octobre 2025*

View file

@ -0,0 +1,253 @@
# 📧 Résumé : Migration Email Signature Salarié vers Système Universel v2
## 🎯 Objectif atteint
Au lieu que la Lambda `postDocuSealSalarie` envoie directement les emails via AWS SES, elle appelle maintenant une API de l'Espace Paie qui utilise le système universel v2. Tous les emails sont maintenant loggés dans la base de données.
---
## ✅ Ce qui a été fait
### 1. **Espace Paie - Système Email**
#### Fichier : `lib/emailTemplateService.ts`
- ✅ Ajout du type `'signature-request-salarie'` dans `EmailTypeV2`
- ✅ Ajout des champs `matricule` et `typecontrat` dans `EmailDataV2`
- ✅ Configuration complète du template avec :
- Subject : `"Signez votre contrat {{organizationName}}"`
- Carte info : employeur + matricule
- Carte détails : référence, type contrat, dates, poste, production
- Bouton CTA : "Signer le contrat"
- Footer personnalisé pour les salariés
#### Fichier : `lib/cleanEnv.ts`
- ✅ Ajout de `LAMBDA_API_KEY` dans l'objet `ENV`
#### Fichier : `app/api/emails/signature-salarie/route.ts` ⭐ NOUVEAU
- ✅ Route POST protégée par API Key
- ✅ Validation des données entrantes
- ✅ Appel à `sendUniversalEmailV2()` avec le type `signature-request-salarie`
- ✅ Retour du messageId SES
- ✅ Logs automatiques dans `email_logs` (via le système universel)
### 2. **Lambda AWS**
#### Fichier : `LAMBDA_SIGNATURE_SALARIE_UPDATED.js` ⭐ CODE MIS À JOUR
Changements par rapport à l'ancien code :
**❌ Supprimé :**
- `sendSignatureEmail()` - Envoi direct via SES
- `uploadEmailToS3()` - Upload du HTML sur S3
- `logToAirtable()` - Journalisation Airtable
- `generateEmailHtml()` - Template HTML hardcodé
- Import de `ses` d'AWS SDK
**✅ Ajouté :**
- Appel POST vers `${ESPACE_PAIE_URL}/api/emails/signature-salarie`
- Header `X-API-Key` pour l'authentification
- Payload structuré avec tous les champs nécessaires
- Gestion d'erreur améliorée avec timeout
**📝 Variables d'environnement :**
- **Nouvelles** : `ESPACE_PAIE_URL`, `ESPACE_PAIE_API_KEY`
- **À supprimer** : `AWS_SES_FROM`, `S3_BUCKET_NAME_EMAILS`, `AIRTABLE_API_KEY`
### 3. **Documentation**
#### Fichier : `LAMBDA_EMAIL_SIGNATURE_SALARIE_GUIDE.md` ⭐ NOUVEAU
Guide complet avec :
- Architecture du système
- Configuration AWS Lambda
- Configuration Espace Paie
- Déploiement pas à pas
- Debug & monitoring
- Sécurité
- Gestion des erreurs
- Checklist de déploiement
---
## 🔧 Configuration requise
### Variables d'environnement Espace Paie
```env
# .env.local (développement)
LAMBDA_API_KEY=<générer une clé de 64 caractères hex>
# Vercel (production)
# Ajouter via : vercel env add LAMBDA_API_KEY
```
### Variables d'environnement Lambda AWS
```env
# À AJOUTER
ESPACE_PAIE_URL=https://staging.paie.odentas.fr # ou production
ESPACE_PAIE_API_KEY=<même clé que LAMBDA_API_KEY>
# À CONSERVER (pour Supabase, DocuSeal, Zapier)
DOCUSEAL_API_TOKEN=xxxxx
SUPABASE_URL=xxxxx
SUPABASE_SERVICE_ROLE=xxxxx
ZAPIER_WEBHOOK_URL=xxxxx
# À SUPPRIMER (optionnel mais recommandé)
AWS_SES_FROM=paie@odentas.fr
S3_BUCKET_NAME_EMAILS=odentas-emails
AIRTABLE_API_KEY=xxxxx
```
---
## 📊 Flux de données
### Avant (ancien système)
```
DocuSeal → Lambda → AWS SES → Salarié
S3 (HTML)
Airtable (logs)
```
### Après (nouveau système) ⭐
```
DocuSeal → Lambda → API Espace Paie → Système Email v2 → AWS SES → Salarié
Supabase email_logs
Interface /staff/email-logs
```
---
## 🎁 Avantages
| Aspect | Avant | Après |
|--------|-------|-------|
| **Templates** | HTML hardcodé dans Lambda | Template universel v2 centralisé |
| **Logs** | Airtable (externe) | Supabase `email_logs` (intégré) |
| **Debugging** | CloudWatch uniquement | CloudWatch + Interface Staff + DB |
| **Cohérence** | Email différent des autres | Même style que tous les emails |
| **Maintenance** | 4 fichiers à maintenir | 1 configuration template |
| **HTML Storage** | S3 manuel | Automatique dans logs |
---
## 🚀 Pour déployer
### 1. Générer l'API Key
```bash
openssl rand -hex 32
# Copier la clé générée
```
### 2. Espace Paie
```bash
# Local
echo "LAMBDA_API_KEY=<clé_générée>" >> .env.local
# Vercel
vercel env add LAMBDA_API_KEY
# Coller la clé
# Puis redéployer
vercel --prod
```
### 3. Lambda AWS
1. Aller dans AWS Console → Lambda → `postDocuSealSalarie`
2. Configuration → Environment variables
3. Ajouter :
- `ESPACE_PAIE_URL` = `https://staging.paie.odentas.fr`
- `ESPACE_PAIE_API_KEY` = `<clé_générée>` (la même)
4. Code → Remplacer par `LAMBDA_SIGNATURE_SALARIE_UPDATED.js`
5. Deploy
### 4. Tester
1. Déclencher un webhook DocuSeal
2. Vérifier CloudWatch logs
3. Vérifier `/staff/email-logs`
4. Vérifier la réception de l'email
---
## 🔍 Vérifications
### ✅ L'API fonctionne
```bash
curl -X POST https://staging.paie.odentas.fr/api/emails/signature-salarie \
-H "X-API-Key: votre_cle" \
-H "Content-Type: application/json" \
-d '{
"employeeEmail": "test@example.com",
"signatureLink": "https://test.com",
"reference": "TEST-001",
"organizationName": "Test Org"
}'
```
Réponse attendue :
```json
{
"success": true,
"messageId": "01000192...",
"recipient": "test@example.com",
"reference": "TEST-001"
}
```
### ✅ Les logs apparaissent
1. Interface : `/staff/email-logs`
2. Filtrer par type : `signature-request-salarie`
3. Vérifier le statut `sent`
4. Cliquer pour voir le HTML
---
## 📁 Fichiers du projet
```
Espace Paie/
├── app/api/emails/signature-salarie/
│ └── route.ts ⭐ NOUVEAU
├── lib/
│ ├── emailTemplateService.ts ✏️ MODIFIÉ
│ └── cleanEnv.ts ✏️ MODIFIÉ
├── LAMBDA_SIGNATURE_SALARIE_UPDATED.js ⭐ NOUVEAU (à copier dans Lambda)
├── LAMBDA_EMAIL_SIGNATURE_SALARIE_GUIDE.md ⭐ NOUVEAU
└── LAMBDA_EMAIL_SIGNATURE_SALARIE_SUMMARY.md ⭐ CE FICHIER
```
---
## 🆘 Support
En cas de problème :
1. **Logs Lambda** : CloudWatch Logs
2. **Logs API** : Console serveur Next.js (Vercel logs)
3. **Logs email** : `/staff/email-logs` dans l'interface
4. **Documentation** : `LAMBDA_EMAIL_SIGNATURE_SALARIE_GUIDE.md`
---
## 📝 Notes
- ✅ Compatible avec l'ancien système (possibilité de rollback)
- ✅ Pas d'impact sur les autres emails
- ✅ Le template peut être modifié dans `emailTemplateService.ts`
- ✅ L'API peut être réutilisée pour d'autres webhooks Lambda
---
*Implémentation réalisée le 15 octobre 2025*
*Système Email Universel v2 - Odentas Espace Paie*

View file

@ -0,0 +1,214 @@
const AWS = require('aws-sdk');
const axios = require('axios');
const dynamoDB = new AWS.DynamoDB.DocumentClient();
// Supabase (option 1 - REST avec service role)
const SUPABASE_URL = process.env.SUPABASE_URL; // ex: https://xxxxx.supabase.co
const SUPABASE_SERVICE_ROLE = process.env.SUPABASE_SERVICE_ROLE; // secret service role JWT
// Fonction principale de gestion du webhook
exports.handler = async (event) => {
console.log("Webhook reçu :", JSON.stringify(event, null, 2));
try {
const body = JSON.parse(event.body);
const submissionId = body.data.submission_id;
const documentName = body.data.documents[0].name;
// Récupération des détails de la soumission depuis DocuSeal
const docusealResponse = await axios.get(`https://api.docuseal.eu/submissions/${submissionId}`, {
headers: {
'X-Auth-Token': process.env.DOCUSEAL_API_TOKEN
}
});
console.log("Réponse de l'API DocuSeal :", JSON.stringify(docusealResponse.data, null, 2));
// Récupération du slug du salarié
const employeeSlug = extractEmployeeSlug(docusealResponse.data);
if (!employeeSlug) throw new Error("Slug pour le salarié introuvable dans la réponse de DocuSeal");
// Récupération des données du document dans DynamoDB
const dynamoData = await retrieveDynamoData(documentName);
if (!dynamoData) {
console.log("Aucune donnée trouvée dans DynamoDB pour la clé :", documentName);
return {
statusCode: 404,
body: JSON.stringify({ message: "Aucune donnée trouvée" })
};
}
// Extraire les champs utiles (dont la référence) depuis DynamoDB
const {
employeeEmail,
reference,
salarie,
date,
poste,
analytique,
structure,
prenom_salarie,
prenom_signataire,
code_employeur,
matricule,
typecontrat,
organization_id,
contract_id
} = dynamoData;
// Met à jour Supabase: contrat signé par employeur = "Oui" pour le contract_number = reference
try {
await updateSupabaseEmployerSigned(reference);
} catch (err) {
// Ne pas bloquer complètement le flux d'envoi d'email, mais journaliser clairement l'erreur
console.error("Echec de mise à jour Supabase (contrat_signe_par_employeur):", err.message);
}
// Formater la date au format JJ/MM/AAAA
let formattedDate;
if (dynamoData.date) {
const parsedDate = new Date(dynamoData.date);
if (!isNaN(parsedDate)) {
formattedDate = parsedDate.toLocaleDateString('fr-FR'); // Résultat au format JJ/MM/AAAA
} else {
console.warn("La date fournie n'est pas valide. Utilisation de la valeur brute.");
formattedDate = dynamoData.date; // Utiliser la valeur brute si la date est invalide
}
} else {
formattedDate = ''; // Valeur par défaut si la date n'est pas définie
}
// Génération du lien de signature
const signatureLink = `https://staging.paie.odentas.fr/odentas-sign?docuseal_id=${employeeSlug}`;
// ============================================
// NOUVEAU : Appel API Espace Paie au lieu d'envoi direct SES
// ============================================
console.log("📤 Appel de l'API Espace Paie pour envoi email...");
const emailPayload = {
employeeEmail: employeeEmail,
signatureLink: signatureLink,
reference: reference,
firstName: prenom_salarie,
organizationName: structure,
matricule: matricule,
typecontrat: typecontrat,
startDate: formattedDate,
profession: poste,
productionName: analytique,
organizationId: organization_id,
contractId: contract_id
};
// Appel de l'API Espace Paie et du webhook Zapier en parallèle
const apiPromise = axios.post(
`${process.env.ESPACE_PAIE_URL}/api/emails/signature-salarie`,
emailPayload,
{
headers: {
'X-API-Key': process.env.ESPACE_PAIE_API_KEY,
'Content-Type': 'application/json'
},
timeout: 10000
}
);
const zapierPromise = axios.post(process.env.ZAPIER_WEBHOOK_URL, {
Contrat: reference,
Type: "autosignature"
}).catch(error => {
console.error("Erreur lors de l'appel au webhook Zapier :", error);
});
const [espacePaieResponse, _] = await Promise.all([apiPromise, zapierPromise]);
console.log("✅ E-mail envoyé via l'API Espace Paie:", espacePaieResponse.data);
return {
statusCode: 200,
body: JSON.stringify({
message: "Traitement du webhook terminé.",
emailSent: true,
messageId: espacePaieResponse.data.messageId
})
};
} catch (error) {
console.error("Erreur lors du traitement du webhook :", error);
return {
statusCode: 500,
body: JSON.stringify({
message: "Erreur lors du traitement du webhook",
error: error.toString()
})
};
}
};
// Fonction pour extraire le slug du salarié
function extractEmployeeSlug(data) {
if (Array.isArray(data) && data.length > 0) {
const employeeData = data.find(sub => sub.role === "Salarié");
return employeeData ? employeeData.slug : null;
} else if (data.submitters && Array.isArray(data.submitters)) {
const employeeData = data.submitters.find(sub => sub.role === "Salarié");
return employeeData ? employeeData.slug : null;
}
return null;
}
// Fonction pour récupérer les données dans DynamoDB
async function retrieveDynamoData(documentName) {
const params = {
TableName: 'DocuSealNotification',
Key: { submission_id: documentName }
};
try {
const result = await dynamoDB.get(params).promise();
return result.Item;
} catch (error) {
console.error("Erreur lors de la récupération des données de DynamoDB :", error);
return null;
}
}
// Mise à jour Supabase: cddu_contracts (contrat_signe_par_employeur = "Oui") en filtrant par contract_number = reference
async function updateSupabaseEmployerSigned(reference) {
if (!SUPABASE_URL || !SUPABASE_SERVICE_ROLE) {
console.error("SUPABASE_URL ou SUPABASE_SERVICE_ROLE manquant(s) dans les variables d'environnement.");
throw new Error("Configuration Supabase incomplète.");
}
if (!reference) {
console.error("Référence vide ou invalide pour la mise à jour Supabase.");
throw new Error("Reference manquante pour update Supabase.");
}
const endpoint =
`${SUPABASE_URL}/rest/v1/cddu_contracts?contract_number=eq.${encodeURIComponent(reference)}&contrat_signe_par_employeur=neq.Oui`;
try {
const { data, status } = await axios.patch(
endpoint,
{ contrat_signe_par_employeur: "Oui" },
{
headers: {
apikey: SUPABASE_SERVICE_ROLE,
Authorization: `Bearer ${SUPABASE_SERVICE_ROLE}`,
'Content-Type': 'application/json',
Prefer: 'return=representation'
},
timeout: 8000
}
);
console.log("Mise à jour Supabase (employeur signé) statut:", status, "data:", JSON.stringify(data));
return data;
} catch (err) {
// Log étendu mais sans secrets
const status = err.response?.status;
const respData = err.response?.data;
console.error("Erreur update Supabase:", status, respData || err.message);
throw new Error(`Echec update Supabase: ${status || ''} ${respData ? JSON.stringify(respData) : err.message}`);
}
}

View file

@ -0,0 +1,130 @@
import { NextRequest, NextResponse } from 'next/server';
import { sendUniversalEmailV2, EmailDataV2 } from '@/lib/emailTemplateService';
import { ENV } from '@/lib/cleanEnv';
/**
* POST /api/emails/signature-salarie
*
* Route API appelée par la Lambda postDocuSealSalarie pour envoyer l'email de signature au salarié
* Utilise le système universel d'emails v2 avec logging automatique
*
* Authentification : API Key dans le header X-API-Key
*/
export async function POST(request: NextRequest) {
console.log('=== API Email Signature Salarié ===');
try {
// 1. Vérification de l'authentification via API Key
const apiKey = request.headers.get('X-API-Key');
const validApiKey = ENV.LAMBDA_API_KEY;
if (!validApiKey) {
console.error('❌ Configuration error: LAMBDA_API_KEY not configured');
return NextResponse.json(
{ error: 'Server configuration error' },
{ status: 500 }
);
}
if (!apiKey || apiKey !== validApiKey) {
console.error('❌ Unauthorized: Invalid or missing API key');
return NextResponse.json(
{ error: 'Unauthorized' },
{ status: 401 }
);
}
console.log('✅ Authentication successful');
// 2. Récupération et validation des données
const data = await request.json();
console.log('📦 Données reçues:', {
employeeEmail: data.employeeEmail,
reference: data.reference,
structure: data.structure,
firstName: data.firstName,
matricule: data.matricule
});
const {
employeeEmail,
signatureLink,
reference,
firstName,
organizationName,
matricule,
typecontrat,
startDate,
profession,
productionName,
organizationId,
contractId
} = data;
// Validation des champs requis
if (!employeeEmail || !signatureLink || !reference || !organizationName) {
console.error('❌ Champs requis manquants');
return NextResponse.json(
{
error: 'Champs manquants',
required: ['employeeEmail', 'signatureLink', 'reference', 'organizationName']
},
{ status: 400 }
);
}
// 3. Préparation des données de l'email
const emailData: EmailDataV2 = {
firstName: firstName || 'Salarié',
organizationName: organizationName,
matricule: matricule || 'N/A',
contractReference: reference,
typecontrat: typecontrat || 'CDDU',
startDate: startDate || '',
profession: profession || '',
productionName: productionName || '',
ctaUrl: signatureLink,
// Ajout des IDs pour le logging (si disponibles)
organizationId: organizationId,
contractId: contractId
};
console.log('📧 Préparation de l\'envoi de l\'email:', {
to: employeeEmail,
type: 'signature-request-salarie',
subject: `Signez votre contrat ${organizationName}`
});
// 4. Envoi de l'email via le système universel v2
const messageId = await sendUniversalEmailV2({
type: 'signature-request-salarie',
toEmail: employeeEmail,
data: emailData,
});
console.log('✅ Email envoyé avec succès:', {
messageId,
recipient: employeeEmail,
reference
});
// 5. Retour du succès avec le messageId SES
return NextResponse.json({
success: true,
messageId,
recipient: employeeEmail,
reference
});
} catch (error) {
console.error('❌ Erreur lors de l\'envoi de l\'email de signature salarié:', error);
return NextResponse.json(
{
error: 'Échec de l\'envoi de l\'email',
message: error instanceof Error ? error.message : 'Unknown error'
},
{ status: 500 }
);
}
}

View file

@ -47,6 +47,9 @@ export const ENV = {
// APIs
UPSTREAM_API_BASE: cleanEnv('UPSTREAM_API_BASE'),
// Sécurité
LAMBDA_API_KEY: cleanEnv('LAMBDA_API_KEY'),
// Autres
NODE_ENV: process.env.NODE_ENV
};

View file

@ -34,6 +34,7 @@ export type EmailTypeV2 =
| 'signature-request'
| 'signature-request-employer'
| 'signature-request-employee'
| 'signature-request-salarie' // Nouveau type pour demande signature salarié (depuis Lambda DocuSeal)
| 'bulk-signature-notification' // Nouveau type pour notification de signatures en masse
| 'salary-transfer-notification' // Nouveau type pour notification d'appel à virement
| 'contribution-notification' // Nouveau type pour notification de cotisations
@ -95,6 +96,9 @@ export interface EmailDataV2 {
ticketStatus?: string;
userEmail?: string;
userMessage?: string;
// Ajout des champs pour la signature salarié (depuis Lambda DocuSeal)
matricule?: string;
typecontrat?: string;
[key: string]: unknown;
}
@ -694,6 +698,42 @@ const EMAIL_TEMPLATES_V2: Record<EmailTypeV2, EmailTemplateV2> = {
}
},
'signature-request-salarie': {
subject: 'Signez votre contrat {{organizationName}}',
title: 'Demande de signature électronique',
greeting: '{{#if firstName}}Bonjour {{firstName}},{{/if}}',
mainMessage: 'Nous vous invitons à signer votre contrat de travail ci-dessous.<br><br>Cliquez sur « Signer le contrat » pour accéder à Odentas Sign. Vous pourrez télécharger votre contrat dès validation de votre signature.',
ctaText: 'Signer le contrat',
closingMessage: 'Pour toute question, contactez-nous à <a href="mailto:paie@odentas.fr" style="color:#0B5FFF; text-decoration:none;">paie@odentas.fr</a>.',
footerText: 'Vous recevez cet e-mail car votre employeur ou futur employeur ({{organizationName}}) est client d\'Odentas Media SAS, pour vous notifier d\'une action sur votre contrat de travail avec cet employeur. Si vous pensez avoir reçu cet e-mail par erreur, merci de contacter notre équipe à l\'adresse paie@odentas.fr.',
preheaderText: 'Signature électronique · {{organizationName}} · Signez votre contrat',
colors: {
headerColor: STANDARD_COLORS.HEADER,
titleColor: '#0F172A',
buttonColor: STANDARD_COLORS.BUTTON,
buttonTextColor: STANDARD_COLORS.BUTTON_TEXT,
cardBackgroundColor: '#FFFFFF',
cardBorder: '#E5E7EB',
cardTitleColor: '#0F172A',
alertIndicatorColor: '#22C55E',
},
infoCard: [
{ label: 'Votre employeur', key: 'organizationName' },
{ label: 'Votre matricule', key: 'matricule' },
],
detailsCard: {
title: 'Détails du contrat',
rows: [
{ label: 'Référence', key: 'contractReference' },
{ label: 'Type de contrat', key: 'typecontrat' },
{ label: 'Employeur', key: 'organizationName' },
{ label: 'Début contrat', key: 'startDate' },
{ label: 'Poste', key: 'profession' },
{ label: 'Production', key: 'productionName' },
]
}
},
'bulk-signature-notification': {
subject: 'Contrats à signer {{contractCount}} signature{{#if (gt contractCount 1)}}s{{/if}} en attente',
title: 'Contrats en attente de signature',