Files
archivmail/features/PROJ-3-import-imap.md
T
sysops d360c9a5ba feat(PROJ-17): Admin Dashboard Systemauslastung immer anzeigen
- 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>
2026-03-14 11:43:19 +01:00

206 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.