Sage100-vps/services/email_service.py
2026-01-02 17:56:28 +03:00

306 lines
17 KiB
Python

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from typing import Optional, List
import logging
from config.config import settings
logger = logging.getLogger(__name__)
class AuthEmailService:
@staticmethod
def _send_email(
to: str,
subject: str,
html_body: str,
cc: Optional[List[str]] = None,
bcc: Optional[List[str]] = None,
) -> bool:
try:
msg = MIMEMultipart("alternative")
msg["From"] = settings.smtp_from
msg["To"] = to
msg["Subject"] = subject
if cc:
msg["Cc"] = ", ".join(cc)
msg.attach(MIMEText(html_body, "html", "utf-8"))
recipients = [to]
if cc:
recipients.extend(cc)
if bcc:
recipients.extend(bcc)
with smtplib.SMTP(
settings.smtp_host, settings.smtp_port, timeout=30
) as server:
if settings.smtp_use_tls:
server.starttls()
if settings.smtp_user and settings.smtp_password:
server.login(settings.smtp_user, settings.smtp_password)
server.sendmail(settings.smtp_from, recipients, msg.as_string())
logger.info(f"Email envoye: {subject} vers {to}")
return True
except smtplib.SMTPException as e:
logger.error(f"Erreur SMTP envoi email: {e}")
return False
except Exception as e:
logger.error(f"Erreur envoi email: {e}")
return False
@classmethod
def send_verification_email(cls, email: str, token: str, base_url: str) -> bool:
verification_link = f"{base_url}/auth/verify-email?token={token}"
html_body = f"""
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Verification de votre email</title>
</head>
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; background-color: #f5f5f5;">
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="background-color: #f5f5f5; padding: 40px 20px;">
<tr>
<td align="center">
<table role="presentation" width="600" cellspacing="0" cellpadding="0" style="background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<tr>
<td style="background-color: #4F46E5; padding: 32px; text-align: center; border-radius: 8px 8px 0 0;">
<h1 style="color: #ffffff; margin: 0; font-size: 24px; font-weight: 600;">Verification de votre email</h1>
</td>
</tr>
<tr>
<td style="padding: 40px 32px;">
<p style="color: #374151; font-size: 16px; line-height: 1.6; margin: 0 0 24px;">
Bienvenue sur Sage Dataven. Pour activer votre compte, veuillez verifier votre adresse email en cliquant sur le bouton ci-dessous.
</p>
<table role="presentation" width="100%" cellspacing="0" cellpadding="0">
<tr>
<td align="center" style="padding: 24px 0;">
<a href="{verification_link}" style="display: inline-block; background-color: #4F46E5; color: #ffffff; text-decoration: none; padding: 14px 32px; border-radius: 6px; font-size: 16px; font-weight: 500;">Verifier mon email</a>
</td>
</tr>
</table>
<p style="color: #6B7280; font-size: 14px; line-height: 1.6; margin: 24px 0 0;">
Si le bouton ne fonctionne pas, copiez ce lien dans votre navigateur :
</p>
<p style="color: #4F46E5; font-size: 14px; word-break: break-all; background-color: #F3F4F6; padding: 12px; border-radius: 4px; margin: 12px 0 24px;">
{verification_link}
</p>
<p style="color: #EF4444; font-size: 14px; margin: 0;">
Ce lien expire dans 24 heures.
</p>
</td>
</tr>
<tr>
<td style="background-color: #F9FAFB; padding: 24px 32px; border-radius: 0 0 8px 8px; border-top: 1px solid #E5E7EB;">
<p style="color: #9CA3AF; font-size: 12px; margin: 0; text-align: center;">
Si vous n'avez pas cree de compte, ignorez cet email.
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
"""
return cls._send_email(
email, "Verifiez votre adresse email - Sage Dataven", html_body
)
@classmethod
def send_password_reset_email(
cls, email: str, token: str, frontend_url: str
) -> bool:
reset_link = f"{frontend_url}/reset-password?token={token}"
html_body = f"""
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Reinitialisation de mot de passe</title>
</head>
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; background-color: #f5f5f5;">
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="background-color: #f5f5f5; padding: 40px 20px;">
<tr>
<td align="center">
<table role="presentation" width="600" cellspacing="0" cellpadding="0" style="background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<tr>
<td style="background-color: #DC2626; padding: 32px; text-align: center; border-radius: 8px 8px 0 0;">
<h1 style="color: #ffffff; margin: 0; font-size: 24px; font-weight: 600;">Reinitialisation du mot de passe</h1>
</td>
</tr>
<tr>
<td style="padding: 40px 32px;">
<p style="color: #374151; font-size: 16px; line-height: 1.6; margin: 0 0 24px;">
Vous avez demande la reinitialisation de votre mot de passe. Cliquez sur le bouton ci-dessous pour creer un nouveau mot de passe.
</p>
<table role="presentation" width="100%" cellspacing="0" cellpadding="0">
<tr>
<td align="center" style="padding: 24px 0;">
<a href="{reset_link}" style="display: inline-block; background-color: #DC2626; color: #ffffff; text-decoration: none; padding: 14px 32px; border-radius: 6px; font-size: 16px; font-weight: 500;">Reinitialiser mon mot de passe</a>
</td>
</tr>
</table>
<p style="color: #6B7280; font-size: 14px; line-height: 1.6; margin: 24px 0 0;">
Si le bouton ne fonctionne pas, copiez ce lien :
</p>
<p style="color: #DC2626; font-size: 14px; word-break: break-all; background-color: #FEF2F2; padding: 12px; border-radius: 4px; margin: 12px 0 24px;">
{reset_link}
</p>
<p style="color: #EF4444; font-size: 14px; margin: 0;">
Ce lien expire dans 1 heure.
</p>
</td>
</tr>
<tr>
<td style="background-color: #FEF2F2; padding: 24px 32px; border-radius: 0 0 8px 8px; border-top: 1px solid #FECACA;">
<p style="color: #991B1B; font-size: 12px; margin: 0; text-align: center;">
Si vous n'avez pas demande cette reinitialisation, ignorez cet email. Votre mot de passe restera inchange.
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
"""
return cls._send_email(
email, "Reinitialisation de votre mot de passe - Sage Dataven", html_body
)
@classmethod
def send_password_changed_notification(cls, email: str) -> bool:
html_body = """
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mot de passe modifie</title>
</head>
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; background-color: #f5f5f5;">
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="background-color: #f5f5f5; padding: 40px 20px;">
<tr>
<td align="center">
<table role="presentation" width="600" cellspacing="0" cellpadding="0" style="background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<tr>
<td style="background-color: #059669; padding: 32px; text-align: center; border-radius: 8px 8px 0 0;">
<h1 style="color: #ffffff; margin: 0; font-size: 24px; font-weight: 600;">Mot de passe modifie</h1>
</td>
</tr>
<tr>
<td style="padding: 40px 32px;">
<p style="color: #374151; font-size: 16px; line-height: 1.6; margin: 0 0 24px;">
Votre mot de passe a ete modifie avec succes.
</p>
<p style="color: #374151; font-size: 16px; line-height: 1.6; margin: 0 0 24px;">
Si vous n'etes pas a l'origine de ce changement, contactez immediatement notre support.
</p>
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="background-color: #FEF3C7; border-left: 4px solid #F59E0B; border-radius: 4px;">
<tr>
<td style="padding: 16px;">
<p style="color: #92400E; font-size: 14px; margin: 0;">
<strong>Securite :</strong> Toutes vos sessions actives ont ete deconnectees. Vous devrez vous reconnecter sur tous vos appareils.
</p>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td style="background-color: #F9FAFB; padding: 24px 32px; border-radius: 0 0 8px 8px; border-top: 1px solid #E5E7EB;">
<p style="color: #9CA3AF; font-size: 12px; margin: 0; text-align: center;">
Sage Dataven - Notification de securite
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
"""
return cls._send_email(
email, "Votre mot de passe a ete modifie - Sage Dataven", html_body
)
@classmethod
def send_security_alert(
cls, email: str, alert_type: str, details: str, ip_address: Optional[str] = None
) -> bool:
ip_info = (
f"<p style='color: #6B7280; font-size: 14px;'>Adresse IP : {ip_address}</p>"
if ip_address
else ""
)
html_body = f"""
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Alerte de securite</title>
</head>
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; background-color: #f5f5f5;">
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="background-color: #f5f5f5; padding: 40px 20px;">
<tr>
<td align="center">
<table role="presentation" width="600" cellspacing="0" cellpadding="0" style="background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<tr>
<td style="background-color: #B91C1C; padding: 32px; text-align: center; border-radius: 8px 8px 0 0;">
<h1 style="color: #ffffff; margin: 0; font-size: 24px; font-weight: 600;">Alerte de securite</h1>
</td>
</tr>
<tr>
<td style="padding: 40px 32px;">
<p style="color: #374151; font-size: 16px; line-height: 1.6; margin: 0 0 16px;">
<strong>{alert_type}</strong>
</p>
<p style="color: #374151; font-size: 16px; line-height: 1.6; margin: 0 0 24px;">
{details}
</p>
{ip_info}
<p style="color: #6B7280; font-size: 14px; margin: 24px 0 0;">
Si vous reconnaissez cette activite, vous pouvez ignorer ce message. Sinon, nous vous recommandons de changer votre mot de passe immediatement.
</p>
</td>
</tr>
<tr>
<td style="background-color: #FEF2F2; padding: 24px 32px; border-radius: 0 0 8px 8px; border-top: 1px solid #FECACA;">
<p style="color: #991B1B; font-size: 12px; margin: 0; text-align: center;">
Sage Dataven - Alerte de securite automatique
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
"""
return cls._send_email(
email, f"Alerte de securite : {alert_type} - Sage Dataven", html_body
)