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):
|
||||
"""Send a plain-text email using SMTP settings from:
|
||||
|
||||
1) Environment variables (highest priority)
|
||||
2) Admin-configured settings stored in the database (AppSettings)
|
||||
1) Admin-configured settings stored in the database (AppSettings) (highest priority)
|
||||
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_PORT
|
||||
- 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
|
||||
"""
|
||||
|
||||
# Pull optional defaults from DB (if available). This keeps backwards compatibility:
|
||||
# existing deployments that only use env vars keep working unchanged.
|
||||
db_defaults = {}
|
||||
# Load DB settings (best-effort). If the DB has a complete SMTP config, we will use
|
||||
# it even if environment variables are present.
|
||||
#
|
||||
# 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:
|
||||
# Local import to avoid import cycles and to keep this module lightweight.
|
||||
from flask import current_app
|
||||
@@ -39,7 +50,8 @@ def send_email(*, to_email: str, subject: str, body_text: str):
|
||||
from .models import AppSettings
|
||||
|
||||
# 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)
|
||||
if s:
|
||||
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.
|
||||
db_defaults = {}
|
||||
|
||||
host = os.environ.get("SMTP_HOST") or db_defaults.get("host")
|
||||
port = int(os.environ.get("SMTP_PORT") or db_defaults.get("port") or 587)
|
||||
username = os.environ.get("SMTP_USERNAME") or db_defaults.get("username")
|
||||
password = os.environ.get("SMTP_PASSWORD") or db_defaults.get("password")
|
||||
from_email = os.environ.get("SMTP_FROM") or db_defaults.get("from_email") or username
|
||||
starttls = _truthy(os.environ.get("SMTP_STARTTLS")) if os.environ.get("SMTP_STARTTLS") is not None else bool(
|
||||
db_defaults.get("starttls") if db_defaults.get("starttls") is not None else True
|
||||
)
|
||||
timeout = float(os.environ.get("SMTP_TIMEOUT_SECONDS") or db_defaults.get("timeout") or 10)
|
||||
debug = _truthy(os.environ.get("SMTP_DEBUG")) if os.environ.get("SMTP_DEBUG") is not None else bool(
|
||||
db_defaults.get("debug") or False
|
||||
# DB-first selection rules:
|
||||
# - If *any* SMTP field is configured in DB, treat DB as authoritative (and do NOT
|
||||
# silently mix in env vars). This avoids confusion where partial DB config still
|
||||
# results in env vars being used.
|
||||
# - Env override is only possible with SMTP_OVERRIDE_DB=1.
|
||||
override_db = _truthy(os.environ.get("SMTP_OVERRIDE_DB"))
|
||||
db_config_present = bool(
|
||||
db_defaults.get("host")
|
||||
or db_defaults.get("username")
|
||||
or db_defaults.get("password")
|
||||
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 = []
|
||||
if not host:
|
||||
missing.append("SMTP_HOST")
|
||||
missing.append("host")
|
||||
if not username:
|
||||
missing.append("SMTP_USERNAME")
|
||||
missing.append("username")
|
||||
if not password:
|
||||
missing.append("SMTP_PASSWORD")
|
||||
missing.append("password")
|
||||
if not from_email:
|
||||
missing.append("SMTP_FROM")
|
||||
missing.append("from_email")
|
||||
if missing:
|
||||
raise RuntimeError(
|
||||
"Missing SMTP configuration: "
|
||||
f"Missing SMTP configuration ({config_source}): "
|
||||
+ ", ".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()
|
||||
@@ -98,6 +132,7 @@ def send_email(*, to_email: str, subject: str, body_text: str):
|
||||
with smtplib.SMTP(host, port, timeout=timeout) as smtp:
|
||||
if debug:
|
||||
smtp.set_debuglevel(1)
|
||||
print(f"[email_utils] Using SMTP config from: {config_source}")
|
||||
smtp.ehlo()
|
||||
if starttls:
|
||||
smtp.starttls()
|
||||
|
||||
Reference in New Issue
Block a user