41bbdb4bef
Add pluggable email provider system supporting SMTP, Mailgun, and SendGrid with factory pattern for runtime provider selection. Includes branded HTML email templates for verification, password reset, MFA notifications, and organization invites. Also rebrands all email content from Gatehouse to Secuird, adds email provider configuration options, and fixes duplicate log handlers in development mode.
86 lines
2.7 KiB
Python
86 lines
2.7 KiB
Python
"""Email provider interfaces and factory."""
|
|
import logging
|
|
import os
|
|
from abc import ABC, abstractmethod
|
|
from dataclasses import dataclass
|
|
from typing import Optional
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@dataclass
|
|
class EmailMessage:
|
|
"""Email message data structure."""
|
|
to: str
|
|
subject: str
|
|
body: str
|
|
html_body: Optional[str] = None
|
|
from_address: Optional[str] = None
|
|
|
|
|
|
class EmailProvider(ABC):
|
|
"""Abstract base class for email providers."""
|
|
|
|
@abstractmethod
|
|
def send(self, message: EmailMessage) -> bool:
|
|
"""
|
|
Send an email message.
|
|
|
|
Args:
|
|
message: EmailMessage instance containing email details
|
|
|
|
Returns:
|
|
bool: True if email was sent successfully, False otherwise
|
|
"""
|
|
pass
|
|
|
|
|
|
class NoOpEmailProvider(EmailProvider):
|
|
"""No-op email provider that logs and returns False."""
|
|
|
|
def send(self, message: EmailMessage) -> bool:
|
|
"""Log that emails are disabled and return False."""
|
|
logger.info(f"Email disabled - would send to={message.to} subject={message.subject}")
|
|
return False
|
|
|
|
|
|
class EmailProviderFactory:
|
|
"""Factory for creating email provider instances."""
|
|
|
|
@staticmethod
|
|
def get_provider() -> EmailProvider:
|
|
"""
|
|
Create an email provider based on EMAIL_PROVIDER config.
|
|
|
|
Returns:
|
|
EmailProvider: An instance of the appropriate email provider
|
|
"""
|
|
provider_name = os.getenv("EMAIL_PROVIDER", "smtp").lower()
|
|
|
|
if provider_name == "smtp":
|
|
try:
|
|
from gatehouse_app.services.providers.smtp_provider import SmtpEmailProvider
|
|
return SmtpEmailProvider()
|
|
except ImportError:
|
|
logger.warning("SMTP provider not implemented, using no-op provider")
|
|
return NoOpEmailProvider()
|
|
|
|
if provider_name == "mailgun":
|
|
try:
|
|
from gatehouse_app.services.providers.mailgun_provider import MailgunEmailProvider
|
|
return MailgunEmailProvider()
|
|
except ImportError:
|
|
logger.warning("Mailgun provider not implemented, using no-op provider")
|
|
return NoOpEmailProvider()
|
|
|
|
if provider_name == "sendgrid":
|
|
try:
|
|
from gatehouse_app.services.providers.sendgrid_provider import SendGridEmailProvider
|
|
return SendGridEmailProvider()
|
|
except ImportError:
|
|
logger.warning("SendGrid provider not implemented, using no-op provider")
|
|
return NoOpEmailProvider()
|
|
|
|
logger.error(f"Invalid EMAIL_PROVIDER value: {provider_name}, defaulting to no-op provider")
|
|
return NoOpEmailProvider()
|