#!/usr/bin/env python3 """ SMTP email service for outbound notifications (access requests, etc). Reads credentials from env vars loaded by api_server.load_environment_config(): SMTP_SERVER, SMTP_PORT, SMTP_USER, SMTP_PASSWORD, SENDER_EMAIL Usage: from email_service import send_email ok, err = send_email( ['admin@example.com'], subject='Access request', body='Plain text body', html_body='
HTML body
', # optional ) """ import os import smtplib import ssl from email.message import EmailMessage from typing import Iterable, Optional, Tuple def is_configured() -> bool: return all(os.environ.get(k) for k in ('SMTP_SERVER', 'SMTP_USER', 'SMTP_PASSWORD', 'SENDER_EMAIL')) def send_email( to_addresses: Iterable[str], subject: str, body: str, html_body: Optional[str] = None, reply_to: Optional[str] = None, ) -> Tuple[bool, Optional[str]]: """Send an email via SMTP with STARTTLS. Returns (success, error_message).""" recipients = [a for a in (to_addresses or []) if a] if not recipients: return False, 'No recipients provided' if not is_configured(): return False, 'SMTP is not configured (missing SMTP_SERVER / SMTP_USER / SMTP_PASSWORD / SENDER_EMAIL)' server = os.environ['SMTP_SERVER'] port = int(os.environ.get('SMTP_PORT', '587')) user = os.environ['SMTP_USER'] password = os.environ['SMTP_PASSWORD'] sender = os.environ['SENDER_EMAIL'] msg = EmailMessage() msg['Subject'] = subject msg['From'] = sender msg['To'] = ', '.join(recipients) if reply_to: msg['Reply-To'] = reply_to msg.set_content(body) if html_body: msg.add_alternative(html_body, subtype='html') try: context = ssl.create_default_context() with smtplib.SMTP(server, port, timeout=15) as smtp: smtp.ehlo() smtp.starttls(context=context) smtp.ehlo() smtp.login(user, password) smtp.send_message(msg) return True, None except Exception as e: print(f'[email_service] send_email failed: {e}') return False, str(e)