5.6 KiB
MailAdmin MVP
Erste Version einer Admin-Webapp für dein DMS/SES/S3/SQS/DynamoDB-Mail-System.
Die App ist bewusst node-orientiert gebaut: Sie läuft auf node1, node2, usw. und entdeckt die Domains dynamisch aus dem lokalen Docker Mailserver. Dadurch ist sie geeignet für dein Szenario, bei dem Domains neu hinzukommen oder zwischen Nodes umziehen.
Was diese Version bereits kann
- Login mit eigenem Admin-User
- PostgreSQL als Admin-Datenbank
- dynamische Domain-Erkennung über
docker exec mailserver setup email list - Domains pro Node speichern:
node1,node2, ... - Mailboxen anzeigen
- Mailbox anlegen
- Mailbox löschen
- Passwort zurücksetzen
- Speicherverbrauch pro Inbox scannen
- Forward- und Auto-Reply-Regeln in DynamoDB bearbeiten
- Blocklisten in DynamoDB bearbeiten
- Audit Log für Admin-Aktionen
- Frontend wird vom Backend direkt ausgeliefert
- Docker Compose Deployment auf dem Mailserver
Warum dynamische Domains berücksichtigt sind
Die Domainliste ist nicht hart codiert. Beim Start und bei jedem manuellen Resync liest die App die DMS-Accounts aus:
docker exec mailserver setup email list
Daraus werden Domains und Mailboxen abgeleitet und in Postgres gespiegelt.
Wenn eine Domain später auf einen anderen Node umzieht, passiert Folgendes:
- auf dem alten Node werden die lokalen Mailboxen nach einem Resync als
missing_on_nodemarkiert - auf dem neuen Node wird dieselbe Domain beim Resync als
activemitcurrent_node=node2erkannt - die App-Codebasis bleibt identisch; nur
.envunterscheidetNODE_NAME=node1oderNODE_NAME=node2
Struktur
mailadmin-mvp/
backend/
src/
server.ts Express App, API, static frontend
config.ts zentrale Konfiguration über Env Vars
db.ts Postgres Pool + Migration + Initial Admin
routes/
auth.ts Login/Logout/Me
domains.ts Domains dynamisch vom DMS syncen/anzeigen
mailboxes.ts Mailbox CRUD, Usage, Rules, Blocklist
audit.ts Audit Log API
services/
dms.ts DMS Docker Integration + Usage Scan
sync.ts Sync DMS -> Postgres
dynamodb.ts email-rules + email-blocked-senders
audit.ts Audit helper
utils/
email.ts Email/domain/local-part helpers
shell.ts sicherer execFile wrapper
migrations/001_init.sql PostgreSQL Schema
Dockerfile Backend + Frontend Image
frontend/
index.html Single Page App
app.js UI Logik ohne Build-Step
styles.css einfache Admin-Oberfläche
deploy/
caddy/ Caddy-Hinweise
scripts/ Snippet-Hilfe für update-caddy-certs.sh
docker-compose.yml App + Postgres
.env.example Beispiel-Konfiguration
Installation auf node1
cd /home/aknuth/git/email-amazon
unzip mailadmin-mvp.zip
cd mailadmin-mvp
cp .env.example .env
nano .env
Wichtige Werte in .env:
MAILADMIN_DB_PASSWORD=ein-sicheres-passwort
MAILADMIN_JWT_SECRET=sehr-langes-random-secret
MAILADMIN_ADMIN_EMAIL=admin@bayarea-cc.com
MAILADMIN_ADMIN_PASSWORD=StartPassword123!
NODE_NAME=node1
NODE_HOSTNAME=node1.email-srvr.com
DMS_CONTAINER=mailserver
AWS_REGION=us-east-2
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
Start:
docker compose up -d --build
Logs:
docker logs -f mailadmin
Installation auf node2
Gleicher Code, andere .env:
NODE_NAME=node2
NODE_HOSTNAME=node2.email-srvr.com
Die Domainliste wird auch dort aus dem lokalen DMS gelesen.
Caddy: mailadmin.{DOMAIN}
Dein bestehendes update-caddy-certs.sh generiert bereits dynamisch Domain-Blöcke. Ergänze im Domain-Loop zusätzlich diesen Block:
# MailAdmin UI
mailadmin.DOMAIN_HERE {
encode gzip
reverse_proxy mailadmin:3000
}
Im Script sieht das ungefähr so aus:
OUTPUT="${OUTPUT}# MailAdmin UI\n"
OUTPUT="${OUTPUT}mailadmin.${domain} {\n"
OUTPUT="${OUTPUT} encode gzip\n"
OUTPUT="${OUTPUT} reverse_proxy mailadmin:3000\n"
OUTPUT="${OUTPUT}}\n\n"
Danach:
cd /home/aknuth/git/email-amazon/caddy
./update-caddy-certs.sh
docker exec caddy caddy reload --config /etc/caddy/Caddyfile
Wichtige Sicherheitshinweise
Diese MVP-Version mountet /var/run/docker.sock, damit der Backend-Container docker exec mailserver ... ausführen kann. Das ist praktisch, aber mächtig. Für die nächste Version wäre ein kleiner dedizierter Local-Agent mit begrenzten Operationen sicherer.
Das Admin-Passwort wird nur beim ersten Start erstellt. Wenn du MAILADMIN_ADMIN_PASSWORD später änderst, wird der vorhandene User nicht überschrieben. Das ist Absicht.
API Kurzüberblick
POST /api/auth/login
POST /api/auth/logout
GET /api/auth/me
GET /api/domains?resync=true
POST /api/domains/resync
GET /api/mailboxes?domain=example.com
POST /api/mailboxes
DELETE /api/mailboxes/:email
POST /api/mailboxes/:email/password
POST /api/mailboxes/usage/rescan
GET /api/mailboxes/:email/rules
PUT /api/mailboxes/:email/rules
GET /api/mailboxes/:email/blocklist
PUT /api/mailboxes/:email/blocklist
GET /api/audit
Nächste sinnvolle Ausbaustufen
- Domain-Detailseite mit DNS/SES/S3/SQS/Caddy Status
- Admin-User Verwaltung im UI
- Quotas pro Mailbox
- Soft-Delete mit Retention statt hartem Löschen
- Outbound Reporting über Postfix Logs und später SES Events
- Node-Migration Workflow: Domain gezielt von node1 auf node2 markieren und prüfen