2025-12-04 10:04:21 +08:00

69 lines
2.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# app/services/mailer.py
import smtplib
import ssl
from email.message import EmailMessage
from typing import Optional
from loguru import logger
from app.core.config import get_app_settings
def _build_message(*, from_email: str, to_email: str, subject: str, html: str) -> EmailMessage:
msg = EmailMessage()
msg["From"] = from_email
msg["To"] = to_email
msg["Subject"] = subject
msg.set_content("Your email client does not support HTML.")
msg.add_alternative(html, subtype="html")
return msg
def send_email(to_email: str, subject: str, html: str) -> bool:
"""
同步发送;成功返回 True失败返回 False并打印详细日志。
- 端口 465使用 SMTP_SSL
- 其他端口:使用 SMTP + (可选)STARTTLS
"""
s = get_app_settings()
from_email = str(s.mail_from)
smtp_host = s.smtp_host
smtp_port = int(s.smtp_port)
smtp_user: Optional[str] = s.smtp_user.get_secret_value() if s.smtp_user else None
smtp_pass: Optional[str] = s.smtp_password.get_secret_value() if s.smtp_password else None
msg = _build_message(from_email=from_email, to_email=to_email, subject=subject, html=html)
logger.info(
"SMTP send start → host={} port={} tls={} from={} to={}",
smtp_host, smtp_port, s.smtp_tls, from_email, to_email,
)
try:
if smtp_port == 465:
context = ssl.create_default_context()
with smtplib.SMTP_SSL(smtp_host, smtp_port, context=context, timeout=20) as server:
if smtp_user and smtp_pass:
server.login(smtp_user, smtp_pass)
server.send_message(msg)
else:
with smtplib.SMTP(smtp_host, smtp_port, timeout=20) as server:
server.ehlo()
if s.smtp_tls:
context = ssl.create_default_context()
server.starttls(context=context)
server.ehlo()
if smtp_user and smtp_pass:
server.login(smtp_user, smtp_pass)
server.send_message(msg)
logger.info("SMTP send OK to {}", to_email)
return True
except smtplib.SMTPResponseException as e:
# 能拿到服务端 code/resp 的错误
logger.error("SMTPResponseException: code={} msg={}", getattr(e, "smtp_code", None), getattr(e, "smtp_error", None))
return False
except Exception as e:
logger.exception("SMTP send failed: {}", e)
return False