""" Email sending service. In development (SMTP_HOST unset or EMAIL_BACKEND=console), codes are printed to stdout instead of being sent. In production set: SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD, EMAIL_FROM """ import asyncio import smtplib import ssl import logging from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from functools import partial from app.config import settings logger = logging.getLogger(__name__) def _send_smtp_blocking(to_address: str, subject: str, html_body: str, text_body: str) -> None: context = ssl.create_default_context() with smtplib.SMTP(settings.SMTP_HOST, settings.SMTP_PORT) as server: if settings.SMTP_USE_TLS: server.starttls(context=context) if settings.SMTP_USER and settings.SMTP_PASSWORD: server.login(settings.SMTP_USER, settings.SMTP_PASSWORD) msg = MIMEMultipart("alternative") msg["Subject"] = subject msg["From"] = settings.EMAIL_FROM msg["To"] = to_address msg.attach(MIMEText(text_body, "plain")) msg.attach(MIMEText(html_body, "html")) server.sendmail(settings.EMAIL_FROM, to_address, msg.as_string()) async def send_email(to_address: str, subject: str, html_body: str, text_body: str) -> None: if settings.EMAIL_BACKEND == "console" or not settings.SMTP_HOST: logger.info( "\n%s\nTO: %s\nSUBJECT: %s\n%s\n%s", "=" * 60, to_address, subject, text_body, "=" * 60, ) print(f"\n{'='*60}\nEMAIL TO: {to_address}\nSUBJECT: {subject}\n{text_body}\n{'='*60}\n") return loop = asyncio.get_event_loop() fn = partial(_send_smtp_blocking, to_address, subject, html_body, text_body) await loop.run_in_executor(None, fn) # ── Template helpers ─────────────────────────────────────────────────────────── def _base_html(content: str) -> str: return f"""
Auckland's favourite dog walking service
Hi {first_name}!
Welcome to the Goodwalk Members Area. Use the code below to claim your account. It expires in 15 minutes.
This code is valid for 15 minutes and can only be used once.
If you didn't request this, please contact us at info@goodwalk.co.nz.
Hi {first_name}!
Here's your one-time login code for the Goodwalk Members Area. It expires in 10 minutes.
This code is valid for 10 minutes and can only be used once.
If you didn't try to log in, please contact us immediately at info@goodwalk.co.nz.
Hi {first_name}!
Thanks for getting in touch with Goodwalk. We've opened your onboarding invitation so you can complete your details and sign your service agreement.
This link is valid for 7 days and can only be used once.
Once your onboarding is complete and your contract is signed, we'll activate your members account.
If you have any questions, reach us at info@goodwalk.co.nz.