Files
CIAgent/apps/api/app/notifications/smtp_email.py
T
saksham 1a4c80958f Initial commit: CI Agent competitive-intelligence monitoring app
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).
2026-08-05 10:48:20 -04:00

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(),
)