214 lines
8.4 KiB
Python
214 lines
8.4 KiB
Python
"""
|
|
Service d'envoi d'emails pour Notytex.
|
|
Gère la configuration SMTP et l'envoi de bilans d'évaluation.
|
|
"""
|
|
|
|
import smtplib
|
|
import logging
|
|
from email.mime.text import MIMEText
|
|
from email.mime.multipart import MIMEMultipart
|
|
from typing import List, Optional, Dict, Any
|
|
from flask import current_app
|
|
from premailer import transform
|
|
|
|
|
|
class EmailService:
|
|
"""Service d'envoi d'emails avec configuration dynamique."""
|
|
|
|
def __init__(self, config_manager=None):
|
|
"""Initialise le service avec le gestionnaire de configuration."""
|
|
if config_manager is None:
|
|
from app_config import config_manager as default_manager
|
|
self.config_manager = default_manager
|
|
else:
|
|
self.config_manager = config_manager
|
|
|
|
self.logger = logging.getLogger(__name__)
|
|
|
|
def get_smtp_config(self) -> Dict[str, Any]:
|
|
"""Récupère la configuration SMTP depuis la base de données."""
|
|
try:
|
|
return {
|
|
'host': self.config_manager.get('email.smtp_host', ''),
|
|
'port': int(self.config_manager.get('email.smtp_port', 587)),
|
|
'username': self.config_manager.get('email.username', ''),
|
|
'password': self.config_manager.get('email.password', ''),
|
|
'use_tls': self.config_manager.get('email.use_tls', 'true').lower() == 'true',
|
|
'from_name': self.config_manager.get('email.from_name', 'Notytex'),
|
|
'from_address': self.config_manager.get('email.from_address', ''),
|
|
}
|
|
except Exception as e:
|
|
self.logger.error(f"Erreur lors de la récupération de la configuration email: {e}")
|
|
return {}
|
|
|
|
def is_configured(self) -> bool:
|
|
"""Vérifie si la configuration email est complète."""
|
|
config = self.get_smtp_config()
|
|
|
|
# Vérifier les champs obligatoires de base
|
|
if not config.get('host'):
|
|
self.logger.warning("Configuration email incomplète: champ 'host' manquant")
|
|
return False
|
|
|
|
if not config.get('from_address'):
|
|
self.logger.warning("Configuration email incomplète: champ 'from_address' manquant")
|
|
return False
|
|
|
|
# Pour les serveurs locaux de test (localhost), l'authentification n'est pas requise
|
|
is_localhost = config.get('host', '').lower() in ['localhost', '127.0.0.1']
|
|
is_test_port = str(config.get('port', '')).strip() in ['1025', '2525', '8025']
|
|
|
|
if not is_localhost or not is_test_port:
|
|
# Pour les vrais serveurs SMTP, username et password sont requis
|
|
if not config.get('username'):
|
|
self.logger.warning("Configuration email incomplète: champ 'username' manquant")
|
|
return False
|
|
if not config.get('password'):
|
|
self.logger.warning("Configuration email incomplète: champ 'password' manquant")
|
|
return False
|
|
|
|
return True
|
|
|
|
def send_email(self, to_emails: List[str], subject: str, html_body: str,
|
|
text_body: Optional[str] = None) -> Dict[str, Any]:
|
|
"""
|
|
Envoie un email à une liste de destinataires.
|
|
|
|
Args:
|
|
to_emails: Liste des adresses email destinataires
|
|
subject: Sujet de l'email
|
|
html_body: Corps de l'email en HTML
|
|
text_body: Corps de l'email en texte brut (optionnel)
|
|
|
|
Returns:
|
|
Dict avec le statut de l'envoi et les détails
|
|
"""
|
|
if not self.is_configured():
|
|
return {
|
|
'success': False,
|
|
'error': 'Configuration email incomplète. Vérifiez les paramètres dans Configuration > Email.'
|
|
}
|
|
|
|
config = self.get_smtp_config()
|
|
|
|
try:
|
|
# Préparer l'email
|
|
msg = MIMEMultipart('alternative')
|
|
msg['Subject'] = subject
|
|
msg['From'] = f"{config['from_name']} <{config['from_address']}>"
|
|
msg['To'] = ', '.join(to_emails)
|
|
|
|
# Ajouter le corps en texte brut si fourni
|
|
if text_body:
|
|
part1 = MIMEText(text_body, 'plain', 'utf-8')
|
|
msg.attach(part1)
|
|
|
|
# Transformer le HTML pour optimiser l'affichage email
|
|
optimized_html = transform(html_body)
|
|
part2 = MIMEText(optimized_html, 'html', 'utf-8')
|
|
msg.attach(part2)
|
|
|
|
# Connexion SMTP et envoi
|
|
with smtplib.SMTP(config['host'], config['port']) as server:
|
|
if config['use_tls']:
|
|
server.starttls()
|
|
|
|
# Ne pas s'authentifier sur les serveurs de test locaux
|
|
is_localhost = config.get('host', '').lower() in ['localhost', '127.0.0.1']
|
|
is_test_port = str(config.get('port', '')).strip() in ['1025', '2525', '8025']
|
|
|
|
if not (is_localhost and is_test_port) and config.get('username') and config.get('password'):
|
|
server.login(config['username'], config['password'])
|
|
|
|
server.send_message(msg)
|
|
|
|
self.logger.info(f"Email envoyé avec succès à {len(to_emails)} destinataires: {subject}")
|
|
|
|
return {
|
|
'success': True,
|
|
'message': f'Email envoyé avec succès à {len(to_emails)} destinataire(s)',
|
|
'recipients_count': len(to_emails)
|
|
}
|
|
|
|
except smtplib.SMTPAuthenticationError as e:
|
|
error_msg = "Erreur d'authentification SMTP. Vérifiez les identifiants."
|
|
self.logger.error(f"Erreur SMTP Auth: {e}")
|
|
return {'success': False, 'error': error_msg}
|
|
|
|
except smtplib.SMTPException as e:
|
|
error_msg = f"Erreur SMTP lors de l'envoi: {str(e)}"
|
|
self.logger.error(f"Erreur SMTP: {e}")
|
|
return {'success': False, 'error': error_msg}
|
|
|
|
except Exception as e:
|
|
error_msg = f"Erreur inattendue lors de l'envoi: {str(e)}"
|
|
self.logger.error(f"Erreur envoi email: {e}")
|
|
return {'success': False, 'error': error_msg}
|
|
|
|
def send_test_email(self, to_email: str) -> Dict[str, Any]:
|
|
"""
|
|
Envoie un email de test pour vérifier la configuration.
|
|
|
|
Args:
|
|
to_email: Adresse email de test
|
|
|
|
Returns:
|
|
Dict avec le statut du test
|
|
"""
|
|
subject = "Test de configuration email - Notytex"
|
|
html_body = """
|
|
<html>
|
|
<body style="font-family: Arial, sans-serif; padding: 20px;">
|
|
<h2 style="color: #3b82f6;">Test de configuration email</h2>
|
|
<p>Félicitations ! Votre configuration email fonctionne correctement.</p>
|
|
<p>Vous pouvez maintenant envoyer des bilans d'évaluation par email.</p>
|
|
<hr style="margin: 20px 0;">
|
|
<p style="color: #6b7280; font-size: 12px;">
|
|
Email envoyé depuis Notytex - Système de gestion scolaire
|
|
</p>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
text_body = """
|
|
Test de configuration email - Notytex
|
|
|
|
Félicitations ! Votre configuration email fonctionne correctement.
|
|
Vous pouvez maintenant envoyer des bilans d'évaluation par email.
|
|
|
|
---
|
|
Email envoyé depuis Notytex - Système de gestion scolaire
|
|
"""
|
|
|
|
return self.send_email([to_email], subject, html_body, text_body)
|
|
|
|
def validate_email_addresses(self, emails: List[str]) -> Dict[str, Any]:
|
|
"""
|
|
Valide une liste d'adresses email.
|
|
|
|
Args:
|
|
emails: Liste des adresses à valider
|
|
|
|
Returns:
|
|
Dict avec les emails valides et invalides
|
|
"""
|
|
import re
|
|
|
|
email_regex = re.compile(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')
|
|
|
|
valid_emails = []
|
|
invalid_emails = []
|
|
|
|
for email in emails:
|
|
email = email.strip()
|
|
if email and email_regex.match(email):
|
|
valid_emails.append(email)
|
|
elif email: # Email non vide mais invalide
|
|
invalid_emails.append(email)
|
|
|
|
return {
|
|
'valid': valid_emails,
|
|
'invalid': invalid_emails,
|
|
'valid_count': len(valid_emails),
|
|
'invalid_count': len(invalid_emails)
|
|
} |