Files
archivmail/features/PROJ-3-import-imap.md
T
sysops 7c29ee88bd docs: vollständige README, PROJ-2 Web-Upload, PROJ-19 Mailpiler-Migration
README.md:
- Vollständige Dokumentation aller implementierten Funktionen
- Konfigurationsreferenz, Installation, Systemd, REST-API-Übersicht
- In-Progress-Features klar gekennzeichnet

PROJ-2 (EML/MBOX Web-Upload):
- POST /api/admin/upload – Multipart-Upload mit Hintergrund-Job
- GET /api/admin/upload/{jobID}/progress – Polling
- Admin-Tab "Import" mit Drag-and-Drop, Fortschrittsbalken, Abschlussbericht

PROJ-19 (Mailpiler Migration):
- archivmail import-piler mit Methoden: pilerexport | direct | auto
- Direct: AES-256-CBC + zlib mit defensiven Fallbacks
- pilerexport: Wrapper um mailpilers Export-Tool

Status-Updates: PROJ-3, PROJ-4, PROJ-6, PROJ-7, PROJ-10, PROJ-11 → Deployed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 09:23:34 +01:00

9.2 KiB
Raw Blame History

PROJ-3: E-Mail-Import: IMAP-Verbindung

Status: Deployed

Created: 2026-03-12 Last Updated: 2026-03-17

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.