Files
archivmail/features/PROJ-13-rest-api-crm.md
T
sysops d360c9a5ba 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>
2026-03-14 11:43:19 +01:00

179 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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_