d360c9a5ba
- Systemauslastungs-Sektion wird immer gerendert (nicht nur bei Erfolg) - Fehlermeldung wenn /api/admin/system/stats nicht erreichbar ist - Feature-Status auf In Review gesetzt Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
206 lines
9.2 KiB
Markdown
206 lines
9.2 KiB
Markdown
# PROJ-3: E-Mail-Import: IMAP-Verbindung
|
||
|
||
## Status: In Review
|
||
**Created:** 2026-03-12
|
||
**Last Updated:** 2026-03-14
|
||
|
||
## Dependencies
|
||
- Requires: PROJ-1 (Authentifizierung) – nur Admins verwalten IMAP-Verbindungen
|
||
- Requires: PROJ-5 (Speicherung & Indexierung) – importierte E-Mails werden gespeichert
|
||
|
||
## User Stories
|
||
- Als Admin oder User möchte ich einen IMAP-Server konfigurieren (Host, Port, Zugangsdaten), damit das System E-Mails von dort abholen kann.
|
||
- Als Admin oder User möchte ich beim ersten Verbinden alle vorhandenen E-Mails eines Postfachs importieren (Initial-Import).
|
||
- Als System möchte ich beim Verbinden automatisch Junk- und Trash-Ordner per IMAP erkennen und ausschließen, damit kein Spam ins Archiv gelangt. Alle anderen Ordner werden importiert.
|
||
- Als Admin oder User möchte ich den Verbindungsstatus sehen (verbunden, Fehler, letzter Sync), damit ich Probleme erkennen kann.
|
||
- Als System möchte ich die IMAP-Verbindung testen bevor sie gespeichert wird, damit Konfigurationsfehler früh erkannt werden.
|
||
- Als Admin möchte ich alle IMAP-Konten aller Nutzer sehen und verwalten können.
|
||
- Als User möchte ich nur meine eigenen IMAP-Konten sehen und verwalten (keine fremden).
|
||
|
||
## Acceptance Criteria
|
||
- [ ] Konfigurationsformular: Host, Port, TLS/SSL, Benutzername, Passwort
|
||
- [ ] Verbindungstest vor dem Speichern (Timeout: 10 Sekunden)
|
||
- [ ] Passwörter werden verschlüsselt in der Datenbank gespeichert (nie im Klartext)
|
||
- [ ] Automatische Ordner-Erkennung via IMAP LIST-EXTENDED (RFC 6154 Special-Use Flags: `\Junk`, `\Trash`)
|
||
- [ ] Fallback auf bekannte Ordnernamen wenn Special-Use Flags fehlen: `Junk`, `Spam`, `Trash`, `Deleted Items`, `Deleted Messages`, `Papierkorb`
|
||
- [ ] Erkannte Ausschluss-Ordner werden dem Admin vor dem Import angezeigt (mit Option zur manuellen Korrektur)
|
||
- [ ] Initial-Import: **alle Ordner außer Junk/Trash** – Ordnerstruktur wird verworfen, nur E-Mail-Inhalt archiviert
|
||
- [ ] Fortschrittsanzeige während Initial-Import
|
||
- [ ] Duplikate (Message-ID) werden übersprungen
|
||
- [ ] Verbindungsstatus-Übersicht im Admin-Bereich
|
||
|
||
## Edge Cases
|
||
- IMAP-Server nicht erreichbar → Fehlermeldung mit Retry-Option
|
||
- Falsche Zugangsdaten → klare Fehlermeldung
|
||
- IMAP-Server trennt Verbindung während Import → automatischer Reconnect
|
||
- Postfach mit 200.000+ E-Mails → paginierter Import, kein Speicher-Overflow
|
||
- OAuth2/XOAUTH2 für Gmail/Outlook → als spätere Erweiterung markiert (nicht MVP)
|
||
|
||
## Technical Requirements
|
||
- **Verbindungsmodi:**
|
||
- `SSL/TLS` – direkte TLS-Verbindung ab dem ersten Byte (Port 993)
|
||
- `STARTTLS` – Verbindung startet unverschlüsselt, wird per STARTTLS-Befehl auf TLS hochgestuft (Port 143)
|
||
- `None` – unverschlüsselt, nur für lokale/Testumgebungen
|
||
- IMAP IDLE-Unterstützung für Echtzeit-Benachrichtigungen (optional)
|
||
- Zugangsdaten AES-256-GCM verschlüsselt in der DB (gleicher Key wie Mail-Store)
|
||
|
||
---
|
||
## Tech Design (Solution Architect)
|
||
|
||
### Komponentenstruktur
|
||
|
||
**Next.js Frontend (Admin-Bereich):**
|
||
```
|
||
/admin/imap
|
||
├── IMAP-Verbindungsliste
|
||
│ └── VerbindungsCard (pro Konto)
|
||
│ ├── Name, Host, Status (OK / Fehler)
|
||
│ ├── Letzter Import + Anzahl importierter Mails
|
||
│ └── Aktionen: Bearbeiten / Löschen / Import starten
|
||
├── Verbindung-Formular (anlegen / bearbeiten)
|
||
│ ├── Host, Port, TLS-Auswahl
|
||
│ ├── Benutzername, Passwort
|
||
│ └── [Verbindung testen] Button
|
||
├── Ordner-Vorschau (nach erfolgreichem Test)
|
||
│ ├── Automatisch erkannte Ausschlüsse (Junk/Trash) – markiert
|
||
│ └── Manuelle Korrektur möglich (Checkbox pro Ordner)
|
||
└── Import-Fortschrittsanzeige
|
||
├── Fortschrittsbalken (X von Y E-Mails)
|
||
├── Aktueller Status
|
||
└── Abschlussbericht (importiert / übersprungen / Fehler)
|
||
```
|
||
|
||
**Go Backend:**
|
||
```
|
||
IMAP-Dienst
|
||
├── Verbindungsverwaltung
|
||
│ ├── POST /api/admin/imap ← Verbindung anlegen
|
||
│ ├── POST /api/admin/imap/test ← testen + Ordner-Erkennung
|
||
│ ├── GET /api/admin/imap ← alle Verbindungen auflisten
|
||
│ └── DELETE /api/admin/imap/{id} ← Verbindung löschen
|
||
│
|
||
├── IMAP-Client
|
||
│ ├── TLS/SSL + STARTTLS Handler
|
||
│ ├── Ordner-Erkenner (Special-Use + Fallback)
|
||
│ ├── SELECT + FETCH UID-basiert
|
||
│ └── Reconnect-Handler
|
||
│
|
||
├── Import-Worker (Hintergrund-Goroutine)
|
||
│ ├── Batch-weise FETCH (50 Mails pro Batch)
|
||
│ ├── Duplikat-Check (Message-ID)
|
||
│ ├── → Storage Coordinator (PROJ-5)
|
||
│ └── Fortschritt in DB schreiben
|
||
│
|
||
└── GET /api/admin/imap/{id}/progress ← Polling durch Frontend
|
||
```
|
||
|
||
### Ordner-Erkennungslogik
|
||
|
||
```
|
||
IMAP-Verbindung aufgebaut
|
||
│
|
||
▼
|
||
LIST-EXTENDED "" "*" RETURN (SPECIAL-USE) ← RFC 6154
|
||
│
|
||
├── \Junk gefunden? → Ordner ausschließen
|
||
├── \Trash gefunden? → Ordner ausschließen
|
||
└── Flags nicht unterstützt? → Fallback:
|
||
Ordnernamen prüfen (case-insensitive):
|
||
"junk", "spam", "trash", "deleted items",
|
||
"deleted messages", "papierkorb"
|
||
→ übereinstimmende Ordner ausschließen
|
||
│
|
||
▼
|
||
Ordnerliste mit Markierungen an Frontend:
|
||
INBOX ✓ (wird importiert)
|
||
Sent ✓ (wird importiert)
|
||
Drafts ✓ (wird importiert)
|
||
Junk [\Junk erkannt] ✗ (ausgeschlossen)
|
||
Trash [\ Trash erkannt] ✗ (ausgeschlossen)
|
||
│
|
||
▼
|
||
Admin kann Ausschlüsse manuell korrigieren
|
||
→ Speichern → Import starten
|
||
```
|
||
|
||
### Importfluss
|
||
|
||
```
|
||
Import-Worker startet
|
||
│
|
||
▼
|
||
Für jeden nicht ausgeschlossenen Ordner:
|
||
│
|
||
├── IMAP UID SEARCH ALL → alle UIDs
|
||
│
|
||
└── Batch-weise (50 UIDs):
|
||
├── IMAP FETCH RFC822
|
||
├── Message-ID Duplikat? → überspringen
|
||
└── Storage Coordinator (PROJ-5)
|
||
→ verschlüsseln + speichern + indexieren
|
||
│
|
||
▼
|
||
Fortschritt in DB → Frontend pollt alle 2 Sek.
|
||
```
|
||
|
||
### Datenmodell
|
||
|
||
**Tabelle `imap_accounts`:**
|
||
|
||
| Feld | Beschreibung |
|
||
|------|-------------|
|
||
| `id` | Interne ID |
|
||
| `name` | Bezeichnung |
|
||
| `host` | IMAP-Hostname |
|
||
| `port` | Port (143 / 993) |
|
||
| `tls` | `ssl` / `starttls` / `none` |
|
||
| `username` | IMAP-Benutzername |
|
||
| `password_enc` | AES-256-GCM verschlüsseltes Passwort |
|
||
| `excluded_folders` | JSON-Array ausgeschlossener Ordner |
|
||
| `last_import_at` | Zeitpunkt des letzten Imports |
|
||
| `last_import_count` | Anzahl importierter Mails |
|
||
| `status` | `idle` / `running` / `error` |
|
||
| `error_msg` | Letzter Fehler |
|
||
|
||
### Technische Entscheidungen
|
||
|
||
| Entscheidung | Begründung |
|
||
|---|---|
|
||
| **RFC 6154 Special-Use Flags** | Standard-Weg für Junk/Trash-Erkennung – funktioniert bei Dovecot, Exchange, Gmail |
|
||
| **Fallback auf Ordnernamen** | Ältere oder nicht-standardkonforme Server kennen Special-Use nicht – Fallback deckt die gängigsten Namen ab |
|
||
| **Admin-Korrektur möglich** | Automatik kann irren – Admin sieht die Erkennungsergebnisse und kann vor dem Import eingreifen |
|
||
| **Nur Ausschlüsse konfigurieren** | Einfacher als Whitelist: alle Ordner importieren außer den erkannten Ausreißern |
|
||
| **UID-basierter Fetch** | Bei Reconnect kann genau dort weitergemacht werden wo abgebrochen wurde |
|
||
| **Batch-Größe 50** | Balance zwischen RAM-Verbrauch und IMAP-Roundtrips |
|
||
|
||
### Abhängigkeiten
|
||
|
||
| Paket | Zweck |
|
||
|---|---|
|
||
| `github.com/emersion/go-imap` | IMAP-Client (RFC 6154 LIST-EXTENDED, TLS, FETCH) |
|
||
|
||
## Implementation Notes (2026-03-14)
|
||
|
||
### Go Backend
|
||
- **`internal/imap/store.go`**: DB CRUD for `imap_accounts` table with AES-256-GCM password encryption. Auto-migrates table on startup. Index on `owner` column.
|
||
- **`internal/imap/client.go`**: IMAP client wrapper using `go-imap/v2` (beta.8). Supports SSL/STARTTLS/plaintext. Folder detection via RFC 6154 special-use flags with name-based fallback.
|
||
- **`internal/imap/importer.go`**: Background import worker. Fetches all UIDs per folder, processes in batches of 50, stores via `storage.Store.Save()` (SHA256 dedup), indexes via `index.Indexer.IndexSync()`. Progress written to DB for frontend polling.
|
||
- **`internal/api/server.go`**: 6 new IMAP endpoints (`GET/POST /api/imap`, `DELETE /api/imap/{id}`, `POST /api/imap/test`, `POST /api/imap/{id}/import`, `GET /api/imap/{id}/progress`). All auth-protected, ownership enforced (admin sees all, user sees own).
|
||
- **`cmd/archivmail/main.go`**: Wires IMAP store and importer into API server.
|
||
|
||
### Frontend
|
||
- **`src/app/imap/page.tsx`**: Full IMAP management page with account cards, add dialog (with connection test and folder preview), progress polling, delete confirmation.
|
||
- **`src/lib/api.ts`**: IMAP types and 6 API functions.
|
||
- **`src/components/navbar.tsx`**: Added "IMAP Import" link for all roles.
|
||
|
||
### Deviations from spec
|
||
- Routes use `/api/imap` (not `/api/admin/imap`) since all authenticated users can manage their own IMAP accounts.
|
||
- Using `go-imap/v2` beta.8 (latest available) instead of beta.5.
|
||
- IMAP page at `/imap` (not `/admin/imap`) to match the route pattern.
|
||
|
||
## QA Test Results
|
||
_To be added by /qa_
|
||
|
||
## Deployment
|
||
Deployed to 192.168.1.131 on 2026-03-14. Both `archivmail` and `archivmail-web` services restarted and active. Database table `imap_accounts` auto-created with index.
|