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,178 @@
|
||||
# PROJ-13: REST API für externe CRM-Anbindung
|
||||
|
||||
## Status: In Progress
|
||||
**Created:** 2026-03-13
|
||||
**Last Updated:** 2026-03-13
|
||||
|
||||
## Dependencies
|
||||
- Requires: PROJ-1 (Authentifizierung) – API-Keys sind an Nutzer/Rollen gebunden
|
||||
- Requires: PROJ-5 (Speicherung & Indexierung) – Daten kommen aus dem Archiv
|
||||
- Requires: PROJ-6 (Volltext-Suche) – Suche über API nutzbar
|
||||
|
||||
## Hinweis
|
||||
Externe Systeme (CRM, ERP, Helpdesk etc.) sollen über eine dokumentierte REST API auf das Archiv zugreifen können – **ausschließlich lesend**. Schreiboperationen (Importieren, Löschen, Labeln etc.) sind über die API nicht möglich und werden nicht implementiert. Authentifizierung über API-Keys, nicht über Session-Cookies.
|
||||
|
||||
## User Stories
|
||||
- Als CRM-Administrator möchte ich einen API-Key generieren, damit mein CRM-System auf das Archiv zugreifen kann.
|
||||
- Als CRM-System möchte ich E-Mails eines bestimmten Kontakts (E-Mail-Adresse) abrufen, damit ich die Kommunikationshistorie im CRM anzeigen kann.
|
||||
- Als CRM-System möchte ich E-Mails nach Datum, Absender oder Betreff durchsuchen, damit ich gezielt relevante Mails finden kann.
|
||||
- Als Admin möchte ich API-Keys verwalten (anlegen, deaktivieren, löschen), damit ich den Zugriff kontrollieren kann.
|
||||
- Als Admin möchte ich sehen, welcher API-Key wann welche Anfragen gestellt hat.
|
||||
|
||||
## Acceptance Criteria
|
||||
- [ ] API-Key-Verwaltung im Admin-Bereich: anlegen, benennen, deaktivieren, löschen
|
||||
- [ ] API-Keys haben eine konfigurierbare Rolle (`user` oder `auditor`) – bestimmt Zugriffsumfang (Leserechte)
|
||||
- [ ] Nur `GET`-Methoden erlaubt – `POST`, `PUT`, `PATCH`, `DELETE` geben generisch 405 Method Not Allowed zurück
|
||||
- [ ] Authentifizierung via HTTP-Header: `Authorization: Bearer <api-key>`
|
||||
- [ ] Endpunkt: `GET /api/v1/mails?from=&to=&subject=&date_from=&date_to=` – Suche/Filterung
|
||||
- [ ] Endpunkt: `GET /api/v1/mails/{message_id}` – einzelne E-Mail abrufen (Metadaten)
|
||||
- [ ] Endpunkt: `GET /api/v1/mails/{message_id}/raw` – Original-EML herunterladen
|
||||
- [ ] Endpunkt: `GET /api/v1/mails?contact=email@firma.de` – alle Mails eines Kontakts (From oder To)
|
||||
- [ ] Antwortformat: JSON für Metadaten, `application/octet-stream` für Raw-EML
|
||||
- [ ] Paginierung: `?page=1&limit=25` (max. 100 pro Anfrage)
|
||||
- [ ] API-Zugriffe werden im Audit-Log erfasst (API-Key-Name, Endpunkt, Zeitstempel)
|
||||
- [ ] OpenAPI/Swagger-Dokumentation unter `/api/v1/docs`
|
||||
|
||||
## Edge Cases
|
||||
- Ungültiger oder deaktivierter API-Key → 401 Unauthorized
|
||||
- API-Key mit `user`-Rolle fragt Mails ab, auf die er keinen Zugriff hat → 403
|
||||
- Rate-Limiting: zu viele Anfragen pro API-Key → 429 Too Many Requests
|
||||
- Sehr große Ergebnismengen (>10.000 Treffer) → Paginierung erzwingen, kein Full-Dump
|
||||
- CRM fragt nicht existierende Message-ID ab → 404
|
||||
|
||||
## Technical Requirements
|
||||
- **Reine Lese-API** – ausschließlich `GET`-Endpunkte, keine Schreiboperationen
|
||||
- Eigener Route-Prefix `/api/v1/` für externe API (getrennt von interner `/api/`)
|
||||
- API-Keys: zufällig generiert (32 Byte, Base64), bcrypt-gehasht in der DB (nie im Klartext)
|
||||
- Rate-Limiting pro API-Key konfigurierbar (Standard: 60 Anfragen/Minute)
|
||||
- OpenAPI 3.0 Spec wird aus Code generiert oder manuell gepflegt
|
||||
- Versionierung: `/api/v1/` – spätere Versionen brechen bestehende Clients nicht
|
||||
|
||||
---
|
||||
## Tech Design (Solution Architect)
|
||||
|
||||
### Systemübersicht
|
||||
|
||||
```
|
||||
CRM / ERP / Helpdesk
|
||||
│
|
||||
│ GET /api/v1/mails?contact=kunde@example.com
|
||||
│ Authorization: Bearer <api-key>
|
||||
▼
|
||||
Go Backend – Externer API-Router (/api/v1/*)
|
||||
│
|
||||
├── API-Key Middleware ← statt Session-Cookie
|
||||
├── Rate Limiter
|
||||
├── Shared Search Service ←──── dieselbe Logik wie interne Suche (PROJ-6)
|
||||
└── Shared Mail Service ←──── dieselbe Logik wie Mail-Abruf (PROJ-7)
|
||||
```
|
||||
|
||||
### Komponentenstruktur
|
||||
|
||||
**Go Backend:**
|
||||
```
|
||||
/api/v1/* (externer Prefix, getrennt von internem /api/*)
|
||||
│
|
||||
├── API-Key Middleware ← ersetzt Session-Middleware
|
||||
│ ├── Bearer-Token aus Header lesen
|
||||
│ ├── SHA-256-Hash → DB-Lookup ← schneller Lookup ohne bcrypt-Overhead
|
||||
│ ├── Key deaktiviert? → 401
|
||||
│ ├── Rolle laden (user/auditor)
|
||||
│ └── Rate-Limit-Konfiguration laden
|
||||
│
|
||||
├── Rate Limiter ← Token-Bucket pro API-Key
|
||||
│ └── Limit überschritten → 429 + Retry-After Header
|
||||
│
|
||||
├── Method Guard ← alles außer GET → 405
|
||||
│
|
||||
├── Shared Search Service ← identische Logik wie /api/search (PROJ-6)
|
||||
│ ├── Xapian QueryParser
|
||||
│ ├── Rollen-Filter (user/auditor)
|
||||
│ └── PostgreSQL Metadaten-Lookup
|
||||
│
|
||||
├── Shared Mail Service ← identische Logik wie /api/mails (PROJ-7)
|
||||
│ ├── .m-Datei lesen + entschlüsseln
|
||||
│ ├── MIME-Parser
|
||||
│ └── Anhang-Streaming
|
||||
│
|
||||
├── Audit Logger ← API-Key-Name + Endpunkt + Zeitstempel
|
||||
│
|
||||
└── API-Key-Verwaltung (Admin)
|
||||
├── POST /api/admin/apikeys ← Key generieren (einmalige Anzeige)
|
||||
├── GET /api/admin/apikeys ← Liste (Name + Rolle + letzter Zugriff)
|
||||
└── DELETE /api/admin/apikeys/{id}
|
||||
```
|
||||
|
||||
### API-Key Authentifizierungsfluss
|
||||
|
||||
```
|
||||
CRM-System
|
||||
│
|
||||
│ Authorization: Bearer am_a1b2c3d4e5f6...
|
||||
▼
|
||||
API-Key Middleware
|
||||
├─ Präfix "am_" prüfen
|
||||
├─ SHA-256(token) → DB-Lookup (indiziert)
|
||||
├─ Key gefunden + aktiv? Nein → 401
|
||||
└─ Ja → Rolle + Rate-Limit laden
|
||||
│
|
||||
▼
|
||||
Rate Limiter (Token-Bucket)
|
||||
├─ Limit erreicht? → 429 + Retry-After: 30
|
||||
└─ OK → weiter
|
||||
│
|
||||
▼
|
||||
Method Guard
|
||||
├─ POST/PUT/DELETE? → 405
|
||||
└─ GET → weiter
|
||||
│
|
||||
▼
|
||||
Shared Service Layer
|
||||
│
|
||||
▼
|
||||
Audit Logger → API-Key-Name + Endpunkt + Zeitstempel
|
||||
```
|
||||
|
||||
### Datenmodell
|
||||
|
||||
**Tabelle `api_keys`:**
|
||||
|
||||
| Feld | Beschreibung |
|
||||
|------|-------------|
|
||||
| `id` | Interne ID |
|
||||
| `name` | Bezeichnung (z.B. "CRM Salesforce") |
|
||||
| `token_hash` | SHA-256 des Tokens (für schnellen Lookup, indiziert) |
|
||||
| `role` | `user` oder `auditor` |
|
||||
| `active` | `true` / `false` |
|
||||
| `rate_limit` | Anfragen pro Minute (Standard: 60) |
|
||||
| `created_at` | Erstellungszeitpunkt |
|
||||
| `last_used_at` | Letzter erfolgreicher Zugriff |
|
||||
|
||||
**Key-Format:**
|
||||
```
|
||||
Generiert: am_<32-Byte-random-Base64>
|
||||
Gespeichert: SHA-256(token) in DB
|
||||
Angezeigt: einmalig im Admin-UI – danach nicht mehr abrufbar
|
||||
```
|
||||
|
||||
### Technische Entscheidungen
|
||||
|
||||
| Entscheidung | Begründung |
|
||||
|---|---|
|
||||
| **Shared Service Layer** | Suche und Mail-Abruf teilen dieselbe Go-Logik mit der internen API – kein doppelter Code |
|
||||
| **SHA-256 statt bcrypt** | API-Keys sind kryptografisch zufällig (32 Byte) – SHA-256 reicht, bcrypt wäre bei jeder Anfrage zu langsam |
|
||||
| **`am_`-Präfix** | Erkennungsmerkmal für archivmail-Keys – einfach filterbar in Logs |
|
||||
| **Token einmalig anzeigen** | Nur Hash gespeichert – kein späteres Auslesen möglich (wie GitHub PAT) |
|
||||
| **Token-Bucket Rate Limiter** | Gleichmäßige Anfragen erlaubt, kurze Bursts toleriert |
|
||||
| **`/api/v1/` Prefix** | Klare Versionierung – zukünftige `/api/v2/` bricht bestehende Clients nicht |
|
||||
| **Audit-Log bei API-Zugriffen** | Externe Zugriffe werden geloggt (anders als interne Lesezugriffe) |
|
||||
|
||||
### Abhängigkeiten
|
||||
|
||||
Kein zusätzliches Paket – Rate-Limiter und SHA-256 aus der Go-Stdlib (`crypto/sha256`, `sync`).
|
||||
|
||||
## QA Test Results
|
||||
_To be added by /qa_
|
||||
|
||||
## Deployment
|
||||
_To be added by /deploy_
|
||||
Reference in New Issue
Block a user