55 lines
1.8 KiB
Python
55 lines
1.8 KiB
Python
"""Outbound email (SMTP) and template rendering."""
|
|||
|
|
import os
|
||
|
|
import smtplib
|
||
|
|
from email.message import EmailMessage
|
||
|
|
|
||
|
|
|
||
|
|
def render(template: str, **vars) -> str:
|
||
|
|
"""Safe {placeholder} substitution — unknown placeholders are left as-is."""
|
||
|
|
out = template
|
||
|
|
for k, v in vars.items():
|
||
|
|
out = out.replace("{" + k + "}", str(v))
|
||
|
|
return out
|
||
|
|
|
||
|
|
|
||
|
|
def send_mail(to_addrs, subject, body):
|
||
|
|
"""Send a plain-text email. Returns (ok, detail)."""
|
||
|
|
if isinstance(to_addrs, str):
|
||
|
|
to_addrs = [to_addrs]
|
||
|
|
to_addrs = [a for a in to_addrs if a]
|
||
|
|
if not to_addrs:
|
||
|
|
return False, "no recipients"
|
||
|
|
|
||
|
|
host = os.environ.get("SMTP_HOST", "")
|
||
|
|
port = int(os.environ.get("SMTP_PORT", "587"))
|
||
|
|
user = os.environ.get("SMTP_USER", "")
|
||
|
|
password = os.environ.get("SMTP_PASSWORD", "")
|
||
|
|
sender = os.environ.get("SMTP_FROM", user or "approvalflow@localhost")
|
||
|
|
security = os.environ.get("SMTP_SECURITY", "starttls").lower() # starttls / ssl / none
|
||
|
|
|
||
|
|
msg = EmailMessage()
|
||
|
|
msg["From"] = sender
|
||
|
|
msg["To"] = ", ".join(to_addrs)
|
||
|
|
msg["Subject"] = subject
|
||
|
|
msg.set_content(body)
|
||
|
|
|
||
|
|
if not host:
|
||
|
|
# Dev mode: print instead of sending so the app is testable without SMTP.
|
||
|
|
print(f"[mail:DEV] To: {msg['To']}\nSubject: {subject}\n{body}\n{'-'*50}")
|
||
|
|
return True, "dev mode (SMTP_HOST not set) — printed to console"
|
||
|
|
|
||
|
|
try:
|
||
|
|
if security == "ssl":
|
||
|
|
server = smtplib.SMTP_SSL(host, port, timeout=30)
|
||
|
|
else:
|
||
|
|
server = smtplib.SMTP(host, port, timeout=30)
|
||
|
|
if security == "starttls":
|
||
|
|
server.starttls()
|
||
|
|
if user:
|
||
|
|
server.login(user, password)
|
||
|
|
server.send_message(msg)
|
||
|
|
server.quit()
|
||
|
|
return True, f"sent to {', '.join(to_addrs)}"
|
||
|
|
except Exception as e:
|
||
|
|
return False, f"SMTP error: {e}"
|