""" Custom email backend that uses SiteSettings for configuration. """ from django.core.mail.backends.smtp import EmailBackend as SMTPEmailBackend from django.core.mail.backends.console import EmailBackend as ConsoleEmailBackend from django.core.mail.backends.base import BaseEmailBackend from django.conf import settings from .models import SiteSettings class SiteSettingsEmailBackend(BaseEmailBackend): """ Email backend that dynamically loads settings from SiteSettings model. Falls back to Django settings if SiteSettings are not configured. """ def __init__(self, fail_silently=False, **kwargs): super().__init__(fail_silently=fail_silently) self._backend = None self._backend_instance = None self._load_backend() def _load_backend(self): """Load the appropriate email backend based on SiteSettings.""" import logging logger = logging.getLogger(__name__) try: site_settings = SiteSettings.get_settings() backend_class = site_settings.email_backend logger.info(f"Loading email backend: {backend_class}") # If using SMTP, configure it with SiteSettings if backend_class == 'django.core.mail.backends.smtp.EmailBackend': # Get decrypted password email_password = site_settings.get_email_password() if hasattr(site_settings, 'get_email_password') else site_settings.email_host_password # Check if SMTP is properly configured email_host = site_settings.email_host or getattr(settings, 'EMAIL_HOST', '') # If no host is configured, fall back to console backend if not email_host: logger.warning("Email host not configured, using console backend") self._backend = ConsoleEmailBackend(fail_silently=self.fail_silently) else: # Ensure TLS and SSL are mutually exclusive use_tls = site_settings.email_use_tls use_ssl = site_settings.email_use_ssl # If both are True, prioritize TLS (common case) if use_tls and use_ssl: use_ssl = False logger.warning("Both TLS and SSL were enabled. Disabling SSL and using TLS only.") logger.info(f"Configuring SMTP: host={email_host}, port={site_settings.email_port}, user={site_settings.email_host_user}, tls={use_tls}, ssl={use_ssl}") self._backend = SMTPEmailBackend( host=email_host, port=site_settings.email_port or getattr(settings, 'EMAIL_PORT', 587), username=site_settings.email_host_user or getattr(settings, 'EMAIL_HOST_USER', ''), password=email_password or getattr(settings, 'EMAIL_HOST_PASSWORD', ''), use_tls=use_tls, use_ssl=use_ssl, timeout=site_settings.email_timeout or getattr(settings, 'EMAIL_TIMEOUT', 10), fail_silently=self.fail_silently, ) logger.info("SMTP backend configured successfully") elif backend_class == 'django.core.mail.backends.console.EmailBackend': logger.info("Using console email backend") self._backend = ConsoleEmailBackend(fail_silently=self.fail_silently) else: # For other backends, try to import and instantiate from django.utils.module_loading import import_string backend_class_obj = import_string(backend_class) self._backend = backend_class_obj(fail_silently=self.fail_silently) logger.info(f"Loaded custom backend: {backend_class}") except Exception as e: # Fallback to console backend if there's an error logger.exception(f"Error loading email backend from SiteSettings: {e}. Using console backend.") self._backend = ConsoleEmailBackend(fail_silently=self.fail_silently) def open(self): """Open a network connection.""" if self._backend: return self._backend.open() return False def close(self): """Close the network connection.""" if self._backend: return self._backend.close() def send_messages(self, email_messages): """Send one or more EmailMessage objects and return the number sent.""" import logging logger = logging.getLogger(__name__) # Reload backend before sending to get latest settings # This ensures settings changes take effect immediately self._load_backend() if self._backend: try: # Log email details for debugging for msg in email_messages: logger.info(f"Sending email: To={msg.to}, Subject={msg.subject}, From={msg.from_email}") result = self._backend.send_messages(email_messages) logger.info(f"Successfully sent {result} email message(s)") return result except Exception as e: error_msg = str(e) logger.exception(f"Error sending email messages: {error_msg}") # Log more details about the error if hasattr(self._backend, 'host'): logger.error(f"SMTP Host: {self._backend.host}, Port: {getattr(self._backend, 'port', 'N/A')}") if not self.fail_silently: raise return 0 logger.warning("No email backend available, cannot send messages") return 0