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>
This commit is contained in:
sysops
2026-03-14 11:43:19 +01:00
parent a893084a88
commit d360c9a5ba
68 changed files with 11938 additions and 435 deletions
+205
View File
@@ -0,0 +1,205 @@
# 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.