"""
Signup Confirmation Email Service
Uses Resend API to send branded DataVision confirmation emails
"""
import os
import httpx
import secrets
from typing import Optional
from datetime import datetime, timedelta
import logging
import json
from pathlib import Path
logger = logging.getLogger(__name__)
# Email configuration
RESEND_API_KEY = os.getenv("RESEND_API_KEY")
FROM_EMAIL = os.getenv("FROM_EMAIL", "insights@ai20insights.tech")
APP_URL = os.getenv("APP_URL", "https://killerkumar-ai-business-analyst.hf.space")
# Storage for pending confirmations
CONFIRMATIONS_DIR = Path(__file__).parent.parent / "storage" / "pending_confirmations"
CONFIRMATIONS_DIR.mkdir(parents=True, exist_ok=True)
async def send_confirmation_email(email: str, user_id: str) -> dict:
"""Send DataVision branded confirmation email using Resend API"""
if not RESEND_API_KEY:
error_msg = "RESEND_API_KEY not configured"
logger.error(error_msg)
return {"success": False, "error": error_msg}
# Generate confirmation token
token = secrets.token_urlsafe(32)
# Store pending confirmation
confirmation_data = {
"user_id": user_id,
"email": email,
"token": token,
"created_at": datetime.utcnow().isoformat(),
"expires_at": (datetime.utcnow() + timedelta(hours=24)).isoformat()
}
token_file = CONFIRMATIONS_DIR / f"{token}.json"
with open(token_file, 'w') as f:
json.dump(confirmation_data, f)
# Build confirmation URL - points to frontend which calls API
confirmation_url = f"{APP_URL}/auth/confirm?token={token}"
# HTML email template
html_content = f'''
Confirm Your DataVision Account
DataVision
Autonomous Data Intelligence Platform
|
Welcome to DataVision! 👋
Thank you for signing up. Click the button below to confirm your email address and get started with autonomous data intelligence.
This link will expire in 24 hours. If you didn't create an account with DataVision, you can safely ignore this email.
If the button doesn't work, copy and paste this URL into your browser:
{confirmation_url}
|
|
© 2024 DataVision — Autonomous Data Intelligence Platform
|
|
'''
try:
async with httpx.AsyncClient(timeout=30.0) as client:
payload = {
"from": f"DataVision <{FROM_EMAIL}>",
"to": [email],
"subject": "Confirm Your DataVision Account",
"html": html_content,
"text": f"Welcome to DataVision!\n\nClick this link to confirm your email: {confirmation_url}\n\nThis link expires in 24 hours.",
}
response = await client.post(
"https://api.resend.com/emails",
headers={
"Authorization": f"Bearer {RESEND_API_KEY}",
"Content-Type": "application/json"
},
json=payload
)
logger.info(f"📧 Confirmation email response: {response.status_code}")
if response.status_code != 200:
error_detail = response.text
logger.error(f"📧 Resend API error: {error_detail}")
return {"success": False, "error": error_detail}
result = response.json()
logger.info(f"✅ Confirmation email sent to {email}! ID: {result.get('id', 'unknown')}")
return {"success": True, "email_id": result.get('id')}
except Exception as e:
logger.error(f"❌ Failed to send confirmation email: {str(e)}")
return {"success": False, "error": str(e)}
async def verify_confirmation_token(token: str) -> dict:
"""Verify a confirmation token and return user info"""
token_file = CONFIRMATIONS_DIR / f"{token}.json"
if not token_file.exists():
return {"success": False, "error": "Invalid or expired token"}
try:
with open(token_file, 'r') as f:
data = json.load(f)
# Check expiration
expires_at = datetime.fromisoformat(data["expires_at"])
if datetime.utcnow() > expires_at:
token_file.unlink() # Delete expired token
return {"success": False, "error": "Token has expired"}
# Token is valid - delete it (one-time use)
token_file.unlink()
return {
"success": True,
"user_id": data["user_id"],
"email": data["email"]
}
except Exception as e:
logger.error(f"Error verifying token: {e}")
return {"success": False, "error": str(e)}