Improve email deliverability headers and SMTP identity
This commit is contained in:
@@ -1 +1 @@
|
||||
0403261321
|
||||
0403261736
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -14,6 +14,7 @@ from email.policy import SMTP as SMTP_POLICY
|
||||
from email.utils import formataddr, formatdate, make_msgid
|
||||
from io import BytesIO
|
||||
from typing import Any, Dict, Optional
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from ..build_info import BUILD_NUMBER
|
||||
from ..config import settings as env_settings
|
||||
@@ -512,6 +513,40 @@ def _build_default_base_url() -> str:
|
||||
return f"http://localhost:{port}"
|
||||
|
||||
|
||||
def _derive_mail_hostname(*, from_address: str) -> str:
|
||||
runtime = get_runtime_settings()
|
||||
candidates = (
|
||||
runtime.magent_application_url,
|
||||
runtime.magent_proxy_base_url,
|
||||
env_settings.cors_allow_origin,
|
||||
)
|
||||
for candidate in candidates:
|
||||
normalized = _normalize_display_text(candidate)
|
||||
if not normalized:
|
||||
continue
|
||||
parsed = urlparse(normalized if "://" in normalized else f"https://{normalized}")
|
||||
hostname = _normalize_display_text(parsed.hostname)
|
||||
if hostname and "." in hostname:
|
||||
return hostname
|
||||
domain = _normalize_display_text(from_address.split("@", 1)[1] if "@" in from_address else None)
|
||||
if domain and "." in domain:
|
||||
return domain
|
||||
return "localhost"
|
||||
|
||||
|
||||
def _add_transactional_headers(
|
||||
message: EmailMessage,
|
||||
*,
|
||||
from_name: str,
|
||||
from_address: str,
|
||||
) -> None:
|
||||
message["Reply-To"] = formataddr((from_name, from_address))
|
||||
message["Organization"] = env_settings.app_name
|
||||
message["X-Mailer"] = f"{env_settings.app_name}/{BUILD_NUMBER}"
|
||||
message["Auto-Submitted"] = "auto-generated"
|
||||
message["X-Auto-Response-Suppress"] = "All"
|
||||
|
||||
|
||||
def _looks_like_full_html_document(value: str) -> bool:
|
||||
probe = value.lstrip().lower()
|
||||
return probe.startswith("<!doctype") or probe.startswith("<html") or "<body" in probe[:300]
|
||||
@@ -918,8 +953,10 @@ def smtp_email_delivery_warning() -> Optional[str]:
|
||||
if host.endswith(".mail.protection.outlook.com") and not (username and password):
|
||||
return (
|
||||
"Unauthenticated Microsoft 365 relay mode is configured. SMTP acceptance does not "
|
||||
"confirm mailbox delivery. For reliable delivery, use smtp.office365.com:587 with "
|
||||
"SMTP credentials or configure a verified Exchange relay connector."
|
||||
"confirm mailbox delivery, and suspicious messages can still be filtered. For reliable "
|
||||
"delivery, use smtp.office365.com:587 with SMTP credentials or configure a verified "
|
||||
"Exchange relay connector and make sure SPF, DKIM, and DMARC are healthy for the "
|
||||
"sender domain."
|
||||
)
|
||||
return None
|
||||
|
||||
@@ -986,8 +1023,9 @@ def _send_email_sync(*, recipient_email: str, subject: str, body_text: str, body
|
||||
delivery_warning = smtp_email_delivery_warning()
|
||||
if not host or not from_address:
|
||||
raise RuntimeError("SMTP email settings are incomplete.")
|
||||
local_hostname = _derive_mail_hostname(from_address=from_address)
|
||||
logger.info(
|
||||
"smtp send started recipient=%s from=%s host=%s port=%s tls=%s ssl=%s auth=%s subject=%s",
|
||||
"smtp send started recipient=%s from=%s host=%s port=%s tls=%s ssl=%s auth=%s subject=%s ehlo=%s",
|
||||
recipient_email,
|
||||
from_address,
|
||||
host,
|
||||
@@ -996,6 +1034,7 @@ def _send_email_sync(*, recipient_email: str, subject: str, body_text: str, body
|
||||
use_ssl,
|
||||
bool(username and password),
|
||||
subject,
|
||||
local_hostname,
|
||||
)
|
||||
if delivery_warning:
|
||||
logger.warning("smtp delivery warning host=%s detail=%s", host, delivery_warning)
|
||||
@@ -1009,6 +1048,11 @@ def _send_email_sync(*, recipient_email: str, subject: str, body_text: str, body
|
||||
message["Message-ID"] = make_msgid(domain=from_address.split("@", 1)[1])
|
||||
else:
|
||||
message["Message-ID"] = make_msgid()
|
||||
_add_transactional_headers(
|
||||
message,
|
||||
from_name=from_name,
|
||||
from_address=from_address,
|
||||
)
|
||||
message.set_content(body_text or _strip_html_for_text(body_html))
|
||||
if body_html.strip():
|
||||
message.add_alternative(body_html, subtype="html")
|
||||
@@ -1027,7 +1071,7 @@ def _send_email_sync(*, recipient_email: str, subject: str, body_text: str, body
|
||||
)
|
||||
|
||||
if use_ssl:
|
||||
with smtplib.SMTP_SSL(host, port, timeout=20) as smtp:
|
||||
with smtplib.SMTP_SSL(host, port, timeout=20, local_hostname=local_hostname) as smtp:
|
||||
logger.debug("smtp ssl connection opened host=%s port=%s", host, port)
|
||||
if username and password:
|
||||
smtp.login(username, password)
|
||||
@@ -1047,7 +1091,7 @@ def _send_email_sync(*, recipient_email: str, subject: str, body_text: str, body
|
||||
)
|
||||
return receipt
|
||||
|
||||
with smtplib.SMTP(host, port, timeout=20) as smtp:
|
||||
with smtplib.SMTP(host, port, timeout=20, local_hostname=local_hostname) as smtp:
|
||||
logger.debug("smtp connection opened host=%s port=%s", host, port)
|
||||
smtp.ehlo()
|
||||
if use_tls:
|
||||
|
||||
4
frontend/package-lock.json
generated
4
frontend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "magent-frontend",
|
||||
"version": "0403261321",
|
||||
"version": "0403261736",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "magent-frontend",
|
||||
"version": "0403261321",
|
||||
"version": "0403261736",
|
||||
"dependencies": {
|
||||
"next": "16.1.6",
|
||||
"react": "19.2.4",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "magent-frontend",
|
||||
"private": true,
|
||||
"version": "0403261321",
|
||||
"version": "0403261736",
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
|
||||
Reference in New Issue
Block a user