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
+41 -13
View File
@@ -1,29 +1,57 @@
# Product Requirements Document
## Vision
_Describe what you are building and why._
Ein selbst gehostetes, unternehmenstaugliches Mail-Archiv-System, das E-Mails aus verschiedenen Quellen (IMAP, SMTP, EML/MBOX) importiert, volltext-indexiert und DSGVO-konform langzeitarchiviert. Admins verwalten Nutzer und Postfächer; alle Mitarbeiter können gezielt im Archiv suchen.
## Target Users
_Who will use this product? Describe their needs and pain points._
**Primär: Unternehmen (5500 Mitarbeiter)**
- **Endnutzer:** Mitarbeiter, die E-Mails im Archiv suchen und lesen wollen
- **Admins:** IT-Verantwortliche, die Nutzer, Postfächer und Import-Quellen verwalten
- **Compliance-Verantwortliche:** Prüfen Audit-Logs, exportieren E-Mails für Anfragen
**Pain Points:**
- E-Mail-Clients bieten keine revisionssichere Langzeitarchivierung
- Suche über mehrere Konten und Postfächer ist unzuverlässig
- Datenschutzbedenken bei Cloud-basierten Archivlösungen
- Nachweis der Unverändertheit von E-Mails für Compliance erforderlich
## Core Features (Roadmap)
| Priority | Feature | Status |
|----------|---------|--------|
| P0 (MVP) | _Feature 1_ | Planned |
| P0 (MVP) | _Feature 2_ | Planned |
| P1 | _Feature 3_ | Planned |
| P2 | _Feature 4_ | Planned |
| P0 (MVP) | Nutzer-Authentifizierung & Rollen (User/Admin) | Planned |
| P0 (MVP) | E-Mail-Import: SMTP-Eingang via BCC *(primärer Eingangsweg)* | Planned |
| P1 | E-Mail-Import: IMAP-Verbindung *(sekundär, Altbestände)* | Planned |
| P1 | E-Mail-Import: POP3-Verbindung *(sekundär, Altbestände)* | Planned |
| P1 | E-Mail-Import: EML/MBOX Upload *(sekundär, Migration)* | Planned |
| P0 (MVP) | E-Mail-Speicherung & Volltext-Indexierung | Planned |
| P0 (MVP) | Volltext-Suche & Filterung | Planned |
| P0 (MVP) | E-Mail-Ansicht (Lesen & Anhänge) | Planned |
| P1 | Automatischer IMAP-Sync (Cron-Job) | Planned |
| P1 | Ordner- & Label-Verwaltung | Planned |
| P1 | Admin-Bereich: Nutzer- & Postfachverwaltung | Planned |
| P2 | Audit-Log & Compliance-Berichte | Planned |
| P2 | E-Mail-Export (EML/PDF) | Planned |
| P2 | REST API für externe CRM-Anbindung | Planned |
## Success Metrics
_How will you measure success? (e.g., user signups, retention, task completion rate)_
- Import und Indexierung von 100.000+ E-Mails ohne Performanceprobleme
- Volltext-Suchanfragen liefern Ergebnisse in < 2 Sekunden
- SMTP-Journaling empfängt E-Mails in Echtzeit (< 5 Sekunden Verzögerung)
- Automatischer IMAP-Sync läuft stabil über Monate ohne manuelle Eingriffe
- Audit-Log erfasst alle Zugriffe lückenlos
## Constraints
_Budget, timeline, technical limitations, team size._
- **Self-hosted:** Kein Zwang zu externen Cloud-Diensten läuft on-premise oder auf eigenem Server
- **DSGVO-konform:** E-Mails verlassen nie den eigenen Server; Löschkonzept vorhanden
- **Großes Volumen:** Architektur skaliert auf > 100.000 E-Mails
- **Einfache Architektur:** Go-Backend REST API + Next.js-Frontend, kleines Team (12 Devs)
- **Tech Stack:** Go (Backend, SMTP-Daemon, Storage, Xapian) + Next.js/TypeScript (Web-GUI) + PostgreSQL
## Non-Goals
_What are you explicitly NOT building in this version?_
---
Use `/requirements` to create detailed feature specifications for each item in the roadmap above.
- Kein vollständiger E-Mail-Client (kein eigenständiges Senden von E-Mails durch Nutzer)
- Keine Ende-zu-Ende-Verschlüsselung im MVP
- Kein Enterprise-LDAP/SSO im MVP (nur lokale Accounts)
- Keine mobile App (nur Web-Interface)
- Keine automatische GoBD/SOX-Zertifizierung im MVP
+252
View File
@@ -0,0 +1,252 @@
# archivmail REST API v1
> **Lese-API** alle Endpunkte sind read-only (`GET`). Schreiboperationen sind nicht verfügbar.
## Authentifizierung
Jede Anfrage muss einen gültigen API-Key im HTTP-Header mitschicken:
```
Authorization: Bearer <api-key>
```
API-Keys werden vom Admin im Admin-Bereich generiert und verwaltet. Ein API-Key hat eine zugewiesene Rolle (`user` oder `auditor`), die den Zugriffsumfang bestimmt.
| Rolle | Zugriff |
|-------|---------|
| `user` | Nur E-Mails aus zugewiesenen Postfächern |
| `auditor` | Alle E-Mails (postfachübergreifend) |
---
## Fehlercodes
| Code | Bedeutung |
|------|-----------|
| `200 OK` | Erfolg |
| `400 Bad Request` | Ungültige Parameter |
| `401 Unauthorized` | API-Key fehlt, ungültig oder deaktiviert |
| `403 Forbidden` | API-Key hat keine Berechtigung für diese Ressource |
| `404 Not Found` | E-Mail nicht gefunden |
| `405 Method Not Allowed` | Schreibmethode (POST, PUT, DELETE etc.) nicht erlaubt |
| `429 Too Many Requests` | Rate-Limit überschritten (Standard: 60 Anfragen/Minute) |
| `500 Internal Server Error` | Serverfehler |
---
## Endpunkte
---
### E-Mails suchen / filtern
```
GET /api/v1/mails
```
**Query-Parameter (alle optional, kombinierbar):**
| Parameter | Typ | Beschreibung | Beispiel |
|-----------|-----|-------------|---------|
| `q` | string | Volltext-Suche (Xapian QueryParser) | `q=Rechnung+2024` |
| `from` | string | Absender (exakt oder Partial-Match) | `from=alice@firma.de` |
| `to` | string | Empfänger | `to=bob@firma.de` |
| `cc` | string | CC-Empfänger | `cc=team@firma.de` |
| `contact` | string | From **oder** To **oder** CC enthält diese Adresse | `contact=kunde@example.com` |
| `subject` | string | Betreff enthält diesen Text | `subject=Angebot` |
| `date_from` | string (ISO 8601) | Mails ab diesem Datum | `date_from=2024-01-01` |
| `date_to` | string (ISO 8601) | Mails bis zu diesem Datum | `date_to=2024-12-31` |
| `has_attachments` | boolean | Nur Mails mit/ohne Anhänge | `has_attachments=true` |
| `page` | integer | Seite (Standard: 1) | `page=2` |
| `limit` | integer | Ergebnisse pro Seite (Standard: 25, max: 100) | `limit=50` |
| `sort` | string | Sortierung: `date_asc`, `date_desc` (Standard), `relevance` | `sort=date_asc` |
**Beispiel-Request:**
```
GET /api/v1/mails?contact=kunde@example.com&date_from=2024-01-01&limit=25
Authorization: Bearer am_abc123...
```
**Antwort:**
```json
{
"total": 142,
"page": 1,
"limit": 25,
"pages": 6,
"mails": [
{
"message_id": "<abc123@mailserver.firma.de>",
"from": "alice@firma.de",
"to": ["kunde@example.com"],
"cc": [],
"subject": "Angebot vom 15.03.2024",
"date": "2024-03-15T10:23:00Z",
"size": 24680,
"has_attachments": true,
"attachments": [
{
"filename": "Angebot_2024.pdf",
"mime_type": "application/pdf",
"size": 18432
}
]
}
]
}
```
---
### Einzelne E-Mail abrufen (Metadaten + Body)
```
GET /api/v1/mails/{message_id}
```
**Pfad-Parameter:**
| Parameter | Beschreibung |
|-----------|-------------|
| `message_id` | RFC-2822 Message-ID (URL-encoded) |
**Beispiel-Request:**
```
GET /api/v1/mails/%3Cabc123%40mailserver.firma.de%3E
Authorization: Bearer am_abc123...
```
**Antwort:**
```json
{
"message_id": "<abc123@mailserver.firma.de>",
"from": "alice@firma.de",
"to": ["kunde@example.com"],
"cc": [],
"subject": "Angebot vom 15.03.2024",
"date": "2024-03-15T10:23:00Z",
"size": 24680,
"body_plain": "Sehr geehrte Damen und Herren,\n\nim Anhang finden Sie...",
"body_html": "<html>...</html>",
"has_attachments": true,
"attachments": [
{
"filename": "Angebot_2024.pdf",
"mime_type": "application/pdf",
"size": 18432,
"download_url": "/api/v1/mails/%3Cabc123%40mailserver.firma.de%3E/attachments/0"
}
]
}
```
---
### Original-E-Mail als EML herunterladen
```
GET /api/v1/mails/{message_id}/raw
```
Gibt die originale, unveränderte E-Mail im RFC-2822-Format zurück.
**Antwort-Header:**
```
Content-Type: message/rfc822
Content-Disposition: attachment; filename="<message_id>.eml"
```
---
### Einzelnen Anhang herunterladen
```
GET /api/v1/mails/{message_id}/attachments/{index}
```
**Pfad-Parameter:**
| Parameter | Beschreibung |
|-----------|-------------|
| `message_id` | RFC-2822 Message-ID (URL-encoded) |
| `index` | Position des Anhangs (0-basiert, aus der Attachment-Liste) |
**Antwort-Header:**
```
Content-Type: application/pdf (je nach MIME-Type des Anhangs)
Content-Disposition: attachment; filename="Angebot_2024.pdf"
```
---
### Server-Info
```
GET /api/v1/info
```
Gibt Version und Status des Servers zurück. Nützlich zum Testen der Verbindung.
**Antwort:**
```json
{
"version": "1.0.0",
"status": "ok",
"mail_count": 142857
}
```
---
## Volltext-Suche Syntax (`q`-Parameter)
Der `q`-Parameter unterstützt Xapian QueryParser-Syntax:
| Syntax | Beispiel | Bedeutung |
|--------|---------|-----------|
| Einfacher Begriff | `Rechnung` | Enthält "Rechnung" |
| Mehrere Begriffe | `Rechnung Mahnung` | Enthält beide Begriffe (AND) |
| Phrasensuche | `"offene Rechnung"` | Exakter Ausdruck |
| OR-Verknüpfung | `Rechnung OR Angebot` | Eines von beiden |
| Ausschließen | `Rechnung NOT Storno` | Rechnung aber nicht Storno |
| Wildcard | `Rechnun*` | Beginnt mit "Rechnun" |
| Feldspezifisch | `subject:Angebot` | Nur im Betreff suchen |
| Feldspezifisch | `from:alice@firma.de` | Nur von diesem Absender |
---
## Paginierung
Alle Listen-Endpunkte sind paginiert:
```
GET /api/v1/mails?page=2&limit=50
```
Die Antwort enthält immer:
- `total` Gesamtanzahl der Treffer
- `page` aktuelle Seite
- `limit` Einträge pro Seite
- `pages` Gesamtanzahl der Seiten
Maximum: 100 Einträge pro Anfrage. Für größere Abfragen mehrere Seiten iterieren.
---
## Rate Limiting
Standard: **60 Anfragen pro Minute** pro API-Key (konfigurierbar durch Admin).
Bei Überschreitung:
```
HTTP 429 Too Many Requests
Retry-After: 30
```
---
## Changelog
| Version | Datum | Änderungen |
|---------|-------|-----------|
| v1.0 | 2026-03-13 | Initiale Version |