""" Email Service - AWS SES integration for verification and password reset emails. """ import os import secrets from datetime import datetime, timedelta import boto3 from botocore.exceptions import ClientError AWS_REGION = os.getenv("AWS_SES_REGION", "us-east-1") EMAIL_FROM = os.getenv("EMAIL_FROM", "noreply@elmeg.xyz") FRONTEND_URL = os.getenv("FRONTEND_URL", "https://elmeg.xyz") # AWS credentials from environment (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY) # or from IAM role if running on AWS infrastructure def get_ses_client(): """Get boto3 SES client""" return boto3.client('ses', region_name=AWS_REGION) def send_email(to: str, subject: str, html_content: str) -> bool: """Send email via AWS SES""" # Dev mode - just log if not os.getenv("AWS_ACCESS_KEY_ID"): print(f"[EMAIL DEV MODE] To: {to}, Subject: {subject}") print(f"[EMAIL DEV MODE] Content: {html_content[:200]}...") return True try: client = get_ses_client() response = client.send_email( Source=EMAIL_FROM, Destination={'ToAddresses': [to]}, Message={ 'Subject': {'Data': subject, 'Charset': 'UTF-8'}, 'Body': { 'Html': {'Data': html_content, 'Charset': 'UTF-8'} } } ) print(f"Email sent to {to}, MessageId: {response['MessageId']}") return True except ClientError as e: print(f"Failed to send email: {e.response['Error']['Message']}") return False def generate_token() -> str: """Generate a secure random token""" return secrets.token_urlsafe(32) async def send_verification_email(email: str, token: str) -> bool: """Send email verification link""" verify_url = f"{FRONTEND_URL}/verify-email?token={token}" html = f"""

Welcome to Elmeg!

Please verify your email address by clicking the button below:

Verify Email

Or copy this link: {verify_url}

This link expires in 48 hours.

""" return send_email(email, "Verify your Elmeg account", html) async def send_password_reset_email(email: str, token: str) -> bool: """Send password reset link""" reset_url = f"{FRONTEND_URL}/reset-password?token={token}" html = f"""

Password Reset

You requested a password reset. Click below to set a new password:

Reset Password

Or copy this link: {reset_url}

This link expires in 1 hour. If you didn't request this, ignore this email.

""" return send_email(email, "Reset your Elmeg password", html) # Token expiration helpers def get_verification_expiry() -> datetime: """48 hour expiry for email verification""" return datetime.utcnow() + timedelta(hours=48) def get_reset_expiry() -> datetime: """1 hour expiry for password reset""" return datetime.utcnow() + timedelta(hours=1)