feat(settings): add SMTP admin configuration

This commit is contained in:
Schubert Ferenc 2026-07-04 23:54:20 +02:00
parent c58e212e3a
commit 884a20e043
32 changed files with 1200 additions and 42 deletions

View file

@ -1,13 +1,10 @@
import smtplib
from dataclasses import dataclass
from datetime import UTC, datetime
from email.message import EmailMessage
from html import escape
from sqlalchemy.orm import Session
from starlette.requests import Request
from app.core.config import settings
from app.models.repair import Repair, RepairNotificationEvent
from app.models.user import User
from app.repositories.repair_repository import RepairRepository
@ -15,6 +12,7 @@ from app.schemas.repair import RepairNotificationOverviewResponse, RepairNotific
from app.services.audit_service import write_audit_log
from app.services.repair_public_link_service import RepairPublicLinkService
from app.services.repair_status_labels import MAIL_STATUS_LABELS, STATUS_LABELS
from app.services.system_settings_service import SystemSettingsService
@dataclass(frozen=True)
@ -98,16 +96,6 @@ def _repair_label(repair: Repair) -> str:
return f"{repair.repair_number} · {repair.customer_name}"
def _smtp_configured() -> bool:
return bool(settings.smtp_host and settings.smtp_from_email)
def _from_header() -> str:
if settings.smtp_from_name:
return f"{settings.smtp_from_name} <{settings.smtp_from_email}>"
return str(settings.smtp_from_email)
def _plain_status_mail(repair: Repair, *, status_label: str, public_status_url: str) -> str:
return (
f"Hallo {repair.customer_name},\n\n"
@ -235,8 +223,9 @@ class RepairNotificationService:
text = _plain_status_mail(repair, status_label=status_label, public_status_url=public_status_url)
html = _html_status_mail(repair, status_label=status_label, public_status_url=public_status_url)
smtp_config = SystemSettingsService.get_smtp_runtime_config(db)
if not _smtp_configured():
if not smtp_config.is_configured:
event = RepairRepository.create_notification_event(
db,
repair_id=repair.id,
@ -253,7 +242,7 @@ class RepairNotificationService:
return event
try:
RepairNotificationService._send_email(recipient=recipient, subject=subject, text=text, html=html)
SystemSettingsService.send_email(smtp_config, recipient=recipient, subject=subject, text=text, html=html)
except Exception:
event = RepairRepository.create_notification_event(
db,
@ -294,22 +283,6 @@ class RepairNotificationService:
)
return event
@staticmethod
def _send_email(*, recipient: str, subject: str, text: str, html: str) -> None:
message = EmailMessage()
message["Subject"] = subject
message["From"] = _from_header()
message["To"] = recipient
message.set_content(text)
message.add_alternative(html, subtype="html")
with smtplib.SMTP(str(settings.smtp_host), settings.smtp_port, timeout=15) as smtp:
if settings.smtp_use_tls:
smtp.starttls()
if settings.smtp_username and settings.smtp_password:
smtp.login(settings.smtp_username, settings.smtp_password)
smtp.send_message(message)
@staticmethod
def _audit_failure(db: Session, repair: Repair, *, actor: User | None, request: Request | None, reason: str) -> None:
write_audit_log(