9.0 KiB
9.0 KiB
API-Nutzung der Tenant Admin App
Diese Dokumentation beschreibt alle API-Endpunkte, die die Tenant Admin App mit der Backend-Hauptapp kommuniziert. Alle Requests sind tenant-scoped und erfordern JWT-Authentifizierung.
Authentifizierung
OAuth2 Flow (PKCE)
-
Start:
GET /oauth/authorize(Browser-Redirect mit PKCE-Challenge)- Params:
client_id,redirect_uri,scope=tenant:read tenant:write,state,code_challenge,code_challenge_method=S256 - Response: Authorization Code
- Params:
-
Token Exchange:
POST /oauth/token- Body:
grant_type=authorization_code,client_id,code,redirect_uri,code_verifier - Response:
{ access_token, refresh_token, expires_in, token_type } - Headers:
Content-Type: application/x-www-form-urlencoded
- Body:
-
Token Refresh:
POST /oauth/token- Body:
grant_type=refresh_token,client_id,refresh_token - Response: Neuer Access/Refresh-Token
- Body:
-
Token Validation:
GET /api/v1/tenant/me- Headers:
Authorization: Bearer {access_token} - Response:
{ id, email, tenant_id, role, name }
- Headers:
Dashboard
Stats laden
- GET /api/v1/tenant/dashboard
- Headers:
Authorization: Bearer {token} - Response:
{ credits, active_events, new_photos, task_progress } - Zweck: Übersicht-Daten für Dashboard-Cards
- Headers:
Events
Events-Liste
- GET /api/v1/tenant/events
- Headers:
Authorization: Bearer {token} - Params:
page=1(Pagination)per_page=50(max für Mobile)status=draft|active|archived(Filter)search=query(Suche in Titel/Ort)
- Response:
{ data: Event[], current_page, last_page, total } - Event-Shape:
{ id, title, date, location, status, photoCount, slug }
- Headers:
Event erstellen
- POST /api/v1/tenant/events
- Headers:
Authorization: Bearer {token},Content-Type: application/json - Body:
{ title, date, location, description } - Response: 201 Created mit erstelltem Event
- Validierung: Prüft Credit-Balance (1 Credit pro Event)
- Headers:
Event-Details
- GET /api/v1/tenant/events/{id}
- Headers:
Authorization: Bearer {token} - Response: Erweitertes Event mit
{ tasks[], members, stats { likes, views, uploads } }
- Headers:
Event updaten
- PATCH /api/v1/tenant/events/{id}
- Headers:
Authorization: Bearer {token},Content-Type: application/json,If-Match: {etag} - Body: Partial Event-Daten (title, date, location, description)
- Response: Updated Event
- Headers:
Event archivieren
- DELETE /api/v1/tenant/events/{id}
- Headers:
Authorization: Bearer {token},If-Match: {etag} - Response: 204 No Content (soft-delete)
- Headers:
Photos
Photos laden
- GET /api/v1/tenant/events/{event_id}/photos
- Headers:
Authorization: Bearer {token} - Params:
page=1,per_page=50status=pending|approved|rejected|featuredsort=date|likessince=cursor(für Infinite Scroll)
- Response:
{ data: Photo[], current_page, last_page } - Photo-Shape:
{ id, eventId, url, thumbnail, uploadedAt, status, likes, views, uploader, etag }
- Headers:
Upload-URL anfordern
- POST /api/v1/tenant/events/{event_id}/photos
- Headers:
Authorization: Bearer {token},Content-Type: application/json - Body:
{ file_name, description? } - Response:
{ id, upload_url (S3 signed), thumbnail_url }
- Headers:
Photo moderieren
- PATCH /api/v1/tenant/photos/{id}
- Headers:
Authorization: Bearer {token},Content-Type: application/json,If-Match: {etag} - Body:
{ status: 'approved'|'rejected'|'featured', featured?, reason? } - Response: Updated Photo
- Headers:
Photo löschen
- DELETE /api/v1/tenant/photos/{id}
- Headers:
Authorization: Bearer {token},If-Match: {etag} - Response: 204 No Content
- Headers:
Members
Mitglieder laden
- GET /api/v1/tenant/events/{event_id}/members
- Headers:
Authorization: Bearer {token} - Params:
page,per_page,status=pending|active|invited - Response:
{ data: Member[], current_page, last_page } - Member-Shape:
{ id, name, email, role, joinedAt, avatar?, status }
- Headers:
Mitglied einladen
- POST /api/v1/tenant/events/{event_id}/members
- Headers:
Authorization: Bearer {token},Content-Type: application/json - Body:
{ email, role: 'member'|'guest', name? } - Response: 201 Created, E-Mail wird versendet
- Headers:
Tasks
Tasks laden
- GET /api/v1/tasks
- Headers:
Authorization: Bearer {token} - Params:
global=true/false(globale vs. tenant Tasks)tenant_id=me(nur eigene Tasks)category=setup|photo|event|cleanup
- Response:
{ data: Task[], global: boolean } - Task-Shape:
{ id, title, description?, category, isGlobal, tenantId?, createdAt, color? }
- Headers:
Event-Tasks laden
- GET /api/v1/tenant/events/{event_id}/tasks
- Headers:
Authorization: Bearer {token} - Response:
{ data: EventTask[], overall_progress } - EventTask-Shape:
{ id, eventId, taskId, task: Task, order, completed, assignedTo?, progress }
- Headers:
Tasks bulk zuweisen
- POST /api/v1/tenant/events/{event_id}/tasks/bulk
- Headers:
Authorization: Bearer {token},Content-Type: application/json - Body:
{ task_ids: string[], order: number[] } - Response: Updated EventTasks mit neuer Reihenfolge
- Headers:
Settings
Settings laden
- GET /api/v1/tenant/settings
- Headers:
Authorization: Bearer {token} - Response:
{ primaryColor, tenantName, maxEventsPerMonth, enableTasks, enableEmotions, legalPages { impressumUrl, privacyUrl } }
- Headers:
Settings updaten
- PATCH /api/v1/tenant/settings
- Headers:
Authorization: Bearer {token},Content-Type: application/json - Body: Partial Settings-Daten
- Response: Updated Settings
- Headers:
Billing
Balance laden
- GET /api/v1/tenant/credits/balance
- Headers:
Authorization: Bearer {token} - Response:
{ balance: number }
- Headers:
Ledger-Verlauf
- GET /api/v1/tenant/ledger
- Headers:
Authorization: Bearer {token} - Params:
page,per_page(Pagination) - Response:
{ data: LedgerEntry[], current_page, last_page } - LedgerEntry:
{ id, type, amount, credits, date, description, transactionId? }
- Headers:
Kauf-Intent erstellen
- POST /api/v1/tenant/purchases/intent
- Headers:
Authorization: Bearer {token},Content-Type: application/json - Body:
{ package_id } - Response:
{ checkout_url: string }(Stripe-Checkout) - Nach dem Kauf: Webhook-Handling auf Backend für Balance-Update
- Headers:
Allgemeine Headers
Alle API-Requests enthalten:
- Authorization:
Bearer {access_token}(JWT mit tenant_id) - Content-Type:
application/json(für POST/PATCH) - If-Match:
{etag}(für Concurrency-Control bei Updates) - Accept:
application/json
Error-Handling
Standard-Error-Response:
{
"error": {
"code": "INSUFFICIENT_CREDITS",
"message": "Nicht genügend Credits verfügbar"
}
}
HTTP Status Codes:
- 200: Erfolg
- 201: Created
- 400: Bad Request (Validierungsfehler)
- 401: Unauthorized (Token-Refresh wird versucht)
- 403: Forbidden (RBAC-Verletzung)
- 404: Not Found
- 422: Unprocessable Entity
- 429: Rate Limited (Client retry mit Backoff)
Pagination
Alle Listen-Endpunkte unterstützen:
- page: Aktuelle Seite (default 1)
- per_page: Einträge pro Seite (default 20, max 50 für Mobile)
- Response:
{ data: [], current_page, last_page, per_page, total }
Headers für Concurrency
Mutierende Endpunkte (PATCH/DELETE) erfordern:
- If-Match:
{etag}aus GET-Response - Response: 412 Precondition Failed bei Conflict
Sicherheit
- Tenant-Isolation: Alle Endpunkte prüfen JWT-tenant_id gegen request tenant_id
- RBAC: Nur tenant_admin kann mutieren, member kann nur lesen/hochladen
- Rate Limiting: 100 Requests/Minute pro Tenant
- ETag: Automatische Concurrency-Control
- No PII-Logging: Keine sensiblen Daten in Logs
Testing
API-Test-Setup
- Backend starten: Stelle sicher, dass die Hauptapp läuft und Endpunkte verfügbar sind.
- Token erzeugen: Verwende Postman mit gültigem Access-Token.
- Endpoints testen: Jeder Endpunkt einzeln mit curl oder Postman.
Beispiel curl (mit Token)
curl -H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
https://api.fotospiel.com/api/v1/tenant/events
Frontend-Test
.envmit korrekter API-URL konfigurieren.npm run devstarten.- Browser-Network-Tab überprüfen für API-Calls.
Deployment
Environment-Variablen
- REACT_APP_API_URL: Backend-API-URL (Pflicht)
- REACT_APP_OAUTH_CLIENT_ID: OAuth-Client-ID (Pflicht)
Build & Deploy
- Development:
npm run dev - Production:
npm run build - Vorschau:
npm run preview
PWA-Installation
- App ist PWA-fähig (Manifest, Service Worker).
- Installierbar auf Desktop/Mobile via "Zum Startbildschirm hinzufügen".
Für weitere Details siehe die spezifischen Dokumentationsdateien.