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:
@@ -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_
|
||||
Reference in New Issue
Block a user