- fix: "Log leeren" without account filter used a plain DELETE over millions of rows. That held the sqlite write lock long enough that the scheduler's own log writes started failing with "database is locked", and the request itself eventually 500'd back as HTML "Internal Server Error" — which the frontend then tried to JSON.parse, producing the popup the user reported. Now uses DROP+CREATE for full clears (near-instant) and wal_checkpoint to reclaim space; per-account clear still uses a normal DELETE. - Raise sqlite busy_timeout from 10s to 60s so a legitimately slow writer no longer starves smaller ones. - retry-failed: single bulk DELETE with mail_uid IN (...) per account instead of one query per uid. - Both endpoints now return proper HTTPException(500, ...) with detail instead of letting the exception bubble as plain text. - Frontend: parseResponse() reads text first, then tries JSON. Both clearLogs() and retryFailed() surface the actual server message instead of crashing on non-JSON responses. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
IMAP Mail Filter Service
Ein Docker-basierter Service, der E-Mail-Filterregeln unabhängig vom Mail-Client ausführt. Egal ob du Evolution, Thunderbird oder einen anderen Client nutzt — die Filter laufen einfach weiter.
Features
- Mehrere IMAP-Konten verwalten
- Filterregeln mit flexiblen Bedingungen (Von, An, Betreff, Text)
- Matching: enthält, exakt, Regex — jeweils mit Negierung
- Aktionen: Verschieben, Weiterleiten, Löschen, Als gelesen markieren
- Web-UI zur Verwaltung von Konten und Regeln
- YAML Import/Export für portable Konfiguration
- Passwort-Verschlüsselung mit Fernet
- Konfigurierbares Polling-Intervall pro Konto
Schnellstart
1. .env erstellen
cp .env.example .env
Encryption-Key generieren und in die .env eintragen:
python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
Falls cryptography noch nicht installiert ist:
pip install cryptography
python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
Den ausgegebenen Key in die .env eintragen:
ENCRYPTION_KEY=dein-generierter-key-hier
2. Mit Docker starten
docker-compose up --build
Der Service ist dann unter http://localhost:8080 erreichbar.
3. Ohne Docker (lokal)
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --reload
Erreichbar unter http://localhost:8000.
Benutzung
Web-UI
- Dashboard (
/) — Übersicht aller Konten mit Status und Quick-Actions - Konten (
/accounts) — IMAP-Konten anlegen, bearbeiten, Verbindungstest - Filter (
/filters) — Filterregeln pro Konto erstellen und verwalten - YAML (
/yaml) — Konfiguration exportieren und importieren
REST-API
| Endpunkt | Beschreibung |
|---|---|
GET /api/accounts/ |
Alle Konten auflisten |
POST /api/accounts/ |
Neues Konto anlegen |
PUT /api/accounts/{id} |
Konto bearbeiten |
DELETE /api/accounts/{id} |
Konto löschen |
POST /api/accounts/{id}/test |
IMAP-Verbindungstest |
POST /api/accounts/{id}/poll-now |
Sofort nach neuen Mails prüfen |
GET /api/filters/account/{id} |
Filterregeln eines Kontos |
POST /api/filters/ |
Neue Filterregel |
PUT /api/filters/{id} |
Filterregel bearbeiten |
DELETE /api/filters/{id} |
Filterregel löschen |
GET /api/yaml/export |
Konfiguration als YAML |
POST /api/yaml/import |
YAML-Datei importieren |
YAML-Konfiguration
Filterregeln können auch direkt als YAML definiert werden. Passwörter lassen sich als Umgebungsvariablen referenzieren:
accounts:
- name: "Arbeit"
imap_host: "imap.example.com"
imap_port: 993
use_ssl: true
username: "user@example.com"
password: "${WORK_IMAP_PW}"
poll_interval_seconds: 120
filters:
- name: "Newsletter sortieren"
priority: 10
source_folder: "INBOX"
stop_processing: true
conditions:
- field: "from"
match_type: "contains"
value: "newsletter@"
actions:
- action_type: "move"
parameter: "Newsletter"
Beim Start wird config/filters.yaml automatisch importiert (konfigurierbar via YAML_SYNC_ON_STARTUP).
Umgebungsvariablen
| Variable | Standard | Beschreibung |
|---|---|---|
ENCRYPTION_KEY |
(leer) | Fernet-Key für Passwort-Verschlüsselung |
LOG_LEVEL |
INFO |
Log-Level (DEBUG, INFO, WARNING, ERROR) |
YAML_SYNC_ON_STARTUP |
true |
YAML-Datei beim Start importieren |
DATABASE_URL |
sqlite:///data/mailfilter.db |
Datenbank-Pfad |
Datenbank-Migrationen
Die Datenbank wird automatisch beim Start per Alembic migriert — Konten und Filterregeln bleiben bei Updates erhalten.
Falls du lokal entwickelst und das Schema änderst:
# Neue Migration erstellen (nach Änderung an db_models.py)
.venv/bin/alembic revision --autogenerate -m "beschreibung der änderung"
# Migration anwenden
.venv/bin/alembic upgrade head
# Migrationsstatus prüfen
.venv/bin/alembic current
Im Docker passiert das automatisch beim Container-Start.
Projektstruktur
├── app/
│ ├── main.py # FastAPI App + Web-Routen
│ ├── config.py # Konfiguration via Umgebungsvariablen
│ ├── database.py # SQLAlchemy Setup
│ ├── models/db_models.py # Datenbank-Modelle
│ ├── schemas/schemas.py # API Request/Response Schemas
│ ├── routers/ # REST-API Endpunkte
│ ├── services/
│ │ ├── imap_client.py # IMAP-Verbindung und Mail-Aktionen
│ │ ├── filter_engine.py # Regelauswertung
│ │ ├── scheduler.py # Polling-Scheduler
│ │ ├── yaml_service.py # YAML Import/Export
│ │ └── encryption.py # Passwort-Verschlüsselung
│ ├── templates/ # Jinja2 HTML-Templates
│ └── static/ # CSS + JS
├── alembic/ # Datenbank-Migrationen
│ ├── env.py
│ └── versions/ # Migrations-Skripte
├── config/filters.yaml # YAML-Filterkonfiguration
├── data/ # SQLite-Datenbank (Docker Volume)
├── docker-compose.yml
├── Dockerfile
└── requirements.txt
Tests
source .venv/bin/activate
python -m pytest tests/ -v