mail fix
This commit is contained in:
@@ -12,10 +12,18 @@ def _truthy(v: str | None) -> bool:
|
|||||||
def send_email(*, to_email: str, subject: str, body_text: str):
|
def send_email(*, to_email: str, subject: str, body_text: str):
|
||||||
"""Send a plain-text email using SMTP settings from:
|
"""Send a plain-text email using SMTP settings from:
|
||||||
|
|
||||||
1) Environment variables (highest priority)
|
1) Admin-configured settings stored in the database (AppSettings) (highest priority)
|
||||||
2) Admin-configured settings stored in the database (AppSettings)
|
2) Environment variables (fallback)
|
||||||
|
|
||||||
Required env vars:
|
If you *do* want environment variables to override DB settings (e.g. in production),
|
||||||
|
set SMTP_OVERRIDE_DB=1.
|
||||||
|
|
||||||
|
Required configuration (either from DB or env):
|
||||||
|
- host
|
||||||
|
- username
|
||||||
|
- password
|
||||||
|
|
||||||
|
When using env vars, the names are:
|
||||||
- SMTP_HOST
|
- SMTP_HOST
|
||||||
- SMTP_PORT
|
- SMTP_PORT
|
||||||
- SMTP_USERNAME
|
- SMTP_USERNAME
|
||||||
@@ -28,9 +36,12 @@ def send_email(*, to_email: str, subject: str, body_text: str):
|
|||||||
- SMTP_DEBUG (default: "0") - set to 1 to print SMTP conversation to console
|
- SMTP_DEBUG (default: "0") - set to 1 to print SMTP conversation to console
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Pull optional defaults from DB (if available). This keeps backwards compatibility:
|
# Load DB settings (best-effort). If the DB has a complete SMTP config, we will use
|
||||||
# existing deployments that only use env vars keep working unchanged.
|
# it even if environment variables are present.
|
||||||
db_defaults = {}
|
#
|
||||||
|
# This fixes the common deployment situation where a .env/.flaskenv provides SMTP_*
|
||||||
|
# values that unintentionally override admin-configured settings.
|
||||||
|
db_defaults: dict[str, object] = {}
|
||||||
try:
|
try:
|
||||||
# Local import to avoid import cycles and to keep this module lightweight.
|
# Local import to avoid import cycles and to keep this module lightweight.
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
@@ -39,7 +50,8 @@ def send_email(*, to_email: str, subject: str, body_text: str):
|
|||||||
from .models import AppSettings
|
from .models import AppSettings
|
||||||
|
|
||||||
# Only try if app context exists (send_email is called inside requests/CLI normally).
|
# Only try if app context exists (send_email is called inside requests/CLI normally).
|
||||||
_ = current_app # noqa: F841
|
# Accessing the LocalProxy will raise if there is no active app context.
|
||||||
|
current_app._get_current_object()
|
||||||
s = db.session.get(AppSettings, 1)
|
s = db.session.get(AppSettings, 1)
|
||||||
if s:
|
if s:
|
||||||
db_defaults = {
|
db_defaults = {
|
||||||
@@ -56,33 +68,55 @@ def send_email(*, to_email: str, subject: str, body_text: str):
|
|||||||
# Best-effort; if DB isn't ready we fall back to env vars only.
|
# Best-effort; if DB isn't ready we fall back to env vars only.
|
||||||
db_defaults = {}
|
db_defaults = {}
|
||||||
|
|
||||||
host = os.environ.get("SMTP_HOST") or db_defaults.get("host")
|
# DB-first selection rules:
|
||||||
port = int(os.environ.get("SMTP_PORT") or db_defaults.get("port") or 587)
|
# - If *any* SMTP field is configured in DB, treat DB as authoritative (and do NOT
|
||||||
username = os.environ.get("SMTP_USERNAME") or db_defaults.get("username")
|
# silently mix in env vars). This avoids confusion where partial DB config still
|
||||||
password = os.environ.get("SMTP_PASSWORD") or db_defaults.get("password")
|
# results in env vars being used.
|
||||||
from_email = os.environ.get("SMTP_FROM") or db_defaults.get("from_email") or username
|
# - Env override is only possible with SMTP_OVERRIDE_DB=1.
|
||||||
starttls = _truthy(os.environ.get("SMTP_STARTTLS")) if os.environ.get("SMTP_STARTTLS") is not None else bool(
|
override_db = _truthy(os.environ.get("SMTP_OVERRIDE_DB"))
|
||||||
db_defaults.get("starttls") if db_defaults.get("starttls") is not None else True
|
db_config_present = bool(
|
||||||
)
|
db_defaults.get("host")
|
||||||
timeout = float(os.environ.get("SMTP_TIMEOUT_SECONDS") or db_defaults.get("timeout") or 10)
|
or db_defaults.get("username")
|
||||||
debug = _truthy(os.environ.get("SMTP_DEBUG")) if os.environ.get("SMTP_DEBUG") is not None else bool(
|
or db_defaults.get("password")
|
||||||
db_defaults.get("debug") or False
|
or db_defaults.get("from_email")
|
||||||
|
or (db_defaults.get("port") is not None)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if db_config_present and not override_db:
|
||||||
|
config_source = "db"
|
||||||
|
host = str(db_defaults.get("host") or "") or None
|
||||||
|
port = int(db_defaults.get("port") or 587)
|
||||||
|
username = str(db_defaults.get("username") or "") or None
|
||||||
|
password = str(db_defaults.get("password") or "") or None
|
||||||
|
from_email = (str(db_defaults.get("from_email")) if db_defaults.get("from_email") else None) or username
|
||||||
|
starttls = bool(db_defaults.get("starttls") if db_defaults.get("starttls") is not None else True)
|
||||||
|
timeout = float(db_defaults.get("timeout") or 10)
|
||||||
|
debug = bool(db_defaults.get("debug") or False)
|
||||||
|
else:
|
||||||
|
config_source = "env"
|
||||||
|
host = os.environ.get("SMTP_HOST")
|
||||||
|
port = int(os.environ.get("SMTP_PORT") or 587)
|
||||||
|
username = os.environ.get("SMTP_USERNAME")
|
||||||
|
password = os.environ.get("SMTP_PASSWORD")
|
||||||
|
from_email = os.environ.get("SMTP_FROM") or username
|
||||||
|
starttls = _truthy(os.environ.get("SMTP_STARTTLS")) if os.environ.get("SMTP_STARTTLS") is not None else True
|
||||||
|
timeout = float(os.environ.get("SMTP_TIMEOUT_SECONDS") or 10)
|
||||||
|
debug = _truthy(os.environ.get("SMTP_DEBUG")) if os.environ.get("SMTP_DEBUG") is not None else False
|
||||||
|
|
||||||
missing = []
|
missing = []
|
||||||
if not host:
|
if not host:
|
||||||
missing.append("SMTP_HOST")
|
missing.append("host")
|
||||||
if not username:
|
if not username:
|
||||||
missing.append("SMTP_USERNAME")
|
missing.append("username")
|
||||||
if not password:
|
if not password:
|
||||||
missing.append("SMTP_PASSWORD")
|
missing.append("password")
|
||||||
if not from_email:
|
if not from_email:
|
||||||
missing.append("SMTP_FROM")
|
missing.append("from_email")
|
||||||
if missing:
|
if missing:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Missing SMTP configuration: "
|
f"Missing SMTP configuration ({config_source}): "
|
||||||
+ ", ".join(missing)
|
+ ", ".join(missing)
|
||||||
+ ". Set them as environment variables (or configure them in Admin → Settings)."
|
+ ". Configure it in Admin → Settings (or set SMTP_* environment variables)."
|
||||||
)
|
)
|
||||||
|
|
||||||
msg = EmailMessage()
|
msg = EmailMessage()
|
||||||
@@ -98,6 +132,7 @@ def send_email(*, to_email: str, subject: str, body_text: str):
|
|||||||
with smtplib.SMTP(host, port, timeout=timeout) as smtp:
|
with smtplib.SMTP(host, port, timeout=timeout) as smtp:
|
||||||
if debug:
|
if debug:
|
||||||
smtp.set_debuglevel(1)
|
smtp.set_debuglevel(1)
|
||||||
|
print(f"[email_utils] Using SMTP config from: {config_source}")
|
||||||
smtp.ehlo()
|
smtp.ehlo()
|
||||||
if starttls:
|
if starttls:
|
||||||
smtp.starttls()
|
smtp.starttls()
|
||||||
|
|||||||
Reference in New Issue
Block a user