#63 — Email Notification
PLANIFIÉ
Priorité: 🟠 HAUTE · Type: TYPE B · Conteneur: rgz-api · Code: app/services/email.pyDépendances: #1 rgz-api, #24 factures-pdf
Description
Service d'email SMTP pour notifications formelles : rapports mensuels revendeurs, factures PDF en pièce jointe, alertes incidents critiques, confirmations onboarding. Utilise Python smtplib + Jinja2 templates HTML. Support attachements (PDF, PNG, JSON).
Chaque email est archivé dans la base pour audit et retransmission. Rate limiting par destinataire (max 1/day marketing, pas de limite transactionnel). Les templates sont versionnées pour permettre évolutions sans déploiement code.
Templates d'Email Principaux
TEMPLATE 1: billing_invoice_monthly
Sujet: "Facture RGZ — {{ month }} {{ year }}"
Destinataire: reseller.contact_email
Variables: month, year, amount_fcfa, link_portal, invoice_number
Pièce jointe: invoice.pdf (généré par #24)
Contenu:
"Bonjour {{ reseller_name }},
Votre facture du mois de {{ month }} {{ year }} est prête.
Montant: {{ amount_fcfa }} FCFA
Numéro de facture: {{ invoice_number }}
Date: {{ invoice_date }}
Télécharger facture: [LIEN PORTAL]
ou en pièce jointe ci-dessous.
Merci pour votre confiance!
Cordialement,
RGZ NOC Team"
SLA: < 1min (auto-sent daily 00:15 by Celery task #70)
Retry: 3 tentatives SMTP
Audit: Enregistrer email_sent event + timestamp
TEMPLATE 2: incident_alert_critical
Sujet: "⚠️ ALERTE CRITIQUE RGZ — {{ incident_number }}"
Destinataire: reseller contact emails + noc@rgz.bj
Variables: incident_number, title, description, affected_sites, detected_at, status_link
Contenu:
"ALERTE CRITIQUE
Incident: {{ incident_number }}
Titre: {{ title }}
Sévérité: {{ priority }}
Sites affectés: {{ affected_sites }}
Détecté: {{ detected_at }}
Descriptif:
{{ description }}
Status en temps réel: {{ status_link }}
Escalade automatique activée.
Merci de votre patience."
SLA: < 30s (urgent, envoi immédiat #58)
Retry: 5 tentatives
Audit: Incident alert email log
TEMPLATE 3: onboarding_welcome
Sujet: "Bienvenue sur RGZ Access!"
Destinataire: reseller contact_email
Variables: reseller_name, validation_link, estimated_duration
Contenu:
"Bienvenue {{ reseller_name }}!
Votre candidature RGZ a été reçue avec succès.
Processus onboarding (7-12 jours):
1. Validation documents (1-2j)
2. Création compte + VLAN (1j)
3. Livraison CPE (3-5j)
4. Installation terrain (2-3j)
5. Tests iperf3 (1-2j)
6. Go-live (1j)
Suivi: {{ validation_link }}/dashboard
Support: support@rgz.bj | +229-XXXXX"
SLA: Immédiat (#56 étape 0)
Retry: 2
Audit: Onboarding welcome email
TEMPLATE 4: onboarding_approved
Sujet: "✓ Documents approuvés — RGZ Access"
Destinataire: reseller
Variables: reseller_name, vlan_number, nas_id, next_steps
Contenu:
"Excellent {{ reseller_name }}!
Vos documents ont été validés. ✓
Informations techniques assignées:
- VLAN: {{ vlan_number }}
- NAS-ID: {{ nas_id }}
- Subnet: 10.{{ vlan_number }}.0.0/24
Prochaines étapes:
{{ next_steps }}
Dashboard: [LIEN]
Merci!"
SLA: < 1min (#56 étape 1→2)
Retry: 2
Audit: Onboarding approval email
TEMPLATE 5: cutover_reminder
Sujet: "Rappel — Checklist cutover {{ reseller_name }}"
Destinataire: reseller
Variables: reseller_name, items_done, items_total, link_portal
Contenu:
"Bonjour {{ reseller_name }},
Votre checklist cutover est à {{ items_done }}/{{ items_total }} éléments.
Pour passer en production, tous les 40 points doivent être validés.
Avancée: [BARRE PROGRESS]
Valider maintenant: {{ link_portal }}/cutover
Support: support@rgz.bj"
SLA: Si délai > 7j sans progression
Retry: 1
Audit: Cutover reminder email
TEMPLATE 6: rma_confirmation
Sujet: "Ticket RMA {{ rma_number }} — {{ serial_number }}"
Destinataire: reseller
Variables: rma_number, serial_number, status, link_tracking
Contenu:
"Ticket RMA créé avec succès.
Numéro: {{ rma_number }}
Produit: {{ serial_number }}
Statut: {{ status }}
Suivi: {{ link_tracking }}
Merci!"
SLA: Immédiat (#57 création)
Retry: 2
Audit: RMA confirmation email
TEMPLATE 7: sla_credit_notification
Sujet: "Crédit SLA appliqué — {{ amount_fcfa }} FCFA"
Destinataire: subscriber.email (si fourni)
Variables: amount_fcfa, downtime_minutes, sla_percent, invoice_number
Contenu:
"Crédit SLA appliqué ✓
Montant: {{ amount_fcfa }} FCFA
Raison: Downtime {{ downtime_minutes }}min
Disponibilité: {{ sla_percent }}%
Facturation: Appliqué à votre solde
Merci pour votre patience!"
SLA: < 5min (#25 credit-sla-auto)
Retry: 2
Audit: SLA credit email
TEMPLATE 8: support_ticket_created
Sujet: "Ticket support #{{ ticket_number }} créé"
Destinataire: user email
Variables: ticket_number, title, priority, support_link
Contenu:
"Votre ticket support a été créé.
Numéro: #{{ ticket_number }}
Titre: {{ title }}
Sévérité: {{ priority }}
Suivi: {{ support_link }}
Nous vous répondrons rapidement.
Merci!"
SLA: Immédiat
Retry: 2
Audit: Support ticket emailModèles de Données
python
class EmailTemplate(BaseModel):
id: UUID
key: str UNIQUE
subject: str (Jinja2)
html_content: str (Jinja2)
text_content: str (plain text fallback)
required_variables: list[str]
attachments_allowed: bool
category: str (billing|incident|onboarding|rma|billing|support)
version: int
is_active: bool
class EmailMessage(BaseModel):
id: UUID
template_key: str
recipient_email: str
subject_rendered: str
html_rendered: str
attachments: list[{filename, content_type, data_base64}]
status: str (queued|sent|delivered|bounced|complaint)
sent_at: datetime | None
delivered_at: datetime | None
error_message: str | None
# Table DB
email_templates:
- id UUID PK
- key VARCHAR(100) UNIQUE
- subject TEXT
- html_content TEXT
- text_content TEXT
- required_variables TEXT[]
- attachments_allowed BOOLEAN DEFAULT false
- category VARCHAR(50)
- version INT DEFAULT 1
- is_active BOOLEAN DEFAULT true
- created_by UUID FK
- created_at TIMESTAMP
- updated_by UUID FK
- updated_at TIMESTAMP
email_messages:
- id UUID PK
- template_key VARCHAR(100)
- recipient_email VARCHAR(255)
- subject TEXT
- html_content TEXT
- attachments_count INT DEFAULT 0
- status VARCHAR(20) CHECK(queued|sent|delivered|bounced|complaint)
- sent_at TIMESTAMP NULL
- delivered_at TIMESTAMP NULL
- bounce_type VARCHAR(20) NULL (permanent|temporary)
- bounce_subtype VARCHAR(50) NULL
- error_message TEXT NULL
- source_endpoint VARCHAR(100) (billing|incident|onboarding|rma|support)
- created_at TIMESTAMP
email_attachments:
- id UUID PK
- message_id UUID FK
- filename VARCHAR(255)
- content_type VARCHAR(100)
- size_bytes INT
- data BYTEA (ou S3 reference)
- created_at TIMESTAMPConfiguration
env
# .env.example
SMTP_HOST=mail.rgz.bj
SMTP_PORT=587
SMTP_USER=noreply@rgz.bj
SMTP_PASSWORD=your_smtp_password
SMTP_FROM_NAME=RGZ Notifications
SMTP_FROM_EMAIL=noreply@rgz.bj
SMTP_REPLY_TO=support@rgz.bj
SMTP_USE_TLS=true
SMTP_TIMEOUT_SECONDS=30
SMTP_RETRY_MAX_ATTEMPTS=3
EMAIL_RATE_LIMIT_TRANSACTIONAL=unlimited
EMAIL_RATE_LIMIT_MARKETING=1/day
EMAIL_AUDIT_RETENTION_DAYS=365
EMAIL_AUTO_ANONYMIZE_AFTER_DAYS=90
# Attachment settings
EMAIL_MAX_ATTACHMENT_SIZE_MB=10
EMAIL_ALLOWED_ATTACHMENT_TYPES=pdf,png,jpg,json,csv
# Templates directory
EMAIL_TEMPLATES_DIR=/app/templates/emailsEndpoints API
| Méthode | Route | Requête | Réponse | Notes |
|---|---|---|---|---|
| POST | /api/v1/email/send | {to, template_key, variables | 201 CREATED | |
| GET | /api/v1/email/templates | — | {items:[{key, subject, category, version}], total} | 200 OK |
| GET | /api/v1/email/templates/ | — | 200 OK | |
| POST | /api/v1/email/templates/{key}/test | {to, variables | 200 OK | |
| PUT | /api/v1/email/templates/ | 200 OK (admin) | ||
| POST | /api/v1/email/webhook | 200 OK (SES callback) | ||
| GET | /api/v1/email/messages | ?to={email}&limit=50 | {items:[{id, to, subject, status, sent_at}], total} | 200 OK |
Commandes Utiles
bash
# Envoyer facture email (Celery task)
curl -X POST http://localhost:8000/api/v1/email/send \
-H "Content-Type: application/json" \
-d '{
"to": "reseller@techconnect.bj",
"template_key": "billing_invoice_monthly",
"variables": {
"reseller_name": "Tech Connect SA",
"month": "février",
"year": "2026",
"amount_fcfa": "450000",
"link_portal": "https://admin-rgz.duckdns.org/invoices/latest",
"invoice_number": "INV-2026-0001"
},
"attachments": [
{
"filename": "invoice_2026-02.pdf",
"content_type": "application/pdf",
"data_base64": "JVBERi0xLjQKJeLjz9MNCi..."
}
]
}'
# Envoyer alerte incident critique
curl -X POST http://localhost:8000/api/v1/email/send \
-d '{
"to": "noc@rgz.bj,reseller@techconnect.bj",
"template_key": "incident_alert_critical",
"variables": {
"incident_number": "INC-2026-000847",
"title": "Fiber cut central",
"description": "Central BGP flap, 45 sites offline",
"priority": "P0",
"affected_sites": 45,
"detected_at": "2026-02-21T14:30:00Z",
"status_link": "https://status.rgz.bj"
}
}'
# Envoyer email onboarding welcome
curl -X POST http://localhost:8000/api/v1/email/send \
-d '{
"to": "john@techconnect.bj",
"template_key": "onboarding_welcome",
"variables": {
"reseller_name": "Tech Connect SA",
"validation_link": "https://admin-rgz.duckdns.org",
"estimated_duration": "7-12 jours"
}
}'
# Lister templates disponibles
curl http://localhost:8000/api/v1/email/templates
# Tester template rendering
curl -X POST http://localhost:8000/api/v1/email/templates/billing_invoice_monthly/test \
-H "Authorization: Bearer ADMIN_TOKEN" \
-d '{
"to": "test@rgz.bj",
"variables": {
"reseller_name": "Test Company",
"month": "février",
"year": "2026",
"amount_fcfa": "450000",
"link_portal": "https://admin-rgz.duckdns.org",
"invoice_number": "TEST-001"
}
}'
# Consulter historique emails envoyés
curl http://localhost:8000/api/v1/email/messages?to=reseller@techconnect.bj \
-H "Authorization: Bearer ADMIN_TOKEN" | jq '.items'Intégration Avec Autres Outils
- #24 factures-pdf: Génère PDF pièce jointe pour billing_invoice_monthly
- #70 rapport-sla-mensuel: Appelle #63 pour envoyer rapports mensuels
- #56 onboarding-reseller: Appelle #63 à chaque étape (welcome, approval, go-live)
- #57 rma-ticket-system: Appelle #63 pour confirmations RMA
- #58 incident-escalation: Appelle #63 pour alertes P0 critiques
- #25 credit-sla-auto: Appelle #63 pour notifications crédit
- #63: Lui-même appelé par plusieurs services
Implémentation TODO
- [ ] CRUD templates (8+ par défaut)
- [ ] Jinja2 rendering (subject + HTML)
- [ ] SMTP client (Python smtplib)
- [ ] Retry logic (3 tentatives avec backoff)
- [ ] Attachment support (PDF, PNG, JSON, CSV)
- [ ] Rate limiting (transactionnel = unlimited, marketing = 1/day)
- [ ] Audit trail (email_messages + email_attachments)
- [ ] Bounce/complaint webhook handler (Amazon SES)
- [ ] Admin template editor + test renderer
- [ ] Tests: send email → delivery callback → audit trail
- [ ] Dashboard admin: email audit logs by recipient
Dernière mise à jour: 2026-02-21