feat: add email system with multi-provider support

Implements a comprehensive email system with:
- Multi-provider support (SMTP, SendGrid, Mailgun, Amazon SES)
- Database-stored templates with i18n (EN, FR, DE, LB)
- Jinja2 template rendering with variable interpolation
- Email logging for debugging and compliance
- Debug mode for development (logs instead of sending)
- Welcome email integration in signup flow

New files:
- models/database/email.py: EmailTemplate and EmailLog models
- app/services/email_service.py: Provider abstraction and service
- scripts/seed_email_templates.py: Template seeding script
- tests/unit/services/test_email_service.py: 28 unit tests
- docs/features/email-system.md: Complete documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-27 21:05:50 +01:00
parent 98d082699c
commit 64fd8b5194
11 changed files with 2540 additions and 0 deletions

View File

@@ -22,12 +22,14 @@ from app.exceptions import (
ResourceNotFoundException,
ValidationException,
)
from app.services.email_service import EmailService
from app.services.stripe_service import stripe_service
from middleware.auth import AuthManager
from models.database.company import Company
from models.database.subscription import (
SubscriptionStatus,
TierCode,
TIER_LIMITS,
VendorSubscription,
)
from models.database.user import User
@@ -467,6 +469,62 @@ class PlatformSignupService:
return setup_intent.client_secret, stripe_customer_id
# =========================================================================
# Welcome Email
# =========================================================================
def send_welcome_email(
self,
db: Session,
user: User,
vendor: Vendor,
tier_code: str,
language: str = "fr",
) -> None:
"""
Send welcome email to new vendor.
Args:
db: Database session
user: User who signed up
vendor: Vendor that was created
tier_code: Selected tier code
language: Language for email (default: French)
"""
try:
# Get tier name
tier_enum = TierCode(tier_code)
tier_name = TIER_LIMITS.get(tier_enum, {}).get("name", tier_code.title())
# Build login URL
login_url = f"https://{settings.platform_domain}/vendor/{vendor.vendor_code}/dashboard"
email_service = EmailService(db)
email_service.send_template(
template_code="signup_welcome",
language=language,
to_email=user.email,
to_name=f"{user.first_name} {user.last_name}",
variables={
"first_name": user.first_name,
"company_name": vendor.name,
"email": user.email,
"vendor_code": vendor.vendor_code,
"login_url": login_url,
"trial_days": settings.stripe_trial_days,
"tier_name": tier_name,
},
vendor_id=vendor.id,
user_id=user.id,
related_type="signup",
)
logger.info(f"Welcome email sent to {user.email}")
except Exception as e:
# Log error but don't fail signup
logger.error(f"Failed to send welcome email to {user.email}: {e}")
# =========================================================================
# Signup Completion
# =========================================================================
@@ -543,6 +601,15 @@ class PlatformSignupService:
else datetime.now(UTC) + timedelta(days=30)
)
# Get user for welcome email
user_id = session.get("user_id")
user = db.query(User).filter(User.id == user_id).first() if user_id else None
# Send welcome email
if user and vendor:
tier_code = session.get("tier_code", TierCode.ESSENTIAL.value)
self.send_welcome_email(db, user, vendor, tier_code)
# Clean up session
self.delete_session(session_id)