Files
imap-mail-filter-service/README.md
T
duffyduckandClaude Opus 5.5 7d03c62d8e automatic DB maintenance: log rotation, orphan cleanup, VACUUM
The existing daily cleanup never effectively ran: it was an interval
job counted from container start (reset on every deploy), it called
asyncio.get_event_loop() from a worker thread, and even when deleting,
SQLite never returns freed pages to the filesystem. Live DB: 4.1 GB,
of which 2.6 GB log and 0.6 GB unused pages.

New maintenance service, scheduled daily at MAINTENANCE_HOUR_UTC:30
and once 5 minutes after every start:
- delete log entries older than LOG_RETENTION_DAYS (30) and keep at
  most LOG_MAX_ENTRIES (200k) newest ones
- delete processed_mails markers of deleted rules/accounts (FKs are
  not enforced by SQLite)
- VACUUM when >25% of the file is unused, only if free disk space
  covers the copy; never runs twice in parallel

GET/POST /api/logs/maintenance expose status and a manual run; the log
menu shows DB size, entry count and last run, with "Datenbank
aufräumen & verkleinern". Removed the dead cleanup_old_logs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 11:04:15 +02:00

185 lines
6.1 KiB
Markdown

# 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
```bash
cp .env.example .env
```
Encryption-Key generieren und in die `.env` eintragen:
```bash
python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
```
Falls `cryptography` noch nicht installiert ist:
```bash
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
```bash
docker-compose up --build
```
Der Service ist dann unter **http://localhost:8080** erreichbar.
### 3. Ohne Docker (lokal)
```bash
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:
```yaml
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 |
| `LOG_RETENTION_DAYS` | `30` | Verarbeitungslog: Einträge älter als N Tage werden gelöscht |
| `LOG_MAX_ENTRIES` | `200000` | Verarbeitungslog: höchstens so viele (neueste) Einträge behalten |
| `MAINTENANCE_HOUR_UTC` | `2` | Stunde (UTC) der täglichen DB-Wartung; läuft zusätzlich 5 Min. nach jedem Start |
| `LOG_VERBOSE` | `false` | Jede Nicht-Übereinstimmung loggen — erzeugt Millionen Einträge, nur zur Fehlersuche |
Die tägliche Wartung löscht alte Log-Einträge, entfernt Verarbeitungsmarker gelöschter Regeln und verkleinert die SQLite-Datei per `VACUUM`, sobald mehr als 25 % ungenutzt sind (nur wenn genug Plattenplatz frei ist). Manuell auslösbar im Log unter ⋯ → „Datenbank aufräumen & verkleinern".
| `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:
```bash
# 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
```bash
source .venv/bin/activate
python -m pytest tests/ -v
```