fix: improve SMTP configuration and error handling
- Change default use_tls from True to False to match typical STARTTLS setup - Add MailDeliveryError exception for mail delivery failures - Wrap send_mail calls in try-catch blocks to handle errors gracefully - Return 502 status code with error details when mail delivery fails - Add SMTP security mode selector in frontend (STARTTLS/TLS/None) - Add test mail form to admin panel - Handle empty SMTP credentials properly in update_mail_settings - Catch
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import smtplib
|
||||
import socket
|
||||
from email.message import EmailMessage
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
@@ -9,6 +10,10 @@ from app.models.entities import MailSetting
|
||||
from app.schemas.common import MailSettingsIn, MailSettingsOut
|
||||
|
||||
|
||||
class MailDeliveryError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
def get_mail_settings(db: Session) -> MailSetting:
|
||||
settings = db.get(MailSetting, 1)
|
||||
if not settings:
|
||||
@@ -33,12 +38,12 @@ def serialize_mail_settings(settings: MailSetting) -> MailSettingsOut:
|
||||
|
||||
def update_mail_settings(db: Session, payload: MailSettingsIn) -> MailSetting:
|
||||
settings = get_mail_settings(db)
|
||||
settings.smtp_host = payload.smtp_host
|
||||
settings.smtp_host = payload.smtp_host or None
|
||||
settings.smtp_port = payload.smtp_port
|
||||
settings.smtp_user = payload.smtp_user
|
||||
if payload.smtp_password is not None:
|
||||
settings.smtp_user = payload.smtp_user or None
|
||||
if payload.smtp_password:
|
||||
settings.smtp_password_encrypted = encrypt_secret(payload.smtp_password)
|
||||
settings.use_tls = payload.use_tls
|
||||
settings.use_tls = payload.use_tls and not payload.use_starttls
|
||||
settings.use_starttls = payload.use_starttls
|
||||
settings.sender_address = str(payload.sender_address) if payload.sender_address else None
|
||||
settings.sender_name = payload.sender_name
|
||||
@@ -48,7 +53,7 @@ def update_mail_settings(db: Session, payload: MailSettingsIn) -> MailSetting:
|
||||
def send_mail(db: Session, to: str, subject: str, body: str) -> None:
|
||||
settings = get_mail_settings(db)
|
||||
if not settings.smtp_host or not settings.sender_address:
|
||||
raise RuntimeError("SMTP is not configured")
|
||||
raise MailDeliveryError("SMTP is not configured")
|
||||
message = EmailMessage()
|
||||
message["From"] = f"{settings.sender_name} <{settings.sender_address}>"
|
||||
message["To"] = to
|
||||
@@ -56,12 +61,15 @@ def send_mail(db: Session, to: str, subject: str, body: str) -> None:
|
||||
message.set_content(body)
|
||||
password = decrypt_secret(settings.smtp_password_encrypted)
|
||||
client_cls = smtplib.SMTP_SSL if settings.use_tls and not settings.use_starttls else smtplib.SMTP
|
||||
with client_cls(settings.smtp_host, settings.smtp_port, timeout=20) as smtp:
|
||||
if settings.use_starttls:
|
||||
smtp.starttls()
|
||||
if settings.smtp_user and password:
|
||||
smtp.login(settings.smtp_user, password)
|
||||
smtp.send_message(message)
|
||||
try:
|
||||
with client_cls(settings.smtp_host, settings.smtp_port, timeout=20) as smtp:
|
||||
if settings.use_starttls:
|
||||
smtp.starttls()
|
||||
if settings.smtp_user and password:
|
||||
smtp.login(settings.smtp_user, password)
|
||||
smtp.send_message(message)
|
||||
except (OSError, smtplib.SMTPException, socket.timeout) as exc:
|
||||
raise MailDeliveryError(f"SMTP delivery failed: {exc}") from exc
|
||||
|
||||
|
||||
def invite_body(token: str) -> str:
|
||||
|
||||
Reference in New Issue
Block a user