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

7.4 KiB
Raw Blame History

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