11 KiB
11 KiB
Mail List Manager
A complete containerized mailing list management system with web interface, REST API, MySQL database, and Postfix mail server configured as an SMTP relay through Amazon SES.
Architecture
Multi-Service Architecture:
- Web Frontend - Modern responsive interface for managing lists and members (Port 3000)
- REST API - FastAPI backend with token authentication (Port 8000)
- MySQL Database - Stores lists, members, and subscriptions with real-time integration
- Postfix Mail Server - SMTP server with native MySQL integration for dynamic list expansion (Port 25)
- Amazon SES - Outbound mail relay for reliable delivery
Key Features:
- Real-time list updates (no Postfix reload needed)
- Native Postfix MySQL queries for instant list expansion
- Token-based API authentication
- Sender whitelisting for authorized board members
- Private Docker network for security
- Complete web-based management interface
Quick Start
-
Copy the environment template:
cp .env.example .env -
Edit
.envwith your credentials:# SES Credentials (Required) SES_USER=your_ses_access_key SES_PASS=your_ses_secret_key # MySQL Credentials (Required) MYSQL_ROOT_PASSWORD=secure_root_password MYSQL_PASSWORD=secure_maillist_password # API Authentication (Required) API_TOKEN=your_secure_api_token # Optional: SMTP configuration (defaults to EU West 2) SMTP_HOST=email-smtp.eu-west-2.amazonaws.com SMTP_PORT=587 -
Build and start all services:
sudo docker-compose up --build -
Access the web interface:
http://localhost:3000Enter your API_TOKEN to authenticate and start managing lists!
-
Alternative: Use the REST API directly:
# Health check curl http://localhost:8000/health # Get all lists (requires authentication) curl -H "Authorization: Bearer your_api_token" http://localhost:8000/lists
Services
Web Interface (Port 3000)
- Modern, responsive interface for managing mailing lists and members
- Token-based authentication
- Real-time updates and feedback
- See
web/README.mdfor details
REST API (Port 8000)
- Complete CRUD operations for lists, members, and subscriptions
- Bearer token authentication
- Interactive documentation at http://localhost:8000/docs
- See
api/README.mdfor API reference
MySQL Database (Internal Only)
- Stores all mailing list and member data
- Automatically initialized with schema
- Native Postfix integration for real-time queries
- See
database/README.mdfor schema and management
Postfix Mail Server (Port 25)
- Accepts mail for lists.sasalliance.org
- Queries MySQL for list expansion
- Relays through Amazon SES
- Sender whitelist: @sasalliance.org domain
- Changes take effect immediately (no restart needed)
Configuration
Managing Lists and Members
Via Web Interface (Recommended):
- Open http://localhost:3000
- Enter your API token
- Use the intuitive interface to:
- Create/edit/delete mailing lists
- Add/edit/remove members
- Manage subscriptions with toggle switches
- View member counts and list details
Via REST API:
# Create a new list
curl -X POST http://localhost:8000/lists \
-H "Authorization: Bearer your_token" \
-H "Content-Type: application/json" \
-d '{
"list_name": "Developers",
"list_email": "dev@lists.sasalliance.org",
"description": "Developer discussions",
"active": true
}'
# Add a new member
curl -X POST http://localhost:8000/members \
-H "Authorization: Bearer your_token" \
-H "Content-Type: application/json" \
-d '{
"name": "John Doe",
"email": "john@example.com",
"active": true
}'
# Subscribe member to list
curl -X POST http://localhost:8000/subscriptions \
-H "Authorization: Bearer your_token" \
-H "Content-Type: application/json" \
-d '{
"list_email": "dev@lists.sasalliance.org",
"member_email": "john@example.com"
}'
Via Direct Database Access:
# Connect to MySQL
docker-compose exec mysql mysql -u maillist -p maillist
# Run SQL queries (see database/README.md for examples)
Mail Server Configuration
Mail Server Configuration
The Postfix mail server is configured for:
- Hostname:
lists.sasalliance.org(accepts mail for this domain) - Origin Domain:
sasalliance.org - SES Region: EU West 2 (configurable via
SMTP_HOST) - Sender Whitelist: @sasalliance.org (only authorized board members can send to lists)
- Dynamic Lists: Postfix queries MySQL in real-time for list members
Testing Mail Delivery
# From inside Postfix container
docker-compose exec postfix bash
echo "Test message" | mail -s "Test Subject" community@lists.sasalliance.org
# Check delivery logs
docker-compose logs -f postfix | grep -E "(sent|bounced|deferred)"
# Verify MySQL query works
docker-compose exec postfix postmap -q "community@lists.sasalliance.org" \
mysql:/etc/postfix/mysql_virtual_alias_maps.cf
Security
- Environment Variables: All credentials stored in
.env(git-ignored) - Private Network: MySQL communicates with Postfix/API on internal Docker network only
- Token Authentication: API requires Bearer token for all write operations
- Sender Whitelist: Only @sasalliance.org addresses can send to mailing lists
- TLS Encryption: Enforced for SES relay connections
- File Permissions: SASL password files have restricted permissions (600)
- No External MySQL: Database is not exposed to host (no port mapping)
Development
Project Structure
├── docker-compose.yaml # Multi-service orchestration
├── .env # Environment configuration (not in git)
├── web/ # Web frontend (Nginx + HTML/CSS/JS)
│ ├── index.html
│ ├── static/
│ │ ├── css/style.css
│ │ └── js/
│ │ ├── api.js
│ │ ├── ui.js
│ │ └── app.js
│ ├── nginx.conf
│ └── Dockerfile
├── api/ # REST API (FastAPI)
│ ├── main.py
│ ├── requirements.txt
│ └── Dockerfile
├── database/ # MySQL schema and docs
│ ├── schema.sql
│ └── README.md
├── postfix/ # Mail server configuration
│ ├── Dockerfile
│ ├── entrypoint.sh
│ ├── main.cf.template
│ ├── mysql_virtual_alias_maps.cf
│ ├── sasl_passwd.template
│ └── sender_access
└── .github/
└── copilot-instructions.md # AI agent guidance
Environment Variables
SES_USER: AWS SES access key IDSES_PASS: AWS SES secret access keySMTP_HOST: SMTP server hostname (default: email-smtp.eu-west-2.amazonaws.com)SMTP_PORT: SMTP server port (default: 587)MYSQL_ROOT_PASSWORD: MySQL root passwordMYSQL_DATABASE: Database name (default: maillist)MYSQL_USER: MySQL user (default: maillist)MYSQL_PASSWORD: MySQL user passwordAPI_TOKEN: Bearer token for API authentication
Service Dependencies
mysql (healthcheck) → postfix, api → web
- MySQL must be healthy before Postfix/API start
- Web frontend depends on API being available
- All services communicate on private Docker network
Debugging
Monitor services:
# View all service logs
docker-compose logs -f
# View specific service
docker-compose logs -f postfix
docker-compose logs -f api
docker-compose logs -f web
docker-compose logs -f mysql
# Filter for mail delivery status
docker-compose logs postfix | grep -E "(sent|bounced|deferred)"
# Check Postfix queue
docker-compose exec postfix postqueue -p
# Test MySQL connectivity from Postfix
docker-compose exec postfix postmap -q "community@lists.sasalliance.org" \
mysql:/etc/postfix/mysql_virtual_alias_maps.cf
# Check API health
curl http://localhost:8000/health
# Access MySQL directly
docker-compose exec mysql mysql -u maillist -p maillist
Development Workflow
- Make changes to code or configuration
- Rebuild affected service:
sudo docker-compose up --build -d web # Just web frontend sudo docker-compose up --build -d api # Just API sudo docker-compose up --build -d postfix # Just mail server - Check logs for errors:
docker-compose logs -f [service-name] - Test changes via web interface or API
Common Tasks
Reset database completely:
docker-compose down -v # Removes volumes
docker-compose up -d # Reinitializes from schema.sql
Update API token:
- Edit
.envand changeAPI_TOKEN - Restart API:
docker-compose restart api - Use new token in web interface
Add authorized sender domain:
- Edit
postfix/sender_access - Add line:
newdomain.com OK - Rebuild:
docker-compose up --build -d postfix
How It Works
- User manages lists via web interface (http://localhost:3000)
- Web frontend makes authenticated API calls to FastAPI backend
- API updates MySQL database with new lists/members/subscriptions
- Email arrives at Postfix for
someone@lists.sasalliance.org - Postfix queries MySQL in real-time using native MySQL support
- MySQL returns comma-separated list of active member emails
- Postfix expands the alias and delivers to all members via SES
- Changes take effect immediately - no restart or reload needed!
Features
- ✅ Real-time Updates - Changes to lists/members take effect immediately
- ✅ Native MySQL Integration - Postfix queries database directly (no scripts)
- ✅ Web Interface - Modern, responsive UI for easy management
- ✅ REST API - Complete programmatic access with token auth
- ✅ Sender Whitelist - Only authorized domains can send to lists
- ✅ SES Integration - Reliable email delivery through Amazon SES
- ✅ Bounce Handling - Automatic tracking and management of email bounces via SNS
- ✅ Secure - Private Docker network, token auth, environment-based credentials
- ✅ Flexible - Manage via web, API, or direct database access
- ✅ Scalable - Database-driven architecture supports many lists and members
Documentation
- Web Interface: See
web/README.mdfor frontend features and usage - REST API: See
api/README.mdfor complete API reference - Database: See
database/README.mdfor schema and SQL examples - Bounce Handling: See
BOUNCE_HANDLING_SETUP.mdfor SNS configuration - AI Agents: See
.github/copilot-instructions.mdfor development guidance
Roadmap
- Web frontend for mailing list management
- SQL database backend for member storage
- Dynamic configuration with native Postfix MySQL
- Multi-service Docker Compose architecture
- REST API with authentication
- Sender whitelist for authorized domains
- Bounce handling with SES SNS integration
- Email verification workflow for new members
- Subscription confirmation (double opt-in)
- List archive functionality
- Unsubscribe links in emails
- Rate limiting and anti-spam measures
- HTTPS support for web interface
- Admin roles and permissions
License
MIT License - see LICENSE file for details.