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).
51 lines
1.8 KiB
Python
51 lines
1.8 KiB
Python
"""Resend HTTP API email provider - a separate transport from SmtpEmailProvider
|
|
(which also happens to point at Resend's SMTP relay for alert notifications
|
|
in this deployment, but that's a different concern/sender identity; see
|
|
app/services/security_email_service.py). Fixed, trusted first-party vendor
|
|
endpoint - no SSRF guard needed, same reasoning as the NinjaPear/USPTO calls
|
|
elsewhere in this app.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import httpx
|
|
|
|
from app.core.config import Settings
|
|
from app.core.logging import get_logger
|
|
from app.notifications.base import DeliveryResult, NotificationMessage
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
_RESEND_API_URL = "https://api.resend.com/emails"
|
|
|
|
|
|
class ResendEmailProvider:
|
|
provider_name = "resend"
|
|
|
|
def __init__(self, settings: Settings) -> None:
|
|
self._settings = settings
|
|
|
|
async def send(self, message: NotificationMessage) -> DeliveryResult:
|
|
payload = {
|
|
"from": self._settings.resend_security_from_email,
|
|
"to": [message.destination_value],
|
|
"subject": message.subject,
|
|
"text": message.body_text,
|
|
}
|
|
if message.body_html:
|
|
payload["html"] = message.body_html
|
|
|
|
try:
|
|
async with httpx.AsyncClient(timeout=10) as client:
|
|
response = await client.post(
|
|
_RESEND_API_URL,
|
|
headers={"Authorization": f"Bearer {self._settings.resend_api_key}"},
|
|
json=payload,
|
|
)
|
|
response.raise_for_status()
|
|
data = response.json()
|
|
return DeliveryResult(success=True, external_message_id=data.get("id"))
|
|
except Exception as exc: # noqa: BLE001 - network/API failure path
|
|
logger.error("resend_send_failed", error=str(exc))
|
|
return DeliveryResult(success=False, error=str(exc))
|