Move 39 documentation files from top-level docs/ into each module's docs/ folder, accessible via symlinks from docs/modules/. Create data-model.md files for 10 modules with full schema documentation. Replace originals with redirect stubs. Remove empty guide stubs. Modules migrated: tenancy, billing, loyalty, marketplace, orders, messaging, cms, catalog, inventory, hosting, prospecting. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
309 lines
12 KiB
Markdown
309 lines
12 KiB
Markdown
# Email Settings Implementation
|
|
|
|
This document describes the technical implementation of the email settings system for both store and platform (admin) configurations.
|
|
|
|
## Architecture Overview
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────┐
|
|
│ Email System Architecture │
|
|
├─────────────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌──────────────────┐ ┌──────────────────┐ │
|
|
│ │ Platform Email │ │ Store Email │ │
|
|
│ │ (Admin/Billing)│ │ (Customer-facing) │ │
|
|
│ └────────┬─────────┘ └────────┬─────────┘ │
|
|
│ │ │ │
|
|
│ ▼ ▼ │
|
|
│ ┌──────────────────┐ ┌──────────────────┐ │
|
|
│ │ get_platform_ │ │ get_store_ │ │
|
|
│ │ email_config(db) │ │ provider() │ │
|
|
│ └────────┬─────────┘ └────────┬─────────┘ │
|
|
│ │ │ │
|
|
│ ▼ ▼ │
|
|
│ ┌──────────────────┐ ┌──────────────────┐ │
|
|
│ │ AdminSettings DB │ │StoreEmailSettings│ │
|
|
│ │ (.env fallback)│ │ (per store) │ │
|
|
│ └────────┬─────────┘ └────────┬─────────┘ │
|
|
│ │ │ │
|
|
│ └───────────┬───────────────┘ │
|
|
│ ▼ │
|
|
│ ┌──────────────────┐ │
|
|
│ │ EmailService │ │
|
|
│ │ send_raw() │ │
|
|
│ └────────┬─────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌──────────────────┐ │
|
|
│ │ Email Providers │ │
|
|
│ │ SMTP/SG/MG/SES │ │
|
|
│ └──────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## Database Models
|
|
|
|
### StoreEmailSettings
|
|
|
|
```python
|
|
# models/database/store_email_settings.py
|
|
|
|
class StoreEmailSettings(Base):
|
|
__tablename__ = "store_email_settings"
|
|
|
|
id: int
|
|
store_id: int # FK to stores.id (one-to-one)
|
|
|
|
# Sender Identity
|
|
from_email: str
|
|
from_name: str
|
|
reply_to_email: str | None
|
|
|
|
# Signature
|
|
signature_text: str | None
|
|
signature_html: str | None
|
|
|
|
# Provider
|
|
provider: str = "smtp" # smtp, sendgrid, mailgun, ses
|
|
|
|
# SMTP Settings
|
|
smtp_host: str | None
|
|
smtp_port: int = 587
|
|
smtp_username: str | None
|
|
smtp_password: str | None
|
|
smtp_use_tls: bool = True
|
|
smtp_use_ssl: bool = False
|
|
|
|
# SendGrid
|
|
sendgrid_api_key: str | None
|
|
|
|
# Mailgun
|
|
mailgun_api_key: str | None
|
|
mailgun_domain: str | None
|
|
|
|
# SES
|
|
ses_access_key_id: str | None
|
|
ses_secret_access_key: str | None
|
|
ses_region: str = "eu-west-1"
|
|
|
|
# Status
|
|
is_configured: bool = False
|
|
is_verified: bool = False
|
|
last_verified_at: datetime | None
|
|
verification_error: str | None
|
|
```
|
|
|
|
### Admin Settings (Platform Email)
|
|
|
|
Platform email settings are stored in the generic `admin_settings` table with category="email":
|
|
|
|
```python
|
|
# Keys stored in admin_settings table
|
|
EMAIL_SETTING_KEYS = {
|
|
"email_provider",
|
|
"email_from_address",
|
|
"email_from_name",
|
|
"email_reply_to",
|
|
"smtp_host",
|
|
"smtp_port",
|
|
"smtp_user",
|
|
"smtp_password",
|
|
"smtp_use_tls",
|
|
"smtp_use_ssl",
|
|
"sendgrid_api_key",
|
|
"mailgun_api_key",
|
|
"mailgun_domain",
|
|
"aws_access_key_id",
|
|
"aws_secret_access_key",
|
|
"aws_region",
|
|
"email_enabled",
|
|
"email_debug",
|
|
}
|
|
```
|
|
|
|
## API Endpoints
|
|
|
|
### Store Email Settings
|
|
|
|
| Endpoint | Method | Description |
|
|
|----------|--------|-------------|
|
|
| `/api/v1/store/email-settings` | GET | Get current email settings |
|
|
| `/api/v1/store/email-settings` | PUT | Create/update email settings |
|
|
| `/api/v1/store/email-settings` | DELETE | Delete email settings |
|
|
| `/api/v1/store/email-settings/status` | GET | Get configuration status |
|
|
| `/api/v1/store/email-settings/providers` | GET | Get available providers for tier |
|
|
| `/api/v1/store/email-settings/verify` | POST | Send test email |
|
|
|
|
### Admin Email Settings
|
|
|
|
| Endpoint | Method | Description |
|
|
|----------|--------|-------------|
|
|
| `/api/v1/admin/settings/email/status` | GET | Get effective email config |
|
|
| `/api/v1/admin/settings/email/settings` | PUT | Update email settings in DB |
|
|
| `/api/v1/admin/settings/email/settings` | DELETE | Reset to .env defaults |
|
|
| `/api/v1/admin/settings/email/test` | POST | Send test email |
|
|
|
|
## Services
|
|
|
|
### StoreEmailSettingsService
|
|
|
|
Location: `app/services/store_email_settings_service.py`
|
|
|
|
Key methods:
|
|
- `get_settings(store_id)` - Get settings for a store
|
|
- `create_or_update(store_id, data, current_tier)` - Create/update settings
|
|
- `delete(store_id)` - Delete settings
|
|
- `verify_settings(store_id, test_email)` - Send test email
|
|
- `get_available_providers(tier)` - Get providers for subscription tier
|
|
|
|
### EmailService Integration
|
|
|
|
The EmailService (`app/services/email_service.py`) uses:
|
|
|
|
1. **Platform Config**: `get_platform_email_config(db)` checks database first, then .env
|
|
2. **Store Config**: `get_store_provider(settings)` creates provider from StoreEmailSettings
|
|
3. **Provider Selection**: `send_raw()` uses store provider when `store_id` provided and `is_platform_email=False`
|
|
|
|
```python
|
|
# EmailService.send_raw() flow
|
|
def send_raw(self, to_email, subject, body_html, store_id=None, is_platform_email=False):
|
|
if store_id and not is_platform_email:
|
|
# Use store's email provider
|
|
store_settings = self._get_store_email_settings(store_id)
|
|
if store_settings and store_settings.is_configured:
|
|
provider = get_store_provider(store_settings)
|
|
else:
|
|
# Use platform provider (DB config > .env)
|
|
provider = self.provider # Set in __init__ via get_platform_provider(db)
|
|
```
|
|
|
|
## Tier-Based Features
|
|
|
|
### Premium Provider Gating
|
|
|
|
Premium providers (SendGrid, Mailgun, SES) are gated to Business+ tiers:
|
|
|
|
```python
|
|
PREMIUM_EMAIL_PROVIDERS = {EmailProvider.SENDGRID, EmailProvider.MAILGUN, EmailProvider.SES}
|
|
PREMIUM_TIERS = {TierCode.BUSINESS, TierCode.ENTERPRISE}
|
|
|
|
def create_or_update(self, store_id, data, current_tier):
|
|
provider = data.get("provider", "smtp")
|
|
if provider in [p.value for p in PREMIUM_EMAIL_PROVIDERS]:
|
|
if current_tier not in PREMIUM_TIERS:
|
|
raise AuthorizationException(...)
|
|
```
|
|
|
|
### White-Label Branding
|
|
|
|
Emails include "Powered by Orion" footer for non-whitelabel tiers:
|
|
|
|
```python
|
|
WHITELABEL_TIERS = {"business", "enterprise"}
|
|
|
|
POWERED_BY_FOOTER_HTML = """
|
|
<div style="margin-top: 30px; ...">
|
|
<p>Powered by <a href="https://orion.lu">Orion</a></p>
|
|
</div>
|
|
"""
|
|
|
|
def _inject_powered_by_footer(self, body_html, store_id):
|
|
tier = self._get_store_tier(store_id)
|
|
if tier and tier.lower() in WHITELABEL_TIERS:
|
|
return body_html # No footer for business/enterprise
|
|
return body_html.replace("</body>", f"{POWERED_BY_FOOTER_HTML}</body>")
|
|
```
|
|
|
|
## Configuration Priority
|
|
|
|
### Platform Email
|
|
|
|
1. **Database** (admin_settings table) - Highest priority
|
|
2. **Environment Variables** (.env) - Fallback
|
|
|
|
```python
|
|
def get_platform_email_config(db: Session) -> dict:
|
|
def get_db_setting(key: str) -> str | None:
|
|
setting = db.query(AdminSetting).filter(AdminSetting.key == key).first()
|
|
return setting.value if setting else None
|
|
|
|
# Check DB first, fallback to .env
|
|
db_provider = get_db_setting("email_provider")
|
|
config["provider"] = db_provider if db_provider else settings.email_provider
|
|
...
|
|
```
|
|
|
|
### Store Email
|
|
|
|
Stores have their own dedicated settings table with no fallback - they must configure their own email.
|
|
|
|
## Frontend Components
|
|
|
|
### Store Settings Page
|
|
|
|
- **Location**: `app/templates/store/settings.html`, `static/store/js/settings.js`
|
|
- **Alpine.js State**: `emailSettings`, `emailForm`, `hasEmailChanges`
|
|
- **Methods**: `loadEmailSettings()`, `saveEmailSettings()`, `sendTestEmail()`
|
|
|
|
### Admin Settings Page
|
|
|
|
- **Location**: `app/templates/admin/settings.html`, `static/admin/js/settings.js`
|
|
- **Alpine.js State**: `emailSettings`, `emailForm`, `emailEditMode`
|
|
- **Methods**: `loadEmailSettings()`, `saveEmailSettings()`, `resetEmailSettings()`, `sendTestEmail()`
|
|
|
|
### Warning Banner
|
|
|
|
Shows until email is configured:
|
|
|
|
```html
|
|
<!-- app/templates/shared/macros/feature_gate.html -->
|
|
{% macro email_settings_warning() %}
|
|
<div x-data="emailSettingsWarning()" x-show="showWarning">
|
|
Configure email settings to send emails to customers.
|
|
</div>
|
|
{% endmacro %}
|
|
```
|
|
|
|
## Testing
|
|
|
|
### Unit Tests
|
|
|
|
Location: `tests/unit/services/test_store_email_settings_service.py`
|
|
|
|
Tests:
|
|
- Read operations (get_settings, get_status, is_configured)
|
|
- Write operations (create_or_update, delete)
|
|
- Tier validation (premium providers)
|
|
- Verification (mock SMTP)
|
|
- Provider availability
|
|
|
|
### Integration Tests
|
|
|
|
Locations:
|
|
- `tests/integration/api/v1/store/test_email_settings.py`
|
|
- `tests/integration/api/v1/admin/test_email_settings.py`
|
|
|
|
Tests:
|
|
- CRUD operations via API
|
|
- Authentication/authorization
|
|
- Validation errors
|
|
- Status endpoints
|
|
|
|
## Files Modified/Created
|
|
|
|
### New Files
|
|
- `models/database/store_email_settings.py` - Model
|
|
- `alembic/versions/v0a1b2c3d4e5_add_store_email_settings.py` - Migration
|
|
- `app/services/store_email_settings_service.py` - Service
|
|
- `app/api/v1/store/email_settings.py` - API endpoints
|
|
- `scripts/seed/install.py` - Installation wizard
|
|
|
|
### Modified Files
|
|
- `app/services/email_service.py` - Added platform config, store providers
|
|
- `app/api/v1/admin/settings.py` - Added email endpoints
|
|
- `app/templates/admin/settings.html` - Email tab
|
|
- `app/templates/store/settings.html` - Email tab
|
|
- `static/admin/js/settings.js` - Email JS
|
|
- `static/store/js/settings.js` - Email JS
|
|
- `static/store/js/init-alpine.js` - Warning banner component
|