58 lines
1.8 KiB
Python
58 lines
1.8 KiB
Python
"""
|
|
utils/email.py — SMTP email helper.
|
|
Reads server settings from app_settings (email.smtp_*) with .env fallbacks.
|
|
"""
|
|
|
|
import logging
|
|
import smtplib
|
|
from email.mime.multipart import MIMEMultipart
|
|
from email.mime.text import MIMEText
|
|
from config import get_setting
|
|
|
|
logger = logging.getLogger("utils.email")
|
|
|
|
|
|
def send_email(to, subject: str, body_text: str, body_html: str = None) -> None:
|
|
"""Send email via configured SMTP. `to` may be a str or list[str]. Raises on failure."""
|
|
host = get_setting("email.smtp_host", "").strip()
|
|
port = get_setting("email.smtp_port", "587").strip()
|
|
user = get_setting("email.smtp_user", "").strip()
|
|
passwd = get_setting("email.smtp_password", "").strip()
|
|
from_ = get_setting("email.smtp_from", "").strip() or user
|
|
|
|
if not host:
|
|
raise ValueError("SMTP host not configured. Add it via Admin → Settings.")
|
|
|
|
try:
|
|
port = int(port)
|
|
except (ValueError, TypeError):
|
|
port = 587
|
|
|
|
recipients = [to] if isinstance(to, str) else list(to)
|
|
if not recipients:
|
|
raise ValueError("No recipients specified.")
|
|
|
|
if body_html:
|
|
msg = MIMEMultipart("alternative")
|
|
msg.attach(MIMEText(body_text, "plain"))
|
|
msg.attach(MIMEText(body_html, "html"))
|
|
else:
|
|
msg = MIMEText(body_text, "plain")
|
|
|
|
msg["Subject"] = subject
|
|
msg["From"] = from_
|
|
msg["To"] = ", ".join(recipients)
|
|
|
|
with smtplib.SMTP(host, port, timeout=15) as smtp:
|
|
smtp.ehlo()
|
|
try:
|
|
smtp.starttls()
|
|
smtp.ehlo()
|
|
except smtplib.SMTPException:
|
|
pass # server may not support STARTTLS
|
|
if user and passwd:
|
|
smtp.login(user, passwd)
|
|
smtp.sendmail(from_, recipients, msg.as_string())
|
|
|
|
logger.info("Email sent to %s: %r", recipients, subject)
|