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
+195
View File
@@ -0,0 +1,195 @@
# PROJ-15: CLI Import & Export
## Status: In Progress
**Created:** 2026-03-13
**Last Updated:** 2026-03-13
## Dependencies
- Requires: PROJ-5 (Speicherung & Indexierung) Import nutzt Storage Coordinator
- Requires: PROJ-1 (Authentifizierung) CLI läuft als Systembenutzer `archivmail`, kein Web-Login
## Hinweis
Die CLI läuft direkt auf dem Server als Systembenutzer `archivmail` kein Web-Login, kein API-Key. Zugriff über den gleichen Storage Coordinator wie der Daemon. Gedacht für automatisierte Skripte, Cron-Jobs und administrative Bulk-Operationen.
## User Stories
- Als Systemadministrator möchte ich EML/MBOX-Dateien per CLI importieren, damit ich Bulk-Importe skriptbasiert automatisieren kann.
- Als Systemadministrator möchte ich E-Mails per CLI exportieren (EML/MBOX), damit ich Sicherungen oder Migrationen durchführen kann.
- Als Systemadministrator möchte ich Import/Export mit Pfadangabe starten, damit ich Quell- und Zielverzeichnisse flexibel festlegen kann.
- Als System möchte ich Import-Fortschritt und Ergebnis auf stdout ausgeben, damit Skripte den Status auswerten können.
## Acceptance Criteria
### Import
- [ ] `archivmail import --file /pfad/zu/datei.eml` einzelne EML importieren
- [ ] `archivmail import --file /pfad/zu/archiv.mbox` MBOX importieren
- [ ] `archivmail import --dir /pfad/zum/verzeichnis/` alle EML-Dateien in einem Verzeichnis importieren (rekursiv optional: `--recursive`)
- [ ] Fortschrittsausgabe auf stdout (eine Zeile pro Mail oder Fortschrittsbalken)
- [ ] Exit-Code 0 bei Erfolg, 1 bei Fehler
- [ ] Duplikate werden übersprungen (gleiche Message-ID), kein Fehler
- [ ] `--dry-run` Flag: zeigt was importiert würde ohne tatsächlich zu speichern
### Export
- [ ] `archivmail export --out /pfad/ziel/` alle Mails als EML-Dateien exportieren
- [ ] `archivmail export --out /pfad/archiv.mbox` alle Mails als MBOX exportieren
- [ ] `archivmail export --from alice@firma.de --out /pfad/` Filter nach Absender
- [ ] `archivmail export --date-from 2024-01-01 --date-to 2024-12-31 --out /pfad/` Filter nach Datum
- [ ] `archivmail export --query "Rechnung" --out /pfad/` Filter per Volltext-Suche (Xapian)
- [ ] Exportierte Mails werden entschlüsselt (Klartext EML auf Disk)
- [ ] `--format eml` (Standard) oder `--format mbox`
### Allgemein
- [ ] CLI läuft als Systembenutzer `archivmail` liest Key aus `/etc/archivmail/keyfile`
- [ ] Fehler werden auf stderr ausgegeben
- [ ] `archivmail help` zeigt Übersicht aller Befehle
- [ ] `archivmail version` zeigt Version
## Edge Cases
- Verzeichnis beim Import enthält keine EML-Dateien → Hinweis + Exit-Code 0
- Zieldatei beim Export bereits vorhanden → Fehler mit `--force` Flag zum Überschreiben
- Kein Lese-/Schreibrecht auf Pfad → klare Fehlermeldung auf stderr
- Import unterbrochen (Ctrl+C) → partiell importierte Mails werden gespeichert, kein Rollback (Archiv ist append-only)
- Export bei leerem Archiv → leeres Verzeichnis / leere MBOX, Exit-Code 0
## Technical Requirements
- CLI ist Teil desselben Go-Binaries (`archivmail`) Subcommands via `archivmail <command>`
- Zugriff auf Storage Coordinator direkt (kein HTTP-Umweg über den laufenden Daemon)
- Key-Datei muss lesbar sein (`/etc/archivmail/keyfile`, `chmod 400`, Owner `archivmail`)
- Kann parallel zum laufenden Daemon betrieben werden (Xapian WritableDatabase: Lock beachten)
- Strukturierte Ausgabe optional: `--json` Flag für maschinenlesbare Ausgabe
---
## Tech Design (Solution Architect)
### CLI-Struktur
```
archivmail <command> [flags]
Commands:
import E-Mails importieren (EML, MBOX, Verzeichnis)
export E-Mails exportieren (EML, MBOX)
version Version anzeigen
help Hilfe anzeigen
archivmail import
--file /pfad/datei.eml oder .mbox
--dir /pfad/verzeichnis/
--recursive Unterverzeichnisse einschließen (mit --dir)
--dry-run Simulation ohne Speichern
--json Maschinenlesbare Ausgabe (JSON)
archivmail export
--out /pfad/ziel/ oder /pfad/archiv.mbox (Pflicht)
--format eml (Standard) | mbox
--from Absender-Filter
--to Empfänger-Filter
--date-from Datum von (ISO 8601: 2024-01-01)
--date-to Datum bis (ISO 8601: 2024-12-31)
--query Volltext-Suche (Xapian QueryParser)
--force Zieldatei überschreiben
--json Maschinenlesbare Ausgabe (JSON)
```
### Komponentenstruktur
```
archivmail (Go-Binary)
├── main.go ← Subcommand-Router (import / export / ...)
├── cmd/import.go
│ ├── Flag-Parsing
│ ├── Dateityp-Erkennung (.eml / .mbox / Verzeichnis)
│ ├── EML-Parser
│ ├── MBOX-Parser (zeilenweise)
│ └── → Storage Coordinator (PROJ-5, direkt, kein HTTP)
└── cmd/export.go
├── Flag-Parsing
├── Filter-Builder (from, to, date, query)
├── → Xapian ReadonlyDatabase (Suche/Filter)
├── → PostgreSQL Metadaten-Lookup
├── → .m-Datei lesen + AES-256-GCM entschlüsseln
└── Schreiben als EML-Dateien oder MBOX
```
### Import-Fluss
```
$ archivmail import --dir /backup/mails/ --recursive
Key laden aus /etc/archivmail/keyfile
Verzeichnis scannen → 3.842 .eml-Dateien gefunden
[████████░░] 2.150 / 3.842 (übersprungen: 12 Duplikate)
Fertig:
Importiert: 2.130
Übersprungen: 12 (Duplikate)
Fehler: 0
```
### Export-Fluss
```
$ archivmail export --from alice@firma.de \
--date-from 2024-01-01 \
--out /backup/export/
Key laden aus /etc/archivmail/keyfile
Xapian: 847 Mails gefunden (Filter: from=alice, date>=2024-01-01)
Exportiere nach /backup/export/
[████████████] 847 / 847
Fertig:
Exportiert: 847 EML-Dateien
Ziel: /backup/export/
```
### JSON-Ausgabe (--json Flag)
```json
{
"status": "done",
"imported": 2130,
"skipped": 12,
"errors": 0,
"duration_sec": 42
}
```
### Xapian-Lock beim parallelen Betrieb
```
Daemon läuft (WritableDatabase hält Lock für Index-Worker)
CLI export → ReadonlyDatabase → kein Lock-Konflikt ✓
CLI import → Storage Coordinator → WritableDatabase
└── Lock bereits gehalten?
→ Warten (max. 30 Sek.) → dann Fehlermeldung:
"Index locked by running daemon. Stop daemon or retry."
```
### Technische Entscheidungen
| Entscheidung | Begründung |
|---|---|
| **Gleiche Binary, Subcommands** | Kein separates CLI-Tool `archivmail import` und `archivmail serve` teilen Code und Storage Coordinator |
| **Direkter Speicherzugriff, kein HTTP** | CLI läuft als `archivmail`-User mit Dateisystem-Zugriff kein laufender Daemon nötig für Import/Export |
| **`--dry-run`** | Sicher testen ohne Daten zu verändern wichtig für große Bulk-Imports |
| **`--json` Flag** | Maschinenlesbar für Cron-Jobs, Monitoring-Skripte, Ansible-Playbooks |
| **Exit-Codes** | 0 = Erfolg, 1 = Fehler Standard für Shell-Skripting |
| **Xapian ReadonlyDatabase für Export** | Export kann parallel zum Daemon laufen ohne Lock-Konflikte |
### Abhängigkeiten
| Paket | Zweck |
|---|---|
| `github.com/spf13/cobra` | Subcommand-CLI-Framework |
| Xapian CGo-Bindings | Volltext-Filter beim Export (bereits PROJ-5) |
## QA Test Results
_To be added by /qa_
## Deployment
_To be added by /deploy_