"""Email service for Sneaky Klaus application. Handles email sending via Resend API with dev mode support. In development mode, emails are logged instead of sent. """ import logging import os from typing import Any import resend from flask import current_app logger = logging.getLogger(__name__) class EmailService: """Service for sending emails via Resend API. In development mode (FLASK_ENV=development), emails are logged instead of actually being sent. Magic link URLs are also logged for easier testing. """ def __init__(self) -> None: """Initialize the email service.""" self.api_key = current_app.config.get("RESEND_API_KEY") self.from_email = current_app.config.get( "EMAIL_FROM", "noreply@sneaky-klaus.com" ) # Check if in dev mode - first check config, then environment flask_env = current_app.config.get("FLASK_ENV") or os.environ.get("FLASK_ENV") self.dev_mode = flask_env == "development" # In production, API key is required if not self.dev_mode and not self.api_key: raise ValueError("RESEND_API_KEY is required in production mode") # Configure Resend if we have an API key if self.api_key: resend.api_key = self.api_key def send_email( self, to: str, subject: str, html_body: str, ) -> dict[str, Any]: """Send an email. Args: to: Recipient email address subject: Email subject line html_body: HTML email body Returns: Response from Resend API or mock response in dev mode Raises: Exception: If email sending fails """ if self.dev_mode: # In dev mode, just log the email logger.info("=" * 80) logger.info("DEV MODE: Email not sent") logger.info(f"To: {to}") logger.info(f"Subject: {subject}") logger.info(f"Body preview: {html_body[:200]}...") logger.info("=" * 80) # Return a mock success response return {"id": f"dev-mode-{os.urandom(8).hex()}"} # Send via Resend params: resend.Emails.SendParams = { "from": self.from_email, "to": [to], "subject": subject, "html": html_body, } result: dict[str, Any] = resend.Emails.send(params) return result def send_magic_link( self, to: str, magic_link_url: str, exchange_name: str, ) -> dict[str, Any]: """Send a magic link email for participant authentication. Args: to: Participant email address magic_link_url: Full URL with magic token exchange_name: Name of the exchange Returns: Response from email service """ if self.dev_mode: logger.info(f"DEV MODE: Magic link URL: {magic_link_url}") subject = f"Access your {exchange_name} Secret Santa" html_body = f"""
Click the link below to access your {exchange_name} Secret Santa exchange:
This link will expire in 1 hour.
If you didn't request this link, you can safely ignore this email.
""" return self.send_email(to=to, subject=subject, html_body=html_body) def send_registration_confirmation( self, to: str, participant_name: str, magic_link_url: str, exchange_name: str, exchange_description: str | None, budget_amount: float, gift_exchange_date: str, ) -> dict[str, Any]: """Send registration confirmation email with magic link. Args: to: Participant email address participant_name: Name of the participant magic_link_url: Full URL with magic token exchange_name: Name of the exchange exchange_description: Description of the exchange (optional) budget_amount: Budget amount gift_exchange_date: Date of gift exchange Returns: Response from email service """ if self.dev_mode: logger.info(f"DEV MODE: Magic link URL: {magic_link_url}") subject = f"Welcome to {exchange_name}!" description_html = "" if exchange_description: description_html = f"{exchange_description}
" html_body = f"""You've successfully registered for the Secret Santa exchange.
{description_html}Click the link below to access your participant dashboard:
This link will expire in 1 hour, but you can always request a new one.
Happy gifting!
""" return self.send_email(to=to, subject=subject, html_body=html_body)