FastAPI + Celery + Next.js + Postgres/Redis app with company monitoring, source collection, LLM-based change analysis, enrichment, and account security (Turnstile, escalating lockout, email verification).
54 lines
2.0 KiB
Python
54 lines
2.0 KiB
Python
"""SMTP email provider. Points at Mailpit in local dev (see docker-compose.yml)
|
|
and any real SMTP server in production - same code path either way. Uses
|
|
stdlib `smtplib` off the event loop via `asyncio.to_thread` rather than
|
|
adding an async SMTP dependency.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import smtplib
|
|
from email.mime.multipart import MIMEMultipart
|
|
from email.mime.text import MIMEText
|
|
|
|
from app.core.config import Settings
|
|
from app.core.logging import get_logger
|
|
from app.notifications.base import DeliveryResult, NotificationMessage
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
|
|
class SmtpEmailProvider:
|
|
provider_name = "smtp"
|
|
|
|
def __init__(self, settings: Settings) -> None:
|
|
self._settings = settings
|
|
|
|
async def send(self, message: NotificationMessage) -> DeliveryResult:
|
|
try:
|
|
await asyncio.to_thread(self._send_sync, message)
|
|
return DeliveryResult(success=True)
|
|
except Exception as exc: # pragma: no cover - network/SMTP failure path
|
|
logger.error("smtp_send_failed", error=str(exc))
|
|
return DeliveryResult(success=False, error=str(exc))
|
|
|
|
def _send_sync(self, message: NotificationMessage) -> None:
|
|
mime_message = MIMEMultipart("alternative")
|
|
mime_message["Subject"] = message.subject
|
|
mime_message["From"] = self._settings.smtp_from_email
|
|
mime_message["To"] = message.destination_value
|
|
mime_message.attach(MIMEText(message.body_text, "plain"))
|
|
if message.body_html:
|
|
mime_message.attach(MIMEText(message.body_html, "html"))
|
|
|
|
with smtplib.SMTP(self._settings.smtp_host, self._settings.smtp_port, timeout=10) as server:
|
|
if self._settings.smtp_use_tls:
|
|
server.starttls()
|
|
if self._settings.smtp_username:
|
|
server.login(self._settings.smtp_username, self._settings.smtp_password)
|
|
server.sendmail(
|
|
self._settings.smtp_from_email,
|
|
[message.destination_value],
|
|
mime_message.as_string(),
|
|
)
|