Aggiunge invio prenotazioni con Mailjet

This commit is contained in:
2026-09-04 15:46:18 +02:00
parent 8d83f0c619
commit ba4a73d5ca
3 changed files with 158 additions and 20 deletions

View File

@@ -1,8 +1,13 @@
from __future__ import annotations
import base64
from datetime import datetime
from email.message import EmailMessage
from html import escape
import json
import smtplib
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen
from app.config import settings
from app.schemas.booking import BookingRequestCreate
@@ -12,28 +17,42 @@ class MailConfigurationError(RuntimeError):
pass
class MailDeliveryError(RuntimeError):
pass
def _build_booking_subject(payload: BookingRequestCreate) -> str:
return f"{settings.booking_email_subject_prefix} Nuova richiesta visita - {payload.name}"
def _build_booking_html(payload: BookingRequestCreate) -> str:
submitted_at = datetime.now().strftime("%d/%m/%Y %H:%M")
name = escape(payload.name)
email = escape(payload.email)
phone = escape(payload.phone)
pet_name = escape(payload.pet_name or "-")
pet_type = escape(payload.pet_type)
doctor = escape(payload.doctor)
service = escape(payload.service)
date = escape(payload.date)
time = escape(payload.time or "Qualsiasi orario")
notes = escape(payload.notes or "-")
return f"""
<html>
<body style="font-family: Arial, sans-serif; color: #1f2937; line-height: 1.6;">
<h2 style="color: #1B4F72;">Nuova richiesta di prenotazione visita</h2>
<p>È stata inviata una nuova richiesta di prenotazione non vincolante dal sito web.</p>
<table cellpadding="8" cellspacing="0" border="0" style="border-collapse: collapse;">
<tr><td><strong>Nome e cognome</strong></td><td>{payload.name}</td></tr>
<tr><td><strong>Email</strong></td><td>{payload.email}</td></tr>
<tr><td><strong>Telefono</strong></td><td>{payload.phone}</td></tr>
<tr><td><strong>Nome animale</strong></td><td>{payload.pet_name or "-"}</td></tr>
<tr><td><strong>Tipo animale</strong></td><td>{payload.pet_type}</td></tr>
<tr><td><strong>Medico richiesto</strong></td><td>{payload.doctor}</td></tr>
<tr><td><strong>Tipo di visita</strong></td><td>{payload.service}</td></tr>
<tr><td><strong>Periodo preferito</strong></td><td>{payload.date}</td></tr>
<tr><td><strong>Fascia oraria preferita</strong></td><td>{payload.time or "Qualsiasi orario"}</td></tr>
<tr><td><strong>Note</strong></td><td>{payload.notes or "-"}</td></tr>
<tr><td><strong>Nome e cognome</strong></td><td>{name}</td></tr>
<tr><td><strong>Email</strong></td><td>{email}</td></tr>
<tr><td><strong>Telefono</strong></td><td>{phone}</td></tr>
<tr><td><strong>Nome animale</strong></td><td>{pet_name}</td></tr>
<tr><td><strong>Tipo animale</strong></td><td>{pet_type}</td></tr>
<tr><td><strong>Medico richiesto</strong></td><td>{doctor}</td></tr>
<tr><td><strong>Tipo di visita</strong></td><td>{service}</td></tr>
<tr><td><strong>Periodo preferito</strong></td><td>{date}</td></tr>
<tr><td><strong>Fascia oraria preferita</strong></td><td>{time}</td></tr>
<tr><td><strong>Note</strong></td><td>{notes}</td></tr>
<tr><td><strong>Inviata il</strong></td><td>{submitted_at}</td></tr>
</table>
<p style="margin-top: 20px;">
@@ -65,6 +84,21 @@ def _build_booking_text(payload: BookingRequestCreate) -> str:
def send_booking_request_email(payload: BookingRequestCreate) -> None:
if not settings.booking_mail_enabled:
raise MailConfigurationError(f"Configurazione email incompleta per provider {settings.mail_provider}.")
if settings.mail_provider == "mailjet":
_send_booking_request_email_mailjet(payload)
return
if settings.mail_provider == "smtp":
_send_booking_request_email_smtp(payload)
return
raise MailConfigurationError(f"Provider email non supportato: {settings.mail_provider}")
def _send_booking_request_email_smtp(payload: BookingRequestCreate) -> None:
if not settings.booking_mail_enabled:
raise MailConfigurationError("Configurazione SMTP incompleta.")
@@ -81,3 +115,69 @@ def send_booking_request_email(payload: BookingRequestCreate) -> None:
server.starttls()
server.login(settings.smtp_username, settings.smtp_password)
server.send_message(message)
def _send_booking_request_email_mailjet(payload: BookingRequestCreate) -> None:
mailjet_payload = {
"Messages": [
{
"From": {
"Email": settings.booking_email_from,
"Name": settings.booking_email_from_name,
},
"To": [
{
"Email": settings.booking_email_to,
"Name": "Clinica Veterinaria Formiginese",
}
],
"ReplyTo": {
"Email": payload.email,
"Name": payload.name,
},
"Subject": _build_booking_subject(payload),
"TextPart": _build_booking_text(payload),
"HTMLPart": _build_booking_html(payload),
}
]
}
if settings.mailjet_sandbox_mode:
mailjet_payload["SandboxMode"] = True
auth_token = base64.b64encode(
f"{settings.mailjet_api_key}:{settings.mailjet_api_secret}".encode("utf-8")
).decode("ascii")
request = Request(
settings.mailjet_api_url,
data=json.dumps(mailjet_payload).encode("utf-8"),
headers={
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization": f"Basic {auth_token}",
},
method="POST",
)
try:
with urlopen(request, timeout=30) as response:
response_body = response.read().decode("utf-8")
response_status = response.status
except HTTPError as exc:
error_body = exc.read().decode("utf-8", errors="replace")
raise MailDeliveryError(f"Mailjet API HTTP {exc.code}: {error_body[:500]}") from exc
except URLError as exc:
raise MailDeliveryError(f"Mailjet API connection error: {exc.reason}") from exc
if response_status < 200 or response_status >= 300:
raise MailDeliveryError(f"Mailjet API HTTP {response_status}: {response_body[:500]}")
try:
response_payload = json.loads(response_body)
except json.JSONDecodeError as exc:
raise MailDeliveryError(f"Mailjet API response non JSON: {response_body[:500]}") from exc
messages = response_payload.get("Messages", [])
message_status = messages[0].get("Status") if messages else None
if message_status != "success":
raise MailDeliveryError(f"Mailjet API delivery status {message_status}: {response_body[:500]}")