# 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.