SMTP and other email realted settings
This commit is contained in:
@@ -2,15 +2,22 @@
|
||||
Email Service - Multi-provider email sending
|
||||
|
||||
Uses Resend for transactional emails with fallback to Django's send_mail.
|
||||
Also supports direct SMTP configuration.
|
||||
Supports template rendering and multiple email types.
|
||||
|
||||
Configuration stored in IntegrationProvider model (provider_id='resend')
|
||||
and EmailSettings model for SMTP and provider selection.
|
||||
"""
|
||||
import hashlib
|
||||
import hmac
|
||||
import logging
|
||||
import re
|
||||
import smtplib
|
||||
import time
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.base import MIMEBase
|
||||
from email import encoders
|
||||
from typing import Optional, List, Dict, Any
|
||||
from urllib.parse import urlencode
|
||||
from django.core.mail import send_mail
|
||||
@@ -162,9 +169,10 @@ class EmailService:
|
||||
Unified email service supporting multiple providers.
|
||||
|
||||
Primary: Resend (for production transactional emails)
|
||||
Alternative: SMTP (configurable via EmailSettings)
|
||||
Fallback: Django's send_mail (uses EMAIL_BACKEND from settings)
|
||||
|
||||
Uses EmailSettings model for configuration (from_email, from_name, etc.)
|
||||
Uses EmailSettings model for configuration (from_email, from_name, provider selection, SMTP settings)
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
@@ -172,6 +180,8 @@ class EmailService:
|
||||
self._resend_config = {}
|
||||
self._brevo_configured = False
|
||||
self._brevo_config = {}
|
||||
self._smtp_configured = False
|
||||
self._smtp_config = {}
|
||||
self._email_settings = None
|
||||
self._setup_providers()
|
||||
|
||||
@@ -193,6 +203,22 @@ class EmailService:
|
||||
else:
|
||||
logger.info("Resend provider not configured in IntegrationProvider")
|
||||
|
||||
# Setup SMTP from EmailSettings
|
||||
if self._email_settings:
|
||||
smtp_host = self._email_settings.smtp_host
|
||||
if smtp_host:
|
||||
self._smtp_config = {
|
||||
'host': smtp_host,
|
||||
'port': self._email_settings.smtp_port,
|
||||
'username': self._email_settings.smtp_username,
|
||||
'password': self._email_settings.smtp_password,
|
||||
'use_tls': self._email_settings.smtp_use_tls,
|
||||
'use_ssl': self._email_settings.smtp_use_ssl,
|
||||
'timeout': self._email_settings.smtp_timeout,
|
||||
}
|
||||
self._smtp_configured = True
|
||||
logger.info(f"SMTP email provider initialized: {smtp_host}:{self._email_settings.smtp_port}")
|
||||
|
||||
# Setup Brevo (future - for marketing emails)
|
||||
brevo_provider = IntegrationProvider.get_provider('brevo')
|
||||
if brevo_provider and brevo_provider.api_key:
|
||||
@@ -200,6 +226,14 @@ class EmailService:
|
||||
self._brevo_configured = True
|
||||
logger.info("Brevo email provider initialized")
|
||||
|
||||
@property
|
||||
def email_provider(self) -> str:
|
||||
"""Get selected email provider from EmailSettings"""
|
||||
settings_obj = self._get_settings()
|
||||
if settings_obj and settings_obj.email_provider:
|
||||
return settings_obj.email_provider
|
||||
return 'resend' # Default to resend
|
||||
|
||||
def _get_settings(self):
|
||||
"""Get fresh email settings (refreshes on each call)"""
|
||||
if not self._email_settings:
|
||||
@@ -373,8 +407,32 @@ class EmailService:
|
||||
sender_email = from_email or self.from_email
|
||||
from_address = f"{sender_name} <{sender_email}>"
|
||||
|
||||
# Try Resend first
|
||||
if self._resend_configured:
|
||||
# Select email provider based on EmailSettings configuration
|
||||
selected_provider = self.email_provider
|
||||
|
||||
if selected_provider == 'smtp' and self._smtp_configured:
|
||||
return self._send_via_smtp(
|
||||
to=to,
|
||||
subject=subject,
|
||||
html=html,
|
||||
text=text,
|
||||
from_address=from_address,
|
||||
reply_to=reply_to or self.reply_to,
|
||||
attachments=attachments,
|
||||
)
|
||||
elif selected_provider == 'resend' and self._resend_configured:
|
||||
return self._send_via_resend(
|
||||
to=to,
|
||||
subject=subject,
|
||||
html=html,
|
||||
text=text,
|
||||
from_address=from_address,
|
||||
reply_to=reply_to or self.reply_to,
|
||||
attachments=attachments,
|
||||
tags=tags,
|
||||
)
|
||||
elif self._resend_configured:
|
||||
# Fallback to Resend if it's configured
|
||||
return self._send_via_resend(
|
||||
to=to,
|
||||
subject=subject,
|
||||
@@ -465,6 +523,101 @@ class EmailService:
|
||||
from_email=from_address.split('<')[-1].rstrip('>'),
|
||||
)
|
||||
|
||||
def _send_via_smtp(
|
||||
self,
|
||||
to: List[str],
|
||||
subject: str,
|
||||
html: Optional[str],
|
||||
text: Optional[str],
|
||||
from_address: str,
|
||||
reply_to: Optional[str],
|
||||
attachments: Optional[List[Dict]],
|
||||
) -> Dict[str, Any]:
|
||||
"""Send email via direct SMTP connection"""
|
||||
try:
|
||||
# Create message
|
||||
msg = MIMEMultipart('alternative')
|
||||
msg['Subject'] = subject
|
||||
msg['From'] = from_address
|
||||
msg['To'] = ', '.join(to)
|
||||
|
||||
if reply_to:
|
||||
msg['Reply-To'] = reply_to
|
||||
|
||||
# Add custom headers
|
||||
msg['X-Mailer'] = 'IGNY8 Email Service'
|
||||
|
||||
# Attach text version
|
||||
if text:
|
||||
part1 = MIMEText(text, 'plain', 'utf-8')
|
||||
msg.attach(part1)
|
||||
|
||||
# Attach HTML version
|
||||
if html:
|
||||
part2 = MIMEText(html, 'html', 'utf-8')
|
||||
msg.attach(part2)
|
||||
|
||||
# Handle attachments
|
||||
if attachments:
|
||||
for attachment in attachments:
|
||||
part = MIMEBase('application', 'octet-stream')
|
||||
content = attachment.get('content', b'')
|
||||
if isinstance(content, str):
|
||||
content = content.encode('utf-8')
|
||||
part.set_payload(content)
|
||||
encoders.encode_base64(part)
|
||||
filename = attachment.get('filename', 'attachment')
|
||||
part.add_header('Content-Disposition', f'attachment; filename="{filename}"')
|
||||
msg.attach(part)
|
||||
|
||||
# Get SMTP configuration
|
||||
smtp_host = self._smtp_config.get('host')
|
||||
smtp_port = self._smtp_config.get('port', 587)
|
||||
smtp_username = self._smtp_config.get('username')
|
||||
smtp_password = self._smtp_config.get('password')
|
||||
use_tls = self._smtp_config.get('use_tls', True)
|
||||
use_ssl = self._smtp_config.get('use_ssl', False)
|
||||
timeout = self._smtp_config.get('timeout', 30)
|
||||
|
||||
# Connect and send
|
||||
if use_ssl:
|
||||
server = smtplib.SMTP_SSL(smtp_host, smtp_port, timeout=timeout)
|
||||
else:
|
||||
server = smtplib.SMTP(smtp_host, smtp_port, timeout=timeout)
|
||||
if use_tls:
|
||||
server.starttls()
|
||||
|
||||
if smtp_username and smtp_password:
|
||||
server.login(smtp_username, smtp_password)
|
||||
|
||||
server.sendmail(
|
||||
from_address.split('<')[-1].rstrip('>'),
|
||||
to,
|
||||
msg.as_string()
|
||||
)
|
||||
server.quit()
|
||||
|
||||
logger.info(f"Email sent via SMTP: {subject} to {to}")
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'id': None, # SMTP doesn't provide message IDs like Resend
|
||||
'provider': 'smtp',
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to send email via SMTP: {str(e)}")
|
||||
|
||||
# Fallback to Django mail
|
||||
logger.info("Falling back to Django mail backend")
|
||||
return self._send_via_django(
|
||||
to=to,
|
||||
subject=subject,
|
||||
html=html,
|
||||
text=text,
|
||||
from_email=from_address.split('<')[-1].rstrip('>'),
|
||||
)
|
||||
|
||||
def _send_via_django(
|
||||
self,
|
||||
to: List[str],
|
||||
|
||||
Reference in New Issue
Block a user