Claude Code - Backend Implementation Specialist
Add complete FastAPI Todo application with Docker support
1941764 | """ | |
| 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", | |
| ... "<h1>Reset your password</h1>" | |
| ... ) | |
| >>> 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 = """ | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Password Reset</title> | |
| </head> | |
| <body style="margin: 0; padding: 0; font-family: Arial, sans-serif; background-color: #f4f4f4;"> | |
| <table width="100%" cellpadding="0" cellspacing="0" style="background-color: #f4f4f4; padding: 20px;"> | |
| <tr> | |
| <td align="center"> | |
| <table width="600" cellpadding="0" cellspacing="0" style="background-color: #ffffff; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 4px rgba(0,0,0,0.1);"> | |
| <tr> | |
| <td style="background-color: #4F46E5; padding: 30px; text-align: center;"> | |
| <h1 style="color: #ffffff; margin: 0; font-size: 24px;">Password Reset Request</h1> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td style="padding: 40px 30px;"> | |
| <p style="color: #333333; font-size: 16px; line-height: 1.6; margin: 0 0 20px 0;"> | |
| Hello, | |
| </p> | |
| <p style="color: #333333; font-size: 16px; line-height: 1.6; margin: 0 0 20px 0;"> | |
| We received a request to reset the password for your account associated with <strong>{{USER_EMAIL}}</strong>. | |
| </p> | |
| <p style="color: #333333; font-size: 16px; line-height: 1.6; margin: 0 0 30px 0;"> | |
| Click the button below to reset your password. This link will expire in 15 minutes. | |
| </p> | |
| <table width="100%" cellpadding="0" cellspacing="0"> | |
| <tr> | |
| <td align="center"> | |
| <a href="{{RESET_LINK}}" style="display: inline-block; background-color: #4F46E5; color: #ffffff; text-decoration: none; padding: 14px 40px; border-radius: 6px; font-size: 16px; font-weight: bold;">Reset Password</a> | |
| </td> | |
| </tr> | |
| </table> | |
| <p style="color: #666666; font-size: 14px; line-height: 1.6; margin: 30px 0 0 0;"> | |
| If you didn't request a password reset, you can safely ignore this email. Your password will not be changed. | |
| </p> | |
| <p style="color: #666666; font-size: 14px; line-height: 1.6; margin: 20px 0 0 0;"> | |
| If the button doesn't work, copy and paste this link into your browser: | |
| </p> | |
| <p style="color: #4F46E5; font-size: 14px; word-break: break-all; margin: 10px 0 0 0;"> | |
| {{RESET_LINK}} | |
| </p> | |
| </td> | |
| </tr> | |
| <tr> | |
| <td style="background-color: #f8f8f8; padding: 20px 30px; text-align: center; border-top: 1px solid #e0e0e0;"> | |
| <p style="color: #999999; font-size: 12px; margin: 0;"> | |
| © 2026 Todo Application. All rights reserved. | |
| </p> | |
| </td> | |
| </tr> | |
| </table> | |
| </td> | |
| </tr> | |
| </table> | |
| </body> | |
| </html> | |
| """ | |
| # 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 | |
| ) | |