"""Email sending service via SMTP. If SMTP credentials are not configured (SMTP_HOST / SMTP_USER empty), emails are logged to console instead — useful for local development. Supabase SMTP settings can be found in: Project Settings → Auth → SMTP Settings (enable custom SMTP) Or use any external provider (SendGrid, Mailgun, Brevo, etc.) and put the credentials in the .env file. """ from __future__ import annotations import logging import smtplib import ssl from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from app.core.config import get_settings logger = logging.getLogger(__name__) class EmailService: def __init__(self): self.settings = get_settings() def _is_configured(self) -> bool: return bool(self.settings.SMTP_HOST and self.settings.SMTP_USER) def _send(self, to: str, subject: str, html_body: str, text_body: str) -> None: s = self.settings msg = MIMEMultipart("alternative") msg["Subject"] = subject msg["From"] = s.EMAIL_FROM msg["To"] = to msg.attach(MIMEText(text_body, "plain", "utf-8")) msg.attach(MIMEText(html_body, "html", "utf-8")) ctx = ssl.create_default_context() try: if s.SMTP_PORT == 465: with smtplib.SMTP_SSL(s.SMTP_HOST, s.SMTP_PORT, context=ctx) as srv: srv.login(s.SMTP_USER, s.SMTP_PASSWORD) srv.sendmail(s.EMAIL_FROM, to, msg.as_string()) else: with smtplib.SMTP(s.SMTP_HOST, s.SMTP_PORT) as srv: srv.ehlo() srv.starttls(context=ctx) srv.login(s.SMTP_USER, s.SMTP_PASSWORD) srv.sendmail(s.EMAIL_FROM, to, msg.as_string()) except Exception as exc: logger.error("Failed to send email to %s: %s", to, exc) raise # ── Public send methods ─────────────────────────────────── def send_password_reset_email(self, to_email: str, reset_url: str) -> None: subject = f"Reset your {self.settings.APP_NAME} password" html = f"""
You requested a password reset for your {self.settings.APP_NAME} account.
Click the button below. This link expires in 1 hour.
If you did not request this, you can safely ignore this email.
Or paste this link: {reset_url}
Thanks for signing up. Please verify your email address to activate your account.
This link expires in 24 hours. If you did not sign up, ignore this email.
Or paste this link: {verify_url}
Hi {agency_name},
{sender_name} sent you a message about your listing "{listing_title}".
Or visit: {dashboard_url}