""" Email service for sending password reset emails via Gmail SMTP. This module provides utilities for: - Sending HTML emails via Gmail SMTP - Rendering password reset email templates - Managing SMTP connection and configuration """ import os import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from typing import Optional from pathlib import Path # SMTP configuration from environment variables SMTP_HOST = os.getenv("SMTP_HOST", "smtp.gmail.com") SMTP_PORT = int(os.getenv("SMTP_PORT", "587")) SMTP_USERNAME = os.getenv("SMTP_USERNAME", "") SMTP_PASSWORD = os.getenv("SMTP_PASSWORD", "") SMTP_USE_TLS = os.getenv("SMTP_USE_TLS", "true").lower() == "true" EMAIL_FROM = os.getenv("EMAIL_FROM", SMTP_USERNAME) EMAIL_FROM_NAME = os.getenv("EMAIL_FROM_NAME", "Todo Application") FRONTEND_URL = os.getenv("FRONTEND_URL", "http://localhost:3000") def send_email(to_email: str, subject: str, html_content: str) -> bool: """ Send an HTML email via Gmail SMTP. Args: to_email: Recipient email address subject: Email subject line html_content: HTML content of the email Returns: True if email sent successfully, False otherwise Example: >>> success = send_email( ... "user@example.com", ... "Password Reset", ... "

Reset your password

" ... ) >>> print(success) True """ try: # Create message message = MIMEMultipart("alternative") message["Subject"] = subject message["From"] = f"{EMAIL_FROM_NAME} <{EMAIL_FROM}>" message["To"] = to_email # Attach HTML content html_part = MIMEText(html_content, "html") message.attach(html_part) # Connect to SMTP server and send email with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as server: if SMTP_USE_TLS: server.starttls() # Login if credentials provided if SMTP_USERNAME and SMTP_PASSWORD: server.login(SMTP_USERNAME, SMTP_PASSWORD) # Send email server.send_message(message) return True except Exception as e: print(f"Failed to send email to {to_email}: {str(e)}") return False def render_password_reset_email(reset_link: str, user_email: str) -> str: """ Render the password reset email HTML template. Args: reset_link: Full URL for password reset user_email: User's email address Returns: Rendered HTML email content Example: >>> html = render_password_reset_email( ... "http://localhost:3000/reset-password?token=abc123", ... "user@example.com" ... ) """ # Try to load template from file template_path = Path(__file__).parent.parent.parent / "templates" / "password_reset_email.html" if template_path.exists(): with open(template_path, "r", encoding="utf-8") as f: template = f.read() else: # Fallback inline template template = """ Password Reset

Password Reset Request

Hello,

We received a request to reset the password for your account associated with {{USER_EMAIL}}.

Click the button below to reset your password. This link will expire in 15 minutes.

Reset Password

If you didn't request a password reset, you can safely ignore this email. Your password will not be changed.

If the button doesn't work, copy and paste this link into your browser:

{{RESET_LINK}}

© 2026 Todo Application. All rights reserved.

""" # Replace placeholders html = template.replace("{{RESET_LINK}}", reset_link) html = html.replace("{{USER_EMAIL}}", user_email) return html def send_password_reset_email(to_email: str, reset_token: str) -> bool: """ Send a password reset email to the user. Args: to_email: User's email address reset_token: Password reset token Returns: True if email sent successfully, False otherwise Example: >>> success = send_password_reset_email("user@example.com", "abc123def456") >>> print(success) True """ # Construct reset link reset_link = f"{FRONTEND_URL}/reset-password?token={reset_token}" # Render email HTML html_content = render_password_reset_email(reset_link, to_email) # Send email return send_email( to_email=to_email, subject="Reset Your Password - Todo Application", html_content=html_content )