6.3 KiB
6.3 KiB
Copilot Instructions: Mail List Manager (Postfix + SES + MySQL + API + Web)
Architecture Overview
This is a complete containerized mailing list management system with four services:
- Web Frontend (Port 3000) - Nginx serving modern HTML/CSS/JS interface
- REST API (Port 8000) - FastAPI backend with token authentication
- MySQL Database (Internal) - Stores lists, members, and subscriptions
- Postfix Mail Server (Port 25) - SMTP server with native MySQL integration
Key Architecture Principles:
- Real-time operation: Changes take effect immediately without Postfix reload
- Native integration: Postfix queries MySQL directly (no Python scripts)
- Security-first: Private Docker network, token auth, sender whitelist
- User-friendly: Web interface for easy management by non-technical users
Configuration Pattern
Dynamic Database-Driven Architecture:
- Data Flow: Web UI → REST API → MySQL ← Postfix (real-time queries)
- Credentials: All sensitive data in
.envfile (SES, MySQL, API_TOKEN) - Mail Processing:
- Email arrives for
list@lists.sasalliance.org - Postfix queries MySQL via
mysql_virtual_alias_maps.cf - MySQL returns comma-separated member emails
- Postfix expands alias and delivers via SES
- Email arrives for
- Security: Private Docker network (maillist-internal), sender whitelist (@sasalliance.org)
No Static Configuration:
- Virtual aliases are stored in MySQL database, not config files
- Changes take effect immediately without container restarts
- Postfix caches query results for performance
Key Files and Their Roles
Docker & Environment
docker-compose.yaml: Multi-service orchestration (mysql, postfix, api, web).env: All credentials and configuration (SES, MySQL, API_TOKEN)
Web Frontend (web/)
index.html: Single-page application interfacestatic/js/api.js: API client with authenticationstatic/js/ui.js: UI helpers and modal managementstatic/js/app.js: Main application controllerstatic/css/style.css: Complete styling systemnginx.conf: Nginx configuration for static file serving
REST API (api/)
main.py: FastAPI application with all CRUD endpointsrequirements.txt: Python dependencies (fastapi, mysql-connector-python, etc.)Dockerfile: Python 3.11 container build
Database (database/)
schema.sql: Database initialization with tables and sample data- Tables:
lists,members,list_members(many-to-many junction)
Postfix (postfix/)
main.cf.template: Core Postfix config with MySQL virtual alias mapsmysql_virtual_alias_maps.cf: Native Postfix MySQL query configurationsender_access: Sender whitelist (sasalliance.org domain allowed)sasl_passwd.template: SES authentication templateentrypoint.sh: Container startup with MySQL health check and config generation
Development Workflows
Building and Running:
Always use sudo for Docker commands. Don't bother opening the simple browser UI for Docker as it is not very useful.
sudo docker-compose up --build # Build and start all services
sudo docker-compose logs -f # Monitor all service logs
sudo docker-compose logs -f api # Monitor specific service
Managing Lists and Members:
Via Web Interface (Recommended):
- Open http://localhost:3000
- Enter API_TOKEN from .env file
- Use intuitive interface to manage lists, members, and subscriptions
- Click "Subscriptions" button on any member for toggle-based subscription management
Via REST API:
# Get all lists
curl -H "Authorization: Bearer $API_TOKEN" http://localhost:8000/lists
# Create new list
curl -X POST http://localhost:8000/lists \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"list_name":"Test","list_email":"test@lists.sasalliance.org","active":true}'
# Subscribe member to list
curl -X POST http://localhost:8000/subscriptions \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"list_email":"test@lists.sasalliance.org","member_email":"user@example.com"}'
Via Direct Database Access:
docker-compose exec mysql mysql -u maillist -p maillist
# See database/README.md for SQL examples
Testing Mail Delivery:
# From inside container
docker-compose exec postfix bash
echo "Test message" | mail -s "Subject" community@lists.sasalliance.org
# Check logs for SES relay status
docker-compose logs 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 Considerations
- All credentials stored in
.envfile (git-ignored) - MySQL on private Docker network (no host port mapping)
- API requires Bearer token authentication
- Sender whitelist restricts who can send to lists (@sasalliance.org only)
- SASL password file permissions set to 600 in entrypoint
- TLS encryption enforced for SES relay (
smtp_tls_security_level = encrypt) - Web interface requires token for all operations
Configuration Conventions
- Hostname Pattern:
lists.sasalliance.orgfor mailing lists, origin domainsasalliance.org - Virtual Aliases: Dynamically queried from MySQL database in real-time
- SES Region: EU West 2 (
email-smtp.eu-west-2.amazonaws.com) - Port Mapping: Standard SMTP port 25 exposed to host
- Database Tables:
lists,members,list_members(many-to-many design) - API Port: 8000 for REST API
- Web Port: 3000 for web interface
Common Modifications
Adding Recipients to a List: Use web interface or API - changes are immediate!
Creating New Mailing List:
- Via Web: Click "Add List" button, fill in form
- Via API: POST to /lists endpoint with list details
- Via MySQL: INSERT into lists table
Adding New Member:
- Via Web: Click "Add Member" button in Members tab
- Via API: POST to /members endpoint
- Via MySQL: INSERT into members table
Managing Subscriptions:
- Via Web: Click "Subscriptions" button on member, toggle lists
- Via API: POST/DELETE to /subscriptions endpoint
- Via MySQL: INSERT/DELETE in list_members table
Credential Updates:
- Edit
.envfile with new credentials - Restart affected services:
- SES:
docker-compose restart postfix - MySQL:
docker-compose restart mysql - API:
docker-compose restart api
- SES: