# Email System The email system provides multi-provider support with database-stored templates and comprehensive logging for the Orion platform. ## Overview The email system supports: - **Multiple Providers**: SMTP, SendGrid, Mailgun, Amazon SES - **Multi-language Templates**: EN, FR, DE, LB (stored in database) - **Jinja2 Templating**: Variable interpolation in subjects and bodies - **Email Logging**: Track all sent emails for debugging and compliance - **Debug Mode**: Log emails instead of sending during development ## Configuration ### Environment Variables Add these settings to your `.env` file: ```env # Provider: smtp, sendgrid, mailgun, ses EMAIL_PROVIDER=smtp EMAIL_FROM_ADDRESS=noreply@orion.lu EMAIL_FROM_NAME=Orion EMAIL_REPLY_TO= # Behavior EMAIL_ENABLED=true EMAIL_DEBUG=false # SMTP Settings (when EMAIL_PROVIDER=smtp) SMTP_HOST=smtp.example.com SMTP_PORT=587 SMTP_USER= SMTP_PASSWORD= SMTP_USE_TLS=true SMTP_USE_SSL=false # SendGrid (when EMAIL_PROVIDER=sendgrid) # SENDGRID_API_KEY=SG.your_api_key_here # Mailgun (when EMAIL_PROVIDER=mailgun) # MAILGUN_API_KEY=your_api_key_here # MAILGUN_DOMAIN=mg.yourdomain.com # Amazon SES (when EMAIL_PROVIDER=ses) # AWS_ACCESS_KEY_ID=your_access_key # AWS_SECRET_ACCESS_KEY=your_secret_key # AWS_REGION=eu-west-1 ``` ### Debug Mode Set `EMAIL_DEBUG=true` to log emails instead of sending them. This is useful during development: ```env EMAIL_DEBUG=true ``` Emails will be logged to the console with full details (recipient, subject, body preview). ## Database Models ### EmailTemplate Stores multi-language email templates: | Column | Type | Description | |--------|------|-------------| | id | Integer | Primary key | | code | String(100) | Template identifier (e.g., "signup_welcome") | | language | String(5) | Language code (en, fr, de, lb) | | name | String(255) | Human-readable name | | description | Text | Template purpose | | category | String(50) | AUTH, ORDERS, BILLING, SYSTEM, MARKETING | | subject | String(500) | Email subject (supports Jinja2) | | body_html | Text | HTML body | | body_text | Text | Plain text fallback | | variables | Text | JSON list of expected variables | | is_active | Boolean | Enable/disable template | ### EmailLog Tracks all sent emails: | Column | Type | Description | |--------|------|-------------| | id | Integer | Primary key | | template_code | String(100) | Template used (if any) | | recipient_email | String(255) | Recipient address | | subject | String(500) | Email subject | | status | String(20) | PENDING, SENT, FAILED, DELIVERED, OPENED | | sent_at | DateTime | When email was sent | | error_message | Text | Error details if failed | | provider | String(50) | Provider used (smtp, sendgrid, etc.) | | store_id | Integer | Related store (optional) | | user_id | Integer | Related user (optional) | ## Usage ### Using EmailService ```python from app.services.email_service import EmailService def send_welcome_email(db, user, store): email_service = EmailService(db) email_service.send_template( template_code="signup_welcome", to_email=user.email, to_name=f"{user.first_name} {user.last_name}", language="fr", # Falls back to "en" if not found variables={ "first_name": user.first_name, "merchant_name": store.name, "store_code": store.store_code, "login_url": f"https://orion.lu/store/{store.store_code}/dashboard", "trial_days": 30, "tier_name": "Essential", }, store_id=store.id, user_id=user.id, related_type="signup", ) ``` ### Convenience Function ```python from app.services.email_service import send_email send_email( db=db, template_code="order_confirmation", to_email="customer@example.com", language="en", variables={"order_number": "ORD-001"}, ) ``` ### Sending Raw Emails For one-off emails without templates: ```python email_service = EmailService(db) email_service.send_raw( to_email="user@example.com", subject="Custom Subject", body_html="
Custom message
", body_text="Hello\n\nCustom message", ) ``` ## Email Templates ### Creating Templates Templates use Jinja2 syntax for variable interpolation: ```htmlHello {{ first_name }},
Welcome to {{ merchant_name }}!
``` ### Seeding Templates Run the seed script to populate default templates: ```bash python scripts/seed/seed_email_templates.py ``` This creates templates for: - `signup_welcome` (en, fr, de, lb) ### Available Variables For `signup_welcome`: | Variable | Description | |----------|-------------| | first_name | User's first name | | merchant_name | Store merchant name | | email | User's email address | | store_code | Store code for dashboard URL | | login_url | Direct link to dashboard | | trial_days | Number of trial days | | tier_name | Subscription tier name | ## Provider Setup ### SMTP Standard SMTP configuration: ```env EMAIL_PROVIDER=smtp SMTP_HOST=smtp.gmail.com SMTP_PORT=587 SMTP_USER=your-email@gmail.com SMTP_PASSWORD=your-app-password SMTP_USE_TLS=true ``` ### SendGrid 1. Create account at [sendgrid.com](https://sendgrid.com) 2. Generate API key in Settings > API Keys 3. Configure: ```env EMAIL_PROVIDER=sendgrid SENDGRID_API_KEY=SG.xxxxxxxxxxxxxxxxxxxx ``` 4. Install package: `pip install sendgrid` ### Mailgun 1. Create account at [mailgun.com](https://mailgun.com) 2. Add and verify your domain 3. Get API key from Domain Settings 4. Configure: ```env EMAIL_PROVIDER=mailgun MAILGUN_API_KEY=key-xxxxxxxxxxxxxxxx MAILGUN_DOMAIN=mg.yourdomain.com ``` ### Amazon SES 1. Set up SES in AWS Console 2. Verify sender domain/email 3. Create IAM user with SES permissions 4. Configure: ```env EMAIL_PROVIDER=ses AWS_ACCESS_KEY_ID=AKIAXXXXXXXXXXXXXXXX AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx AWS_REGION=eu-west-1 ``` 5. Install package: `pip install boto3` ## Email Logging All emails are logged to the `email_logs` table. Query examples: ```python # Get failed emails failed = db.query(EmailLog).filter( EmailLog.status == EmailStatus.FAILED.value ).all() # Get emails for a store store_emails = db.query(EmailLog).filter( EmailLog.store_id == store_id ).order_by(EmailLog.created_at.desc()).all() # Get recent signup emails signups = db.query(EmailLog).filter( EmailLog.template_code == "signup_welcome", EmailLog.created_at >= datetime.now() - timedelta(days=7) ).all() ``` ## Language Fallback The system automatically falls back to English if a template isn't available in the requested language: 1. Request template for "de" (German) 2. If not found, try "en" (English) 3. If still not found, return None (log error) ## Testing Run email service tests: ```bash pytest tests/unit/services/test_email_service.py -v ``` Test coverage includes: - Provider abstraction (Debug, SMTP, etc.) - Template rendering with Jinja2 - Language fallback behavior - Email sending success/failure - EmailLog model methods - Template variable handling ## Architecture ``` app/services/email_service.py # Email service with provider abstraction models/database/email.py # EmailTemplate and EmailLog models app/core/config.py # Email configuration settings scripts/seed/seed_email_templates.py # Template seeding script ``` ### Provider Abstraction The system uses a strategy pattern for email providers: ``` EmailProvider (ABC) ├── SMTPProvider ├── SendGridProvider ├── MailgunProvider ├── SESProvider └── DebugProvider ``` Each provider implements the `send()` method with the same signature, making it easy to switch providers via configuration. ## Future Enhancements Planned improvements: 1. **Email Queue**: Background task queue for high-volume sending 2. **Webhook Tracking**: Track deliveries, opens, clicks via provider webhooks 3. **Template Editor**: Admin UI for editing templates 4. **A/B Testing**: Test different email versions 5. **Scheduled Emails**: Send emails at specific times