# API-Nutzung der Tenant Admin App

Diese Dokumentation beschreibt alle API-Endpunkte der Tenant Admin App. Alle Requests sind tenant-scoped und erfordern ein Sanctum Personal Access Token (PAT) mit der Fähigkeit `tenant-admin` bzw. `tenant:<id>`.

## Authentifizierung

### Passwort-Login (PAT)
- **Endpoint**: `POST /api/v1/tenant-auth/login`
- **Body** (`application/json`): `{ "login": "username oder email", "password": "••••" }`
- **Response**: `{ token, token_type, abilities[], user { ... } }`
- **Fehler**: `422` bei ungültigen Daten, `429` bei Rate-Limit (`throttle:tenant-auth`).
- **Hinweis**: `login` akzeptiert E-Mail _oder_ Usernamen. Erfolgreiche Logins revoken vorhandene PATs mit Name `tenant-admin`.

### Session→PAT Exchange (Google/Login-Shell)
- **Endpoint**: `POST /api/v1/tenant-auth/exchange`
- **Middleware**: Sanctum stateful cookies (`SANCTUM_STATEFUL_DOMAINS`), `throttle:tenant-auth`.
- **Use-Case**: Nach Google-Login oder Marketing-Checkout Session wird das PAT ohne erneute Passwort-Eingabe ausgegeben.
- **Response**: identisch zum Passwort-Login.

### Token Status & Logout
- **`GET /api/v1/tenant-auth/me`** – liefert `{ user, tenant, abilities }` und spiegelt die PAT-Fähigkeiten wider.
- **`POST /api/v1/tenant-auth/logout`** – löscht das aktuelle PAT aus `personal_access_tokens`.

### Legacy Payload `/tenant/me`
- **Endpoint**: `GET /api/v1/tenant/me`
- **Response**: historisches Format (`tenant_id`, `remaining_events`, `scopes`), betrieben über denselben PAT.
- **Kompatibilität**: Dient älteren Clients als Übergang; neue Features sollten `/tenant-auth/me` verwenden.

## Dashboard

### Stats laden
- **GET /api/v1/tenant/dashboard**
  - **Headers**: `Authorization: Bearer {token}`
  - **Response**: `{ active_package, active_events, new_photos, task_progress }`
  - **Zweck**: Übersicht-Daten für Dashboard-Cards (active_package: current tenant package info)

## 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 }`

### Event erstellen
- **POST /api/v1/tenant/events**
  - **Headers**: `Authorization: Bearer {token}`, `Content-Type: application/json`
  - **Body**: `{ title, date, location, description, package_id }`
  - **Response**: 201 Created mit erstelltem Event
  - **Validierung**: Prüft Tenant-Package (Reseller-Limit) und erstellt Event-Package (Einmalkauf oder Free)

### Event-Details
- **GET /api/v1/tenant/events/{slug}**
  - **Headers**: `Authorization: Bearer {token}`
  - **Response**: Erweitertes Event mit `{ tasks[], members, stats { likes, views, uploads } }`

### Event updaten
- **PATCH /api/v1/tenant/events/{slug}**
  - **Headers**: `Authorization: Bearer {token}`, `Content-Type: application/json`, `If-Match: {etag}`
  - **Body**: Partial Event-Daten (title, date, location, description)
  - **Response**: Updated Event

### Event archivieren
- **DELETE /api/v1/tenant/events/{slug}**
  - **Headers**: `Authorization: Bearer {token}`, `If-Match: {etag}`
  - **Response**: 204 No Content (soft-delete)

## Photos

### Photos laden
- **GET /api/v1/tenant/events/{event_id}/photos**
  - **Headers**: `Authorization: Bearer {token}`
  - **Params**: 
    - `page=1`, `per_page=50`
    - `status=pending|approved|rejected|featured`
    - `sort=date|likes`
    - `since=cursor` (für Infinite Scroll)
  - **Response**: `{ data: Photo[], current_page, last_page }`
  - **Photo-Shape**: `{ id, eventId, url, thumbnail, uploadedAt, status, likes, views, uploader, etag }`

### 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 }`

### 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

