Replace all ~1,086 occurrences of Wizamart/wizamart/WIZAMART/WizaMart with Orion/orion/ORION across 184 files. This includes database identifiers, email addresses, domain references, R2 bucket names, DNS prefixes, encryption salt, Celery app name, config defaults, Docker configs, CI configs, documentation, seed data, and templates. Renames homepage-wizamart.html template to homepage-orion.html. Fixes duplicate file_pattern key in api.yaml architecture rule. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
332 lines
7.9 KiB
Markdown
332 lines
7.9 KiB
Markdown
# 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="<h1>Hello</h1><p>Custom message</p>",
|
|
body_text="Hello\n\nCustom message",
|
|
)
|
|
```
|
|
|
|
## Email Templates
|
|
|
|
### Creating Templates
|
|
|
|
Templates use Jinja2 syntax for variable interpolation:
|
|
|
|
```html
|
|
<p>Hello {{ first_name }},</p>
|
|
<p>Welcome to {{ merchant_name }}!</p>
|
|
```
|
|
|
|
### 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
|