""" Email Service - Send notification emails Uses Resend API (can be swapped for SendGrid, AWS SES, etc.) """ import os import httpx from typing import Optional import logging logger = logging.getLogger(__name__) # Email configuration RESEND_API_KEY = os.getenv("RESEND_API_KEY") # FROM_EMAIL must be from a verified domain in Resend # User has verified: ai20insights.tech FROM_EMAIL = os.getenv("FROM_EMAIL", "insights@ai20insights.tech") APP_URL = os.getenv("APP_URL", "https://datavision-ai-datavision.hf.space") async def send_insight_email( to_email: str, title: str, body: str, chart_payload: Optional[dict] = None, workspace_id: str = None ): """Send insight notification email via Resend API or SMTP fallback""" html_content = render_insight_email_template(title, body, chart_payload, workspace_id) # 1. Try Resend API first if RESEND_API_KEY is configured if RESEND_API_KEY: logger.info(f"📧 Sending email via Resend API from {FROM_EMAIL} to {to_email}") try: async with httpx.AsyncClient(timeout=15.0) as client: payload = { "from": f"DataVision <{FROM_EMAIL}>", "to": [to_email], "subject": f"📊 DataVision: {title}", "html": html_content, "text": render_plain_text_email(title, body), } response = await client.post( "https://api.resend.com/emails", headers={ "Authorization": f"Bearer {RESEND_API_KEY}", "Content-Type": "application/json" }, json=payload ) if response.status_code == 200: result = response.json() logger.info(f"✅ Email sent via Resend API! ID: {result.get('id', 'unknown')}") return result else: logger.warning(f"📧 Resend API status {response.status_code}: {response.text}") except Exception as e: logger.warning(f"📧 Resend API attempt failed: {e}") # 2. Try SMTP fallback if SMTP env vars are present smtp_host = os.getenv("SMTP_HOST") smtp_user = os.getenv("SMTP_USER") smtp_pass = os.getenv("SMTP_PASSWORD") smtp_port = int(os.getenv("SMTP_PORT", "587")) if smtp_host and smtp_user and smtp_pass: logger.info(f"📧 Attempting SMTP send via {smtp_host}:{smtp_port} to {to_email}") try: import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart msg = MIMEMultipart("alternative") msg["Subject"] = f"📊 DataVision: {title}" msg["From"] = f"DataVision <{smtp_user}>" msg["To"] = to_email part1 = MIMEText(render_plain_text_email(title, body), "plain") part2 = MIMEText(html_content, "html") msg.attach(part1) msg.attach(part2) with smtplib.SMTP(smtp_host, smtp_port, timeout=10) as server: server.starttls() server.login(smtp_user, smtp_pass) server.sendmail(smtp_user, [to_email], msg.as_string()) logger.info(f"✅ Email sent via SMTP to {to_email}!") return {"status": "sent", "provider": "smtp"} except Exception as smtp_err: logger.error(f"❌ SMTP send failed: {smtp_err}") logger.warning("📧 No active email provider (Resend API key or SMTP config). Simulating email send for development/testing.") # Log the email content to console so developers can still see what would have been sent logger.info(f"[SIMULATED EMAIL TO {to_email}]: {title}\n{body[:100]}...") return {"status": "simulated", "provider": "none"} def render_insight_email_template( title: str, body: str, chart_payload: Optional[dict], workspace_id: str ) -> str: """Render ultra-premium HTML email template for insight notification (DataVision Enterprise)""" # Modern Enterprise Gradient (Teal/Emerald) button_gradient = "linear-gradient(135deg, #0d9488 0%, #059669 100%)" # Check if we should link to a specific chat context or general dashboard cta_text = "View Dashboard Insights" if not chart_payload: cta_text = "Discuss with AI Analyst" return f'''