### Photo löschen
- **DELETE /api/v1/tenant/photos/{id}**
  - **Headers**: `Authorization: Bearer {token}`, `If-Match: {etag}`
  - **Response**: 204 No Content

## 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 }`

### 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

## 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? }`

### 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 }`

### 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

## Settings

### Settings laden
- **GET /api/v1/tenant/settings**
  - **Headers**: `Authorization: Bearer {token}`
  - **Response**: `{ primaryColor, tenantName, maxEventsPerMonth, enableTasks, enableEmotions, legalPages { impressumUrl, privacyUrl } }`

### Settings updaten
- **PATCH /api/v1/tenant/settings**
  - **Headers**: `Authorization: Bearer {token}`, `Content-Type: application/json`
  - **Body**: Partial Settings-Daten
  - **Response**: Updated Settings

## Billing

### Balance laden
- **GET /api/v1/tenant/credits/balance**
  - **Headers**: `Authorization: Bearer {token}`
  - **Response**: `{ balance: number }`

### Ledger-Verlauf
- **GET /api/v1/tenant/credits/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? }`

### Credits kaufen (In-App)
- **POST /api/v1/tenant/credits/purchase**
  - **Headers**: `Authorization: Bearer {token}`, `Content-Type: application/json`
  - **Body**: `{ package_id: string, credits_added: number, platform?: 'capacitor'|'web', transaction_id?: string, subscription_active?: boolean }`
  - **Response**: `{ message, balance, subscription_active }`
  - **Hinweis**: Wird nach erfolgreichen In-App-Kuferfolgen aufgerufen, aktualisiert Balance & Ledger.

### Credits synchronisieren
- **POST /api/v1/tenant/credits/sync**
  - **Headers**: `Authorization: Bearer {token}`, `Content-Type: application/json`
  - **Body**: `{ balance: number, subscription_active: boolean, last_sync: ISODateString }`
  - **Response**: `{ balance, subscription_active, server_time }`
  - **Hinweis**: Client meldet lokalen Stand; Server gibt Quelle-der-Wahrheit zurcck.

### 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

## Allgemeine Headers

Alle API-Requests enthalten:
- **Authorization**: `Bearer {access_token}` (Sanctum PAT mit Fähigkeit `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:**
```json
{
  "error": {
    "code": "PACKAGE_LIMIT_EXCEEDED",
    "message": "Package-Limit überschritten (z.B. max_photos)"
  }
}
```

**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**: Middleware vergleicht PAT-Fähigkeit (`tenant:{id}`) mit dem angefragten Tenant
- **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
1. **Backend starten**: Stelle sicher, dass die Hauptapp läuft und Endpunkte verfügbar sind.
2. **Token erzeugen**: Verwende Postman mit gültigem Access-Token.
3. **Endpoints testen**: Jeder Endpunkt einzeln mit curl oder Postman.

### Beispiel curl (mit Token)
```bash
curl -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  https://api.fotospiel.com/api/v1/tenant/events
```

### Frontend-Test
1. `.env` mit korrekter API-URL konfigurieren.
2. `npm run dev` starten.
3. Browser-Network-Tab überprüfen für API-Calls.

## Deployment

### Environment-Variablen
- **VITE_API_URL**: Backend-API-URL (Pflicht)
- **VITE_ENABLE_TENANT_SWITCHER**: Dev-Flag um Tenants im Header auszuwählen (optional, Default `false`)
- **VITE_REVENUECAT_PUBLIC_KEY**: Optional für In-App-Käufe (RevenueCat)
- **SANCTUM_STATEFUL_DOMAINS** (Backend): Enthält die Origins des Admin-PWA/TWA, damit der Session→PAT-Exchange funktioniert.
- **REVENUECAT_WEBHOOK_SECRET / REVENUECAT_PRODUCT_MAPPINGS / REVENUECAT_APP_USER_PREFIX / REVENUECAT_WEBHOOK_QUEUE**: Backend-Konfiguration für RevenueCat-Webhooks, siehe `config/services.php`.

### Build & Deploy
1. **Development**: `npm run dev`
2. **Production**: `npm run build`
3. **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.
