For senders whose mail body is the receipt (e.g. GoCardless payouts) there is no attachment to file. The new action "Als PDF anhängen + nach <Ordner>" renders the mail like a print from a mail client (header block From/To/Date/Subject, then the content) and replaces it in the target folder with: original content + PDF + the original mail byte-for-byte as Original.eml, so the unaltered original is retained (GoBD). - Rendering with WeasyPrint. Images are fetched by us and inlined as data: URIs; WeasyPrint itself may only read data:. Fetching is restricted to http(s), public addresses (private, loopback, link-local and PDF_BLOCKED_NETWORKS, default 172.0.0.0/12, are refused; redirects are checked hop by hop), with time and size limits. cid: images come from the MIME parts. - The replacement is APPENDed with the original flags and INTERNALDATE, verified in the target via its new Message-ID, and only then is the original deleted. Already converted mails are only moved. - "PDF-Vorschau" in the rule dialog renders the newest matching mail read-only and opens it in the browser. - List/log show 📄 targets; warnings for PDF + move/delete and for PDF + "Hat Anhang = Ja"; log folder search covers PDF targets. - Docker image gets Pango and fonts (~316 MB total). Tested with the real GoCardless mail (3 remote images, rendered both locally and inside the built image) and a simulated IMAP server for success, rejected APPEND, missing copy and already converted mails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
186 lines
6.3 KiB
Markdown
186 lines
6.3 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 |
|
|
| `PDF_BLOCKED_NETWORKS` | `172.0.0.0/12` | „Als PDF anhängen": aus diesen Netzen werden keine Bilder geladen (zusätzlich zu privaten Netzen, die immer gesperrt sind) |
|
|
| `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
|
|
```
|