# Email Service Documentation ## Table of Contents 1. [Overview](#overview) 2. [Architecture](#architecture) 3. [Configuration](#configuration) 4. [API Usage](#api-usage) 5. [Django Email Backend](#django-email-backend) 6. [Testing](#testing) 7. [Management Commands](#management-commands) 8. [Troubleshooting](#troubleshooting) 9. [Best Practices](#best-practices) ## Overview The Email Service is a comprehensive email delivery system built for the Django application. It provides a centralized way to send emails through the ForwardEmail API service, with support for site-specific configurations, Django email backend integration, and comprehensive testing tools. ### Key Features - **Site-specific Configuration**: Different email settings per Django site - **ForwardEmail Integration**: Uses ForwardEmail API for reliable email delivery - **Django Backend Integration**: Drop-in replacement for Django's email backend - **REST API Endpoint**: Send emails via HTTP API - **Comprehensive Testing**: Built-in testing commands and flows - **History Tracking**: All configurations are tracked with pghistory - **Admin Interface**: Easy configuration management through Django admin ## Architecture The email service consists of several key components: ``` apps/email_service/ ├── models.py # EmailConfiguration model ├── services.py # Core EmailService class ├── backends.py # Django email backend implementation ├── admin.py # Django admin configuration ├── urls.py # URL patterns (legacy) └── management/commands/ # Testing and utility commands apps/api/v1/email/ ├── views.py # Centralized API views └── urls.py # API URL patterns ``` ### Component Overview 1. **EmailConfiguration Model**: Stores site-specific email settings 2. **EmailService**: Core service class for sending emails 3. **ForwardEmailBackend**: Django email backend implementation 4. **API Views**: REST API endpoints for email sending 5. **Management Commands**: Testing and utility tools ## Configuration ### Database Configuration Email configurations are stored in the database and managed through the Django admin interface. #### EmailConfiguration Model The [`EmailConfiguration`](../backend/apps/email_service/models.py:8) model stores the following fields: - `api_key`: ForwardEmail API key - `from_email`: Default sender email address - `from_name`: Display name for the sender - `reply_to`: Reply-to email address - `site`: Associated Django site (ForeignKey) - `created_at`: Creation timestamp - `updated_at`: Last update timestamp #### Creating Configuration via Admin 1. Access Django admin at `/admin/` 2. Navigate to "Email Configurations" 3. Click "Add Email Configuration" 4. Fill in the required fields: - **Site**: Select the Django site - **API Key**: Your ForwardEmail API key - **From Name**: Display name (e.g., "ThrillWiki") - **From Email**: Sender email address - **Reply To**: Reply-to email address ### Environment Variables (Fallback) If no database configuration exists, the system falls back to environment variables: ```bash FORWARD_EMAIL_API_KEY=your_api_key_here FORWARD_EMAIL_FROM=noreply@yourdomain.com FORWARD_EMAIL_BASE_URL=https://api.forwardemail.net ``` ### Django Settings Add the email service to your Django settings: ```python # settings.py INSTALLED_APPS = [ # ... other apps 'apps.email_service', ] # ForwardEmail API base URL FORWARD_EMAIL_BASE_URL = 'https://api.forwardemail.net' # Optional: Use custom email backend EMAIL_BACKEND = 'apps.email_service.backends.ForwardEmailBackend' ``` ## API Usage ### REST API Endpoint The email service provides a REST API endpoint for sending emails. #### Endpoint ``` POST /api/v1/email/send/ ``` #### Request Format ```json { "to": "recipient@example.com", "subject": "Email Subject", "text": "Email body text", "from_email": "sender@example.com" // optional } ``` #### Response Format **Success (200 OK):** ```json { "message": "Email sent successfully", "response": { // ForwardEmail API response } } ``` **Error (400 Bad Request):** ```json { "error": "Missing required fields", "required_fields": ["to", "subject", "text"] } ``` **Error (500 Internal Server Error):** ```json { "error": "Failed to send email: [error details]" } ``` #### Example Usage **cURL:** ```bash curl -X POST http://localhost:8000/api/v1/email/send/ \ -H "Content-Type: application/json" \ -d '{ "to": "user@example.com", "subject": "Welcome to ThrillWiki", "text": "Thank you for joining ThrillWiki!" }' ``` **JavaScript (fetch):** ```javascript const response = await fetch('/api/v1/email/send/', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': getCookie('csrftoken') // if CSRF protection enabled }, body: JSON.stringify({ to: 'user@example.com', subject: 'Welcome to ThrillWiki', text: 'Thank you for joining ThrillWiki!' }) }); const result = await response.json(); ``` **Python (requests):** ```python import requests response = requests.post('http://localhost:8000/api/v1/email/send/', json={ 'to': 'user@example.com', 'subject': 'Welcome to ThrillWiki', 'text': 'Thank you for joining ThrillWiki!' }) result = response.json() ``` ### Direct Service Usage You can also use the [`EmailService`](../backend/apps/email_service/services.py:11) class directly in your Python code: ```python from django.contrib.sites.shortcuts import get_current_site from apps.email_service.services import EmailService # In a view or other code def send_welcome_email(request, user_email): site = get_current_site(request) try: response = EmailService.send_email( to=user_email, subject="Welcome to ThrillWiki", text="Thank you for joining ThrillWiki!", site=site ) return response except Exception as e: # Handle error print(f"Failed to send email: {e}") return None ``` ## Django Email Backend The email service includes a custom Django email backend that integrates with Django's built-in email system. ### Configuration To use the custom backend, set it in your Django settings: ```python # settings.py EMAIL_BACKEND = 'apps.email_service.backends.ForwardEmailBackend' ``` ### Usage Once configured, you can use Django's standard email functions: ```python from django.core.mail import send_mail from django.contrib.sites.shortcuts import get_current_site def send_notification(request): site = get_current_site(request) send_mail( subject='Notification', message='This is a notification email.', from_email=None, # Will use site's default recipient_list=['user@example.com'], fail_silently=False, ) ``` ### Backend Features The [`ForwardEmailBackend`](../backend/apps/email_service/backends.py:7) provides: - **Site-aware Configuration**: Automatically uses the correct site's email settings - **Error Handling**: Proper error handling with optional silent failures - **Multiple Recipients**: Handles multiple recipients (sends individually) - **From Email Handling**: Automatic from email resolution ### Backend Limitations - **Single Recipient**: ForwardEmail API sends to one recipient at a time - **Text Only**: Currently supports text emails only (HTML support can be added) - **Site Requirement**: Requires site context for configuration lookup ## Testing The email service includes comprehensive testing tools to verify functionality. ### Management Commands #### Test Email Service Test the core email service functionality: ```bash cd backend && uv run manage.py test_email_service ``` **Options:** - `--to EMAIL`: Recipient email (default: test@thrillwiki.com) - `--api-key KEY`: Override API key - `--from-email EMAIL`: Override from email **Example:** ```bash cd backend && uv run manage.py test_email_service --to user@example.com ``` This command tests: 1. Site configuration setup 2. Direct EmailService usage 3. API endpoint functionality 4. Django email backend #### Test Email Flows Test all application email flows: ```bash cd backend && uv run manage.py test_email_flows ``` This command tests: 1. User registration emails 2. Password change notifications 3. Email change verification 4. Password reset emails ### Manual Testing #### API Endpoint Testing Test the API endpoint directly: ```bash curl -X POST http://localhost:8000/api/v1/email/send/ \ -H "Content-Type: application/json" \ -d '{ "to": "test@example.com", "subject": "Test Email", "text": "This is a test email." }' ``` #### Django Shell Testing Test using Django shell: ```python # cd backend && uv run manage.py shell from django.contrib.sites.models import Site from apps.email_service.services import EmailService site = Site.objects.get_current() response = EmailService.send_email( to="test@example.com", subject="Test from Shell", text="This is a test email from Django shell.", site=site ) print(response) ``` ## Management Commands ### test_email_service **Purpose**: Comprehensive testing of email service functionality **Location**: [`backend/apps/email_service/management/commands/test_email_service.py`](../backend/apps/email_service/management/commands/test_email_service.py:12) **Usage:** ```bash cd backend && uv run manage.py test_email_service [options] ``` **Options:** - `--to EMAIL`: Recipient email address - `--api-key KEY`: ForwardEmail API key override - `--from-email EMAIL`: Sender email override **Tests Performed:** 1. **Site Configuration**: Creates/updates email configuration 2. **Direct Service**: Tests EmailService.send_email() 3. **API Endpoint**: Tests REST API endpoint 4. **Django Backend**: Tests Django email backend ### test_email_flows **Purpose**: Test all application email flows **Location**: [`backend/apps/email_service/management/commands/test_email_flows.py`](../backend/apps/email_service/management/commands/test_email_flows.py:13) **Usage:** ```bash cd backend && uv run manage.py test_email_flows ``` **Tests Performed:** 1. **Registration**: User registration email flow 2. **Password Change**: Password change notification 3. **Email Change**: Email change verification 4. **Password Reset**: Password reset email flow ## Troubleshooting ### Common Issues #### 1. "Email configuration is missing for site" **Cause**: No EmailConfiguration exists for the current site. **Solution:** 1. Access Django admin 2. Create EmailConfiguration for your site 3. Or set environment variables as fallback #### 2. "Failed to send email (Status 401)" **Cause**: Invalid or missing API key. **Solution:** 1. Verify API key in EmailConfiguration 2. Check ForwardEmail account status 3. Ensure API key has proper permissions #### 3. "Could not connect to server" **Cause**: Django development server not running or wrong URL. **Solution:** 1. Start Django server: `cd backend && uv run manage.py runserver` 2. Verify server is running on expected port 3. Check FORWARD_EMAIL_BASE_URL setting #### 4. "Site matching query does not exist" **Cause**: Default site not configured properly. **Solution:** ```python # Django shell from django.contrib.sites.models import Site Site.objects.get_or_create( id=1, defaults={'domain': 'localhost:8000', 'name': 'localhost:8000'} ) ``` ### Debug Mode The EmailService includes debug output. Check console logs for: - Request URL and data - Response status and body - API key (masked) - Site information ### Testing Configuration Verify your configuration: ```bash # Test with specific configuration cd backend && uv run manage.py test_email_service \ --api-key your_api_key \ --from-email noreply@yourdomain.com \ --to test@example.com ``` ### API Response Codes - **200**: Success - **400**: Bad request (missing fields, invalid data) - **401**: Unauthorized (invalid API key) - **500**: Server error (configuration issues, network problems) ## Best Practices ### Security 1. **API Key Protection**: Store API keys securely, never in code 2. **Environment Variables**: Use environment variables for sensitive data 3. **HTTPS**: Always use HTTPS in production 4. **Rate Limiting**: Implement rate limiting for API endpoints ### Configuration Management 1. **Site-Specific Settings**: Use database configuration for multi-site setups 2. **Fallback Configuration**: Always provide environment variable fallbacks 3. **Admin Interface**: Use Django admin for easy configuration management 4. **Configuration Validation**: Test configurations after changes ### Error Handling 1. **Graceful Degradation**: Handle email failures gracefully 2. **Logging**: Log email failures for debugging 3. **User Feedback**: Provide appropriate user feedback 4. **Retry Logic**: Implement retry logic for transient failures ### Performance 1. **Async Processing**: Consider using Celery for email sending 2. **Batch Operations**: Group multiple emails when possible 3. **Connection Pooling**: Reuse HTTP connections when sending multiple emails 4. **Monitoring**: Monitor email delivery rates and failures ### Testing 1. **Automated Tests**: Include email testing in your test suite 2. **Test Environments**: Use test email addresses in development 3. **Integration Tests**: Test complete email flows 4. **Load Testing**: Test email service under load ### Monitoring 1. **Delivery Tracking**: Monitor email delivery success rates 2. **Error Tracking**: Track and alert on email failures 3. **Performance Metrics**: Monitor email sending performance 4. **API Limits**: Monitor ForwardEmail API usage limits ### Example Production Configuration ```python # settings/production.py EMAIL_BACKEND = 'apps.email_service.backends.ForwardEmailBackend' FORWARD_EMAIL_BASE_URL = 'https://api.forwardemail.net' # Use environment variables for sensitive data import os FORWARD_EMAIL_API_KEY = os.environ.get('FORWARD_EMAIL_API_KEY') FORWARD_EMAIL_FROM = os.environ.get('FORWARD_EMAIL_FROM') # Logging configuration LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'email_file': { 'level': 'INFO', 'class': 'logging.FileHandler', 'filename': '/var/log/django/email.log', }, }, 'loggers': { 'apps.email_service': { 'handlers': ['email_file'], 'level': 'INFO', 'propagate': True, }, }, } ``` This documentation provides comprehensive coverage of the email service functionality, from basic setup to advanced usage patterns and troubleshooting.