further rework to the documentation
@@ -117,7 +117,7 @@ services:
|
|||||||
|
|
||||||
queue:
|
queue:
|
||||||
image: registry.internal:5443/${APP_IMAGE:-fotospiel-app:latest}
|
image: registry.internal:5443/${APP_IMAGE:-fotospiel-app:latest}
|
||||||
command: /var/www/html/docs/queue-supervisor/queue-worker.sh default
|
command: /var/www/html/scripts/queue-worker.sh default
|
||||||
environment:
|
environment:
|
||||||
<<: *app-env
|
<<: *app-env
|
||||||
SKIP_CODE_SYNC: "1"
|
SKIP_CODE_SYNC: "1"
|
||||||
@@ -134,7 +134,7 @@ services:
|
|||||||
|
|
||||||
media-storage-worker:
|
media-storage-worker:
|
||||||
image: registry.internal:5443/${APP_IMAGE:-fotospiel-app:latest}
|
image: registry.internal:5443/${APP_IMAGE:-fotospiel-app:latest}
|
||||||
command: /var/www/html/docs/queue-supervisor/queue-worker.sh media-storage
|
command: /var/www/html/scripts/queue-worker.sh media-storage
|
||||||
environment:
|
environment:
|
||||||
<<: *app-env
|
<<: *app-env
|
||||||
QUEUE_TRIES: 5
|
QUEUE_TRIES: 5
|
||||||
@@ -169,7 +169,7 @@ services:
|
|||||||
|
|
||||||
horizon:
|
horizon:
|
||||||
image: registry.internal:5443/${APP_IMAGE:-fotospiel-app:latest}
|
image: registry.internal:5443/${APP_IMAGE:-fotospiel-app:latest}
|
||||||
command: /var/www/html/docs/queue-supervisor/horizon.sh
|
command: /var/www/html/scripts/horizon.sh
|
||||||
environment:
|
environment:
|
||||||
<<: *app-env
|
<<: *app-env
|
||||||
SKIP_CODE_SYNC: "1"
|
SKIP_CODE_SYNC: "1"
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ services:
|
|||||||
|
|
||||||
queue:
|
queue:
|
||||||
image: fotospiel-app:latest
|
image: fotospiel-app:latest
|
||||||
command: /var/www/html/docs/queue-supervisor/queue-worker.sh default
|
command: /var/www/html/scripts/queue-worker.sh default
|
||||||
env_file:
|
env_file:
|
||||||
- docker/.env.docker
|
- docker/.env.docker
|
||||||
environment:
|
environment:
|
||||||
@@ -56,7 +56,7 @@ services:
|
|||||||
|
|
||||||
media-storage-worker:
|
media-storage-worker:
|
||||||
image: fotospiel-app:latest
|
image: fotospiel-app:latest
|
||||||
command: /var/www/html/docs/queue-supervisor/queue-worker.sh media-storage
|
command: /var/www/html/scripts/queue-worker.sh media-storage
|
||||||
env_file:
|
env_file:
|
||||||
- docker/.env.docker
|
- docker/.env.docker
|
||||||
environment:
|
environment:
|
||||||
@@ -85,7 +85,7 @@ services:
|
|||||||
|
|
||||||
horizon:
|
horizon:
|
||||||
image: fotospiel-app:latest
|
image: fotospiel-app:latest
|
||||||
command: /var/www/html/docs/queue-supervisor/horizon.sh
|
command: /var/www/html/scripts/horizon.sh
|
||||||
env_file:
|
env_file:
|
||||||
- docker/.env.docker
|
- docker/.env.docker
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ sync_code() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ensure_helper_scripts() {
|
ensure_helper_scripts() {
|
||||||
if compgen -G "$APP_TARGET/docs/queue-supervisor/*.sh" > /dev/null; then
|
if compgen -G "$APP_TARGET/scripts/*.sh" > /dev/null; then
|
||||||
chmod +x "$APP_TARGET"/docs/queue-supervisor/*.sh || true
|
chmod +x "$APP_TARGET"/scripts/*.sh || true
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 655 KiB After Width: | Height: | Size: 655 KiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 127 KiB |
|
Before Width: | Height: | Size: 384 KiB After Width: | Height: | Size: 384 KiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 739 KiB After Width: | Height: | Size: 739 KiB |
|
Before Width: | Height: | Size: 365 KiB After Width: | Height: | Size: 365 KiB |
|
Before Width: | Height: | Size: 340 KiB After Width: | Height: | Size: 340 KiB |
|
Before Width: | Height: | Size: 364 KiB After Width: | Height: | Size: 364 KiB |
|
Before Width: | Height: | Size: 365 KiB After Width: | Height: | Size: 365 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
@@ -8,6 +8,6 @@ This section consolidates everything platform operators need: deployment guides,
|
|||||||
- `photobooth/` — FTP ingest service docs and ops playbooks.
|
- `photobooth/` — FTP ingest service docs and ops playbooks.
|
||||||
- `media-storage-spec.md` — Upload/archival flow overview.
|
- `media-storage-spec.md` — Upload/archival flow overview.
|
||||||
- `guest-notification-ops.md` — Push notification queue monitoring.
|
- `guest-notification-ops.md` — Push notification queue monitoring.
|
||||||
- `queue-workers.md` — Worker container instructions referencing scripts in `/docs/queue-supervisor/`.
|
- `queue-workers.md` — Worker container instructions referencing scripts in `/scripts/`.
|
||||||
|
|
||||||
Future additions (e.g., escalations, on-call checklists, Terraform notes) should live here as well so all ops content remains in one location.
|
Future additions (e.g., escalations, on-call checklists, Terraform notes) should live here as well so all ops content remains in one location.
|
||||||
|
|||||||
94
docs/ops/backup-restore.md
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
---
|
||||||
|
title: Backup & Restore / Disaster Recovery
|
||||||
|
sidebar_label: Backup & DR
|
||||||
|
---
|
||||||
|
|
||||||
|
Dieses Dokument beschreibt, was gesichert werden sollte, wie Backups geprüft werden und wie ein Restore im Notfall abläuft.
|
||||||
|
|
||||||
|
## 1. Was muss gesichert werden?
|
||||||
|
|
||||||
|
- **Datenbank**
|
||||||
|
- MySQL‑Datenbank (alle Schemas/Tables des Fotospiel‑Backends).
|
||||||
|
- Enthält Tenants, Events, Fotos‑Metadaten, Join‑Tokens, Abrechnungsdaten, Logs (soweit in DB).
|
||||||
|
- **Medienspeicher**
|
||||||
|
- Hot‑Storage: Pfade unter `storage/app/private` / `storage/app/public` oder konfigurierten „hot“‑Disks.
|
||||||
|
- Archivspeicher: Buckets/Disks, in denen `event_media_assets` mit Status `archived` liegen.
|
||||||
|
- **Konfiguration**
|
||||||
|
- `.env` Dateien (ohne sie in Git zu speichern), Dokploy‑Compose‑Konfigurationen, Secrets für externe Dienste.
|
||||||
|
- Optional: Horizon‑Konfiguration, Monitoring‑Dashboards.
|
||||||
|
|
||||||
|
> TODO: Füge hier konkrete Pfade/Bucket‑Namen und die verwendeten Backup‑Tools (z.B. `mysqldump`, S3‑Snapshots, Dokploy‑Backups) ein.
|
||||||
|
|
||||||
|
## 2. Backup-Strategie
|
||||||
|
|
||||||
|
- **Datenbank-Backups**
|
||||||
|
- Frequenz: mindestens täglich (idealerweise alle 4–6 Stunden für Produktions‑DB).
|
||||||
|
- Aufbewahrung: z.B. 7–30 Tage, mit Off‑Site‑Kopie.
|
||||||
|
- Prüfschritte:
|
||||||
|
- Dump/Backupfile auf Plausibilität (Größe, letzte Änderung).
|
||||||
|
- Regelmäßige Test‑Restores in eine Staging‑DB:
|
||||||
|
- Beispiel (einfacher Dump auf Host – Pfade/Passwörter an Umgebung anpassen):
|
||||||
|
- `mysqldump -h 127.0.0.1 -u fotospiel -p fotospiel > fotospiel-$(date +%F).sql`
|
||||||
|
- Restore in temporäre DB (z.B. `fotospiel_restore`) und kurze Stichproben:
|
||||||
|
- `mysql -h 127.0.0.1 -u fotospiel -p fotospiel_restore < fotospiel-YYYY-MM-DD.sql`
|
||||||
|
- **Medien-Backups**
|
||||||
|
- Hot‑Storage:
|
||||||
|
- Snapshot/Incremental‑Backup der Storage‑Volumes oder S3‑Buckets.
|
||||||
|
- Archive:
|
||||||
|
- Sicherstellen, dass Archiv‑Backups nicht versehentlich durch Lifecycle‑Policies gelöscht werden, bevor gesetzliche Retention erfüllt ist.
|
||||||
|
- **Konfig-Backups**
|
||||||
|
- `.env` und Secrets nur verschlüsselt speichern (z.B. in einem Secrets‑Manager, nicht in Klartext‑Backups).
|
||||||
|
- Dokploy‑/Compose‑Konfiguration versionieren (Git) und zusätzlich sicher exportieren.
|
||||||
|
|
||||||
|
## 3. Restore-Szenarien
|
||||||
|
|
||||||
|
### 3.1 Einzelner Tenant/Event defekt
|
||||||
|
|
||||||
|
1. Reproduzieren, ob der Fehler rein logisch (Datenkonsistenz) oder physisch (Fehlender Medien‑Blob) ist.
|
||||||
|
2. **DB‑Restore (punktuell)**:
|
||||||
|
- Wenn möglich, nur relevante Tabellenbereiche (z.B. `tenants`, `events`, `photos`, `event_media_assets`, `event_packages`) aus Backup in eine temporäre DB laden.
|
||||||
|
- Differenzanalyse: welche Daten fehlen/fehlerhaft? Manuell oder via Skript zurückspielen.
|
||||||
|
3. **Medien-Check**
|
||||||
|
- Fehlende Dateien im Hot/Archive‑Storage identifizieren (z.B. per `event_media_assets` Pfade + `Storage::disk()->exists`).
|
||||||
|
- Wenn Dateien im Backup vorhanden, gezielt an den richtigen Pfad zurückkopieren.
|
||||||
|
|
||||||
|
> Diese Schritte sollten zuerst in einer Staging‑Umgebung eingeübt werden, bevor sie in Produktion angewendet werden.
|
||||||
|
|
||||||
|
### 3.2 Betriebsweite Störung (DB/Storage Verlust)
|
||||||
|
|
||||||
|
1. **DB wiederherstellen**
|
||||||
|
- Leere Datenbank aufsetzen, letztes konsistentes Backup einspielen.
|
||||||
|
2. **Storage wiederherstellen**
|
||||||
|
- Hot‑Storage‑Backup auf Volumes/Buckets zurückspielen (z.B. Docker‑Volume `app-storage` oder zugeordneten Bucket).
|
||||||
|
- Archiv‑Buckets ggf. unverändert lassen, sofern noch intakt.
|
||||||
|
3. **App & Queues**
|
||||||
|
- App mit readonly/maintenance‑Flag starten, Queues gestoppt lassen.
|
||||||
|
- Konsistenzprüfungen (z.B. stichprobenartige Tenants, Events, Medien, Abrechnungsdaten).
|
||||||
|
4. **Queues wieder freigeben**
|
||||||
|
- Wenn die wichtigsten Funktionen wieder intakt sind, Queues/Horizon graduell zuschalten.
|
||||||
|
|
||||||
|
> TODO: Ergänze konkrete Kommandos (Migrationsstatus prüfen, Health‑Checks) und definierte RTO/RPO‑Ziele.
|
||||||
|
|
||||||
|
## 4. Tests & DR-Übungen
|
||||||
|
|
||||||
|
- Mindestens 1–2 Mal pro Jahr einen vollständigen Restore in einer separaten Umgebung durchspielen:
|
||||||
|
- DB‑Backup einspielen.
|
||||||
|
- Medien‑Backups anbinden.
|
||||||
|
- Eine Handvoll Tenants/Events kompletter durchklicken (Upload, Galerie, Admin‑Funktionen).
|
||||||
|
- Ergebnisse im `docs/process/changes/`‑Ordner dokumentieren (z.B. „DR‑Übung 2026‑Q1“ mit Learnings).
|
||||||
|
|
||||||
|
## 5. Verantwortlichkeiten
|
||||||
|
|
||||||
|
- **Backup-Ownership**: Wer stellt sicher, dass Backups laufen und testweise wiederhergestellt werden?
|
||||||
|
- **DR-Ownership**: Wer führt die DR‑Übungen durch und wer entscheidet im Ernstfall über Failover/Restore?
|
||||||
|
|
||||||
|
Diese Punkte sollten mit konkreten Namen/Rollen befüllt werden, damit im Ernstfall keine Unklarheiten bestehen.
|
||||||
|
|
||||||
|
## 6. Ergänzende DR-Playbooks
|
||||||
|
|
||||||
|
Spezielle DR‑Szenarien sind in separaten Runbooks beschrieben:
|
||||||
|
|
||||||
|
- `docs/ops/dr-tenant-event-restore.md` – Vorgehen bei versehentlich gelöschten oder beschädigten Tenants/Events.
|
||||||
|
- `docs/ops/dr-storage-issues.md` – Vorgehen bei Hot‑/Archive‑Storage‑Problemen (voll, hängende Archivierung, fehlende Medien).
|
||||||
|
|
||||||
|
Dieses Dokument bleibt die High‑Level‑Übersicht – für konkrete Fälle solltest du immer auch die entsprechenden Playbooks konsultieren.
|
||||||
133
docs/ops/billing-ops.md
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
---
|
||||||
|
title: Billing & Zahlungs-Operationen
|
||||||
|
sidebar_label: Billing-Runbook
|
||||||
|
---
|
||||||
|
|
||||||
|
Dieses Runbook beschreibt, wie mit Zahlungsproblemen, Paddle/RevenueCat‑Webhooks und Paket‑Inkonsistenzen operativ umzugehen ist.
|
||||||
|
|
||||||
|
## 1. Komponentenüberblick
|
||||||
|
|
||||||
|
- **Paddle**
|
||||||
|
- Abwicklung von Web‑Checkout, Paketen und Subscriptions.
|
||||||
|
- Webhooks für Käufe, Verlängerungen, Stornos.
|
||||||
|
- **Fotospiel Backend**
|
||||||
|
- Modelle wie `Tenant`, `Packages`, `tenant_packages`, `event_packages`.
|
||||||
|
- Services/Jobs zur Paket‑Zuweisung, Limit‑Berechnung und Nutzungstracking.
|
||||||
|
|
||||||
|
> Details zur Architektur findest du in den PRP‑Kapiteln (Billing/Freemium) sowie in den TODO‑Dokumenten unter `docs/process/todo/paddle-migration.md` und `docs/process/todo/paddle-catalog-sync.md`.
|
||||||
|
|
||||||
|
## 2. Typische Problemszenarien
|
||||||
|
|
||||||
|
- **Webhook kommt nicht an / schlägt fehl**
|
||||||
|
- Symptom: Paddle zeigt Zahlung „completed“, Tenant‑Paket im Backend bleibt unverändert.
|
||||||
|
- Checkliste:
|
||||||
|
- Logs der Webhook‑Routes prüfen (Statuscodes, Exceptions).
|
||||||
|
- Endpoint: `POST /paddle/webhook` → `PaddleWebhookController::handle()`.
|
||||||
|
- Controller ruft `CheckoutWebhookService::handlePaddleEvent()` auf.
|
||||||
|
- Webhook‑Replay über das Paddle Dashboard auslösen (für einzelne Events).
|
||||||
|
- Queue‑Status prüfen:
|
||||||
|
- Falls Webhooks über Queues verarbeitet werden, auf `default`/`billing`‑Queues achten (je nach Konfiguration).
|
||||||
|
- **Doppelte oder fehlende Abbuchungen**
|
||||||
|
- Abgleich von Zahlungsprovider‑Daten (Paddle/RevenueCat) mit internem Ledger.
|
||||||
|
- Bei doppelten Buchungen: Prozess definieren (Refund via Paddle, Anpassung im Ledger).
|
||||||
|
- Bei fehlenden Buchungen: ggf. manuelle Paketzuweisung nach erfolgter Zahlung.
|
||||||
|
- **Pakete/Limits passen nicht zur Realität**
|
||||||
|
- Tenant meldet: „Paket falsch“, „Galerie schon abgelaufen“ o.Ä.
|
||||||
|
- Prüfen:
|
||||||
|
- Aktives Paket (`tenant_packages`, `event_packages`).
|
||||||
|
- Limit‑Zähler (`used_photos`, `used_events`) und aktuelle Nutzung.
|
||||||
|
- Letzte relevante Webhooks/Jobs (z.B. vor kurzem migriert?).
|
||||||
|
|
||||||
|
## 3. Operative Schritte bei Payment Incidents
|
||||||
|
|
||||||
|
1. **Event/Tenant identifizieren**
|
||||||
|
- IDs und relevante Paket‑Infos aus DB/Admin UI holen.
|
||||||
|
2. **Provider-Status prüfen**
|
||||||
|
- Paddle‑Dashboard: ist die Zahlung dort korrekt verbucht? (Transaktions‑/Abonnement‑Ansicht).
|
||||||
|
3. **Backend-Status prüfen**
|
||||||
|
- Paketzuweisung und Limits in der DB (Read‑only!) inspizieren:
|
||||||
|
- `checkout_sessions` – wurde die Session korrekt auf `completed` gesetzt? (`provider = paddle`, `paddle_transaction_id` gefüllt?)
|
||||||
|
- `package_purchases` – existiert ein Eintrag für Tenant/Package mit erwarteter Provider‑Referenz?
|
||||||
|
- `tenant_packages` – stimmt der `active`‑Status und `expires_at` mit dem erwarteten Abostatus überein?
|
||||||
|
4. **Entscheidung**
|
||||||
|
- Automatische Nachverarbeitung via Webhook‑Replay/Job‑Retry:
|
||||||
|
- Paddle‑Events erneut senden lassen, ggf. `tests/api/_testing/checkout/sessions/{session}/simulate-paddle` (in Test‑Umgebungen) nutzen.
|
||||||
|
- Notfall: manuelle Paket‑Anpassung (nur mit klar dokumentierter Begründung):
|
||||||
|
- Paket in `tenant_packages` aktivieren/verlängern und `package_purchases` sauber nachziehen.
|
||||||
|
5. **Dokumentation**
|
||||||
|
- Vorgang im Ticket / `docs/process/changes/*` festhalten, falls wiederkehrend.
|
||||||
|
|
||||||
|
> TODO: Ergänze konkrete Tabellen-/Modellnamen und die relevanten Jobs/Artisan Commands, sobald Paddle/RevenueCat Migration finalisiert ist.
|
||||||
|
|
||||||
|
## 4. Zusammenarbeit mit Finance/Support
|
||||||
|
|
||||||
|
- Klar definieren, wer Rückerstattungen freigibt und durchführt.
|
||||||
|
- Playbook für Support:
|
||||||
|
- Welche Informationen sie sammeln sollen, bevor sie an Ops eskalieren (Tenant‑ID, Event‑ID, Payment‑Provider‑Referenz, Zeitstempel).
|
||||||
|
- Welche Standardantworten es gibt (z.B. „Zahlung in Prüfung, Paket kurzfristig manuell freigeschaltet“).
|
||||||
|
|
||||||
|
## 5. Hinweise zur Implementierung
|
||||||
|
|
||||||
|
- **Konfiguration**
|
||||||
|
- Paddle‑Keys, Webhook‑Secrets und Feature‑Flags sollten ausschließlich in `.env`/Config‑Dateien liegen und niemals im Code/Logs landen.
|
||||||
|
- Sandbox vs. Live‑Keys klar trennen; Ops sollte wissen, welche Umgebung gerade aktiv ist.
|
||||||
|
- **Sicherheit**
|
||||||
|
- Webhook‑Signaturen und Timestamps prüfen; bei verdächtigen Mustern (z.B. Replay‑Angriffe) Security‑Runbooks konsultieren.
|
||||||
|
- Keine sensiblen Payment‑Details in Applikations‑Logs ausgeben.
|
||||||
|
|
||||||
|
Diese Sektion ist bewusst generisch gehalten, damit sie auch nach Implementation der finalen Billing‑Architektur noch passt. Details zu Tabellen/Jobs sollten ergänzt werden, sobald die Paddle‑Migration abgeschlossen ist.
|
||||||
|
|
||||||
|
## 6. Konkrete Paddle-Flows im System
|
||||||
|
|
||||||
|
### 6.1 Checkout-Erstellung
|
||||||
|
|
||||||
|
- Marketing-Checkout / API:
|
||||||
|
- `MarketingController` und `PackageController` nutzen `PaddleCheckoutService::createCheckout()` (`App\Services\Paddle\PaddleCheckoutService`).
|
||||||
|
- Der Service:
|
||||||
|
- Stellt sicher, dass ein `paddle_customer_id` für den Tenant existiert (`PaddleCustomerService::ensureCustomerId()`).
|
||||||
|
- Baut Metadaten (`tenant_id`, `package_id`, optional `checkout_session_id`) für spätere Zuordnung.
|
||||||
|
- Ruft `POST /checkout/links` im Paddle‑API auf und erhält eine `checkout_url`.
|
||||||
|
- Ops-Sicht:
|
||||||
|
- Wenn `paddle_price_id` bei einem Package fehlt, wird kein Checkout erzeugt – Marketing‑UI zeigt entsprechende Fehlertexte (siehe `resources/lang/*/marketing.php`).
|
||||||
|
- Bei wiederkehrenden „checkout failed“‑Fehlern die Logs (`PaddleCheckoutService`, Controller) und Package‑Konfiguration prüfen.
|
||||||
|
|
||||||
|
### 6.2 Webhook-Verarbeitung & Idempotenz
|
||||||
|
|
||||||
|
- Endpoint: `POST /paddle/webhook` → `PaddleWebhookController::handle()`.
|
||||||
|
- Service: `CheckoutWebhookService` (`App\Services\Checkout\CheckoutWebhookService`):
|
||||||
|
- Unterscheidet zwischen **Transaktions‑Events** (`transaction.*`) und **Subscription‑Events** (`subscription.*`).
|
||||||
|
- Idempotenz:
|
||||||
|
- Nutzt ein Cache‑Lock (`checkout:webhook:paddle:{transaction_id|session_id}`), um parallele Verarbeitung desselben Events zu verhindern.
|
||||||
|
- Schreibt Metadaten (`paddle_last_event`, `paddle_status`, `paddle_checkout_id`) in `checkout_sessions.provider_metadata`.
|
||||||
|
- Ergebnis:
|
||||||
|
- Bei `transaction.completed`:
|
||||||
|
- `CheckoutSession` wird als `processing` markiert.
|
||||||
|
- `CheckoutAssignmentService::finalise()` weist Paket/Tenant zu.
|
||||||
|
- Session wird auf `completed` gesetzt.
|
||||||
|
- Bei `transaction.failed` / `transaction.cancelled`:
|
||||||
|
- Session wird auf `failed` gesetzt, Coupons werden als fehlgeschlagen markiert.
|
||||||
|
|
||||||
|
### 6.3 Subscriptions & TenantPackages
|
||||||
|
|
||||||
|
- Subscription‑Events (`subscription.*`) werden ebenfalls von `CheckoutWebhookService` behandelt:
|
||||||
|
- Tenant wird aus `metadata.tenant_id` oder `paddle_customer_id` ermittelt.
|
||||||
|
- Package wird über `metadata.package_id` oder `paddle_price_id` aufgelöst.
|
||||||
|
- `TenantPackage` wird erstellt/aktualisiert (`paddle_subscription_id`, `expires_at`, `active`).
|
||||||
|
- `Tenant.subscription_status` und `subscription_expires_at` werden gesteuert.
|
||||||
|
- Ops-Sicht:
|
||||||
|
- Bei abweichenden Abostatus (z.B. Paddle zeigt „active“, Tenant nicht):
|
||||||
|
- Subscription‑Events im Paddle‑Dashboard prüfen.
|
||||||
|
- Letzte `subscription.*`‑Events in den Logs, `TenantPackage`‑ und `Tenant`‑Felder gegenprüfen.
|
||||||
|
|
||||||
|
### 6.4 Paket- & Coupon-Synchronisation
|
||||||
|
|
||||||
|
- Pakete:
|
||||||
|
- Artisan‑Command `paddle:sync-packages` (`App\Console\Commands\PaddleSyncPackages`) stößt für ausgewählte oder alle Pakete `SyncPackageToPaddle`/`PullPackageFromPaddle` Jobs an.
|
||||||
|
- Sync‑Jobs nutzen `PaddleCatalogService`, um Produkte/Preise in Paddle zu erstellen/aktualisieren und `paddle_product_id`/`paddle_price_id` lokal zu pflegen.
|
||||||
|
- Coupons:
|
||||||
|
- `SyncCouponToPaddle`‑Job spiegelt interne Coupon‑Konfiguration in Paddle Discounts (`PaddleDiscountService`).
|
||||||
|
- Ops-Sicht:
|
||||||
|
- Bei Katalog‑Abweichungen `paddle:sync-packages --dry-run` verwenden, um Snapshots zu prüfen, bevor tatsächliche Änderungen gesendet werden.
|
||||||
|
- Fehlgeschlagene Syncs in den Logs (`Paddle package sync failed`, `Paddle discount sync failed`) beobachten.
|
||||||
|
|
||||||
|
Diese Untersektion soll dir als Operator helfen zu verstehen, wie Paddle‑Aktionen im System abgebildet sind und an welchen Stellen du im Fehlerfall ansetzen kannst.
|
||||||
90
docs/ops/compliance-dsgvo-ops.md
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
---
|
||||||
|
title: DSGVO & Compliance-Operationen
|
||||||
|
sidebar_label: DSGVO-Operationen
|
||||||
|
---
|
||||||
|
|
||||||
|
Dieses Runbook beschreibt praktische Abläufe für Datenschutz‑Anfragen, Datenlöschung und Aufbewahrungsfristen.
|
||||||
|
|
||||||
|
## 1. Grundprinzipien
|
||||||
|
|
||||||
|
- Gäste benötigen kein Konto; die meisten Daten sind Event‑ und Foto‑bezogen.
|
||||||
|
- Tenant Admins sind in der Regel Verantwortliche im Sinne der DSGVO, Fotospiel fungiert als Auftragsverarbeiter (je nach Vertragsmodell).
|
||||||
|
- Rechtsgrundlagen, Impressum und Datenschutzerklärungen werden über die Legal‑Pages im Admin verwaltet – dieses Dokument fokussiert sich auf **Betriebsprozesse**.
|
||||||
|
|
||||||
|
## 2. Typische Anfragen & Aktionen
|
||||||
|
|
||||||
|
- **Auskunftsanfragen**
|
||||||
|
- Gast möchte wissen, ob Fotos von ihm/ihr gespeichert sind.
|
||||||
|
- Typischer Ablauf:
|
||||||
|
- Gast an Tenant verweisen (wenn Veranstalter Ansprechpartner ist).
|
||||||
|
- Falls Fotospiel direkt handeln muss: Event/Foto‑IDs anhand bereitgestellter Infos identifizieren (z.B. Link, Screenshot, Zeitfenster).
|
||||||
|
- Relevante Datensätze in DB (Fotos, Likes, Meldungen) lokalisieren und dokumentieren.
|
||||||
|
- **Löschanfragen**
|
||||||
|
- Ein Gast/Tenant bittet um Löschung spezifischer Fotos oder eines ganzen Events.
|
||||||
|
- Vorgehen:
|
||||||
|
1. Identität und Berechtigung prüfen (z.B. Tenant Admin, verifizierte Anfrage).
|
||||||
|
2. Fotos/Ereignisse über Admin UI oder interne Tools löschen/archivieren.
|
||||||
|
- Sicherstellen, dass `event_media_assets` und Archiv‑Speicher ebenfalls bereinigt werden.
|
||||||
|
3. Prüfen, ob Logs/Audits pseudonymisiert bleiben können, ohne personenbezogene Inhalte.
|
||||||
|
4. Anfrage und Zeitpunkt dokumentieren (Ticket, internes Log).
|
||||||
|
- **Datenexport**
|
||||||
|
- Tenant will einen Export seiner Daten (Events, Medien, Statistiken).
|
||||||
|
- Nutzung der vorhandenen Export‑Funktionen (z.B. CSV/ZIP im Admin) bevorzugen.
|
||||||
|
- Falls diese nicht ausreichen, manuelle Exporte via Skript/DB mit Datenschutz im Blick (keine unnötigen Felder).
|
||||||
|
- Beispiel: nur die Felder exportieren, die für den Zweck der Anfrage wirklich notwendig sind (kein Debug‑Log, keine internen IDs, sofern nicht sinnvoll).
|
||||||
|
|
||||||
|
### 2.1 Konkrete Tools & Endpoints
|
||||||
|
|
||||||
|
- **Profil-Datenexport (User-Ebene)**
|
||||||
|
- Controller: `App\Http\Controllers\ProfileDataExportController`.
|
||||||
|
- UI: Profilbereich der Tenant Admin PWA; Nutzer kann dort einen Export anstoßen.
|
||||||
|
- Ablauf:
|
||||||
|
1. Nutzer triggert Export → `ProfileDataExportController::store()` legt einen `DataExport`‑Eintrag an (`status = pending`) und dispatcht `GenerateDataExport`.
|
||||||
|
2. Der Job `GenerateDataExport` erstellt ein ZIP mit relevanten Daten und setzt `status = ready`, `path`, ggf. `expires_at`.
|
||||||
|
3. Nutzer kann die Datei über `ProfileDataExportController::download()` abrufen, solange `isReady()` und nicht `hasExpired()`.
|
||||||
|
- Ops-Sicht:
|
||||||
|
- Wenn Exporte „hängen bleiben“ (lange `pending`/`processing`), Queue/Horizon und Logs prüfen, ggf. Job neu anstoßen.
|
||||||
|
- Für DSGVO‑Exports bevorzugt diesen Pfad nutzen, statt ad‑hoc DB‑Abfragen.
|
||||||
|
|
||||||
|
- **Account-Anonymisierung (User/Tenant-Ebene)**
|
||||||
|
- Service: `App\Services\Compliance\AccountAnonymizer`.
|
||||||
|
- Job: `App\Jobs\AnonymizeAccount` nutzt diesen Service typischerweise im Hintergrund.
|
||||||
|
- Verhalten:
|
||||||
|
- Löscht/entfernt Medien (`EventMediaAsset`, `Photo`) für einen Tenant.
|
||||||
|
- Anonymisiert Tenant‑ und User‑Daten (setzt neutrale Namen, entfernt Kontaktinfos, sperrt Accounts).
|
||||||
|
- Ops-Sicht:
|
||||||
|
- Nur nach klarer Freigabe einsetzen, da Anonymisierung irreversibel ist.
|
||||||
|
- Vor Einsatz prüfen, ob für den betreffenden Tenant alle vertraglichen Zusagen (z.B. Datenexport) erfüllt sind.
|
||||||
|
|
||||||
|
## 3. Retention & automatisierte Löschung
|
||||||
|
|
||||||
|
- **Event-bezogene Aufbewahrung**
|
||||||
|
- Standard‑Retentionsfristen für Events/Fotos (z.B. X Tage nach Eventende/Archivierung) laut Produkt‑Spezifikation.
|
||||||
|
- Jobs/Kommandos, die nach Ablauf Medien archivieren oder löschen (siehe `docs/ops/media-storage-spec.md`).
|
||||||
|
- **Logs**
|
||||||
|
- Aufbewahrungsdauer von Applikations‑Logs (z.B. 30–90 Tage), Rotation/Anonymisierung.
|
||||||
|
- **Konfiguration pro Tenant**
|
||||||
|
- Wenn Tenants eigene Retention wünschen, prüfen ob das UI/Config‑Model dies unterstützt (nicht ad‑hoc in SQL ändern).
|
||||||
|
|
||||||
|
## 4. Operative Checkliste bei DSGVO-Fällen
|
||||||
|
|
||||||
|
1. Anfrage klassifizieren (Auskunft, Löschung, Export, Sonstiges).
|
||||||
|
2. Verantwortlichkeit klären (Tenant vs. Fotospiel).
|
||||||
|
3. Technische Schritte definieren (welche Events/Fotos/Accounts betroffen).
|
||||||
|
4. Durchführung:
|
||||||
|
- In Admin UI oder via internen Tools.
|
||||||
|
- Medien/Metadaten konsistent behandeln (keine „hängenden“ Records).
|
||||||
|
5. Dokumentation:
|
||||||
|
- Ticket/E-Mail‑Thread mit Datum, Betreff, Maßnahmen.
|
||||||
|
6. Follow‑Up:
|
||||||
|
- Prüfen, ob Runbooks/Automatisierungen angepasst werden sollten (z.B. besserer Self‑Service für Tenants).
|
||||||
|
|
||||||
|
## 5. Verbindung zu Security-Hardening
|
||||||
|
|
||||||
|
Das Security‑Hardening‑Epic (`docs/process/todo/security-hardening-epic.md`) enthält mehrere Workstreams, die DSGVO‑relevant sind:
|
||||||
|
|
||||||
|
- Signierte Asset‑URLs statt direkter Storage‑Links.
|
||||||
|
- Verbesserte Token‑/Auth‑Flows.
|
||||||
|
- Storage‑Health und Checksummen‑Verifizierung.
|
||||||
|
|
||||||
|
Wenn dort neue Features produktiv gehen, sollten die Auswirkungen auf DSGVO‑Prozesse in diesem Runbook ergänzt werden.
|
||||||
@@ -67,7 +67,7 @@ If you already have data, skip the seeder or seed only new records.
|
|||||||
|
|
||||||
## 5. Queue & Horizon management
|
## 5. Queue & Horizon management
|
||||||
|
|
||||||
Worker entrypoints live in `docs/queue-supervisor/`. The Compose services mount the same application volume so code stays in sync. Adjust concurrency by scaling services:
|
Worker entrypoints live in `/scripts/` inside the container (copied from the repository’s `scripts/` folder). The Compose services mount the same application volume so code stays in sync. Adjust concurrency by scaling services:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose up -d --scale queue=2 --scale media-storage-worker=2
|
docker compose up -d --scale queue=2 --scale media-storage-worker=2
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ Dokploy is our self-hosted PaaS for orchestrating the Fotospiel stack (Laravel a
|
|||||||
|---------|-------|
|
|---------|-------|
|
||||||
| **Laravel App** | Build from this repository. Expose port 8080 (or Dokploy HTTP service). Attach the production `.env`. Health check `/up`. |
|
| **Laravel App** | Build from this repository. Expose port 8080 (or Dokploy HTTP service). Attach the production `.env`. Health check `/up`. |
|
||||||
| **Scheduler** | Clone the app container; command `php artisan schedule:work`. |
|
| **Scheduler** | Clone the app container; command `php artisan schedule:work`. |
|
||||||
| **Queue workers** | Use `docs/queue-supervisor/queue-worker.sh` scripts (default, media-storage, media-security). Deploy each as a dedicated Dokploy application or Docker service. |
|
| **Queue workers** | Use the `/scripts/queue-worker.sh` entrypoints (default, media-storage, media-security). Deploy each as a dedicated Dokploy application or Docker service. |
|
||||||
| **Horizon (optional)** | Run `docs/queue-supervisor/horizon.sh` for dashboard + metrics. |
|
| **Horizon (optional)** | Run `/scripts/horizon.sh` for dashboard + metrics. |
|
||||||
| **Redis / Database** | Use managed offerings or self-host in Dokploy. Configure network access for the app + workers. |
|
| **Redis / Database** | Use managed offerings or self-host in Dokploy. Configure network access for the app + workers. |
|
||||||
| **vsftpd container** | Expose port 2121 and mount the shared Photobooth volume. |
|
| **vsftpd container** | Expose port 2121 and mount the shared Photobooth volume. |
|
||||||
| **Photobooth Control Service** | Lightweight API (Go/Node/Laravel Octane) that can be redeployed together with vsftpd for ingest controls. |
|
| **Photobooth Control Service** | Lightweight API (Go/Node/Laravel Octane) that can be redeployed together with vsftpd for ingest controls. |
|
||||||
@@ -72,10 +72,10 @@ Follow these steps for each component:
|
|||||||
3. **Queue workers**
|
3. **Queue workers**
|
||||||
- Duplicate the image.
|
- Duplicate the image.
|
||||||
- Commands:
|
- Commands:
|
||||||
- `docs/queue-supervisor/queue-worker.sh default`
|
- `/var/www/html/scripts/queue-worker.sh default`
|
||||||
- `docs/queue-supervisor/queue-worker.sh media-storage`
|
- `/var/www/html/scripts/queue-worker.sh media-storage`
|
||||||
- `docs/queue-supervisor/queue-worker.sh media-security`
|
- `/var/www/html/scripts/queue-worker.sh media-security`
|
||||||
- Optionally create a dedicated container for Horizon using `docs/queue-supervisor/horizon.sh`.
|
- Optionally create a dedicated container for Horizon using `/var/www/html/scripts/horizon.sh`.
|
||||||
|
|
||||||
4. **vsftpd + Photobooth control**
|
4. **vsftpd + Photobooth control**
|
||||||
- Nutze deinen bestehenden Docker-Compose-Stack (z. B. `docker-compose.dokploy.yml`) oder dedizierte Compose-Applikationen.
|
- Nutze deinen bestehenden Docker-Compose-Stack (z. B. `docker-compose.dokploy.yml`) oder dedizierte Compose-Applikationen.
|
||||||
|
|||||||
42
docs/ops/diagrams.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
title: Architekturdiagramme
|
||||||
|
sidebar_label: Diagramme
|
||||||
|
---
|
||||||
|
|
||||||
|
Diese Seite bündelt einfache Diagramme für zentrale Plattform‑Flows. Sie sind absichtlich high‑level gehalten und sollen neuen Operatoren einen schnellen Überblick geben.
|
||||||
|
|
||||||
|
## 1. Medien‑Pipeline (Mermaid)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
Guest[Guest PWA] -->|Foto upload| API[Laravel API]
|
||||||
|
API -->|Validierung & DB| DB[(DB: events,\nevent_media_assets)]
|
||||||
|
API -->|Datei schreiben| HotStorage[(Hot Storage\n/var/www/storage)]
|
||||||
|
|
||||||
|
HotStorage --> QueueMedia[Queue: media-storage]
|
||||||
|
QueueMedia --> WorkerMedia[Worker: media-storage-worker]
|
||||||
|
WorkerMedia --> Archive[(Archive Storage\nz.B. S3/Wasabi)]
|
||||||
|
|
||||||
|
WorkerMedia --> Thumbs[Job: Thumbnails]
|
||||||
|
Thumbs --> HotStorage
|
||||||
|
|
||||||
|
DB --> PublicAPI[Public API]
|
||||||
|
PublicAPI --> Guest
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Checkout & Billing (Mermaid)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
Tenant[Browser Tenant-Admin] -->|Paket wählen| App[Laravel App]
|
||||||
|
App -->|CheckoutSession anlegen| DB[(DB: checkout_sessions,\n tenant_packages)]
|
||||||
|
App -->|Redirect| Paddle[Paddle Checkout]
|
||||||
|
|
||||||
|
Paddle -->|Zahlung erfolgreich| Webhook[Paddle Webhook Endpoint]
|
||||||
|
Webhook -->|Event verarbeiten| BillingService[CheckoutWebhookService]
|
||||||
|
BillingService -->|TenantPackage aktualisieren| DB
|
||||||
|
|
||||||
|
DB --> App
|
||||||
|
App --> Tenant
|
||||||
|
```
|
||||||
|
|
||||||
74
docs/ops/dr-storage-issues.md
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
---
|
||||||
|
title: DR-Playbook – Storage-Probleme (Hot/Archive)
|
||||||
|
sidebar_label: DR – Storage
|
||||||
|
---
|
||||||
|
|
||||||
|
Dieses Playbook beschreibt, wie du vorgehst, wenn es Probleme mit dem Medien‑Storage gibt – z.B. Hot‑Storage voll, Archivierung bleibt hängen oder viele Assets im Status `failed`.
|
||||||
|
|
||||||
|
## 1. Symptome & erste Checks
|
||||||
|
|
||||||
|
Typische Symptome:
|
||||||
|
|
||||||
|
- Gäste können keine Fotos mehr hochladen (Fehlermeldungen im Upload‑Flow).
|
||||||
|
- Tenant Admins sehen „fehlende Medien“ oder sehr langsame Galerien.
|
||||||
|
- `event_media_assets` enthält viele Einträge mit Status `pending` oder `failed`.
|
||||||
|
- Logs enthalten Hinweise auf fehlgeschlagene Archivierungen oder fehlende Dateien.
|
||||||
|
|
||||||
|
Erste Checks:
|
||||||
|
|
||||||
|
- Storage‑Usage der Hot‑Volumes/Buckets prüfen (Docker‑Volume, S3‑Dashboard o.ä.).
|
||||||
|
- `EventMediaAsset`‑Status stichprobenartig prüfen (`hot`, `archived`, `pending`, `failed`).
|
||||||
|
- Queue‑Längen und Fehler in `media-storage` und `media-security` via Horizon und Logs.
|
||||||
|
|
||||||
|
## 2. Hot-Storage voll oder kurz vor Limit
|
||||||
|
|
||||||
|
1. **Warnungen bestätigen**
|
||||||
|
- System‑/Provider‑Warnungen (z.B. 90 % voll) bestätigen.
|
||||||
|
- Prüfen, ob `storage:monitor` oder ähnliche Kommandos bereits Alerts ausgelöst haben.
|
||||||
|
2. **Sofortmaßnahmen**
|
||||||
|
- Archivierung priorisieren: sicherstellen, dass `storage:archive-pending` regelmäßig läuft und die `media-storage`‑Queue abgearbeitet wird.
|
||||||
|
- Temporäre Limits erhöhen, falls Provider dies erlaubt (z.B. S3‑Bucket praktisch „unbegrenzt“ vs. lokaler Disk).
|
||||||
|
3. **Aufräumen**
|
||||||
|
- Alte Caches/Thumbnails, die problemlos neu generiert werden können, ggf. gezielt löschen.
|
||||||
|
- Keine unüberlegten `rm -rf` Aktionen auf dem Storage – immer mit klarer Strategie arbeiten.
|
||||||
|
|
||||||
|
## 3. Archivierung hängt oder schlägt häufig fehl
|
||||||
|
|
||||||
|
1. **Queue-Status prüfen**
|
||||||
|
- `media-storage` Queue‑Länge, Failed Jobs in Horizon prüfen.
|
||||||
|
- Log‑Channel `storage-jobs` nach Fehlermeldungen durchsuchen.
|
||||||
|
2. **Fehlerbilder auswerten**
|
||||||
|
- Typische Ursachen:
|
||||||
|
- Netzwerk‑/Credential‑Probleme beim Zugriff auf den Archiv‑Bucket.
|
||||||
|
- Zeitüberschreitungen bei sehr großen Medien.
|
||||||
|
- Inkonsistente `EventMediaAsset`‑Einträge (Pfad nicht mehr vorhanden, falscher Disk‑Key).
|
||||||
|
3. **Abhilfe**
|
||||||
|
- Netzwerk/Credentials fixen (z.B. S3‑Keys, Endpoints, Rechte).
|
||||||
|
- Problematische Assets gezielt in den Logs identifizieren und manuell nachziehen (Kopie auf Archive‑Disk, Status auf `archived` setzen, Fehler‑Feld leeren).
|
||||||
|
- Wenn viele Assets betroffen sind, lieber ein dediziertes Skript/Job bauen als ad‑hoc SQL.
|
||||||
|
|
||||||
|
## 4. Fehlende oder beschädigte Medien-Dateien
|
||||||
|
|
||||||
|
Wenn `EventMediaAsset`‑Einträge existieren, die zu nicht mehr vorhandenen Dateien zeigen:
|
||||||
|
|
||||||
|
1. **Umfang ermitteln**
|
||||||
|
- Stichproben auf Basis der Fehlerlogs oder per Batch‑Check (z.B. ein Artisan‑Command, das `exists()` prüft).
|
||||||
|
2. **Backup-Sicht**
|
||||||
|
- Prüfen, ob die Dateien noch im Backup vorhanden sind (Hot‑/Archive‑Backups).
|
||||||
|
3. **Wiederherstellung**
|
||||||
|
- Fehlende Dateien an den erwarteten Pfad im Storage kopieren (Hot oder Archive).
|
||||||
|
- `EventMediaAsset`‑Status und Timestamps ggf. aktualisieren (`hot` vs. `archived`).
|
||||||
|
|
||||||
|
Wenn keine Backups existieren, bleibt nur, die betroffenen Assets sauber als „nicht mehr verfügbar“ zu kennzeichnen und die Nutzer entsprechend zu informieren.
|
||||||
|
|
||||||
|
## 5. Nach einem Storage-Incident
|
||||||
|
|
||||||
|
- **Monitoring schärfen**
|
||||||
|
- Schwellwerte in `storage-monitor` anpassen (Warnung/Kritisch), Alerts für Queues/Storage erweitern.
|
||||||
|
- **Kapazitätsplanung**
|
||||||
|
- Erkenntnisse über Medienwachstum nutzen, um frühzeitig auf größere Volumes/Buckets oder häufigere Archivierung umzusteigen.
|
||||||
|
- **Dokumentation**
|
||||||
|
- Incident und Maßnahmen in `docs/process/changes/*` dokumentieren.
|
||||||
|
- Dieses Playbook aktualisieren, wenn neue Muster entdeckt wurden.
|
||||||
|
|
||||||
|
Dieses Playbook ist eng mit `docs/ops/media-storage-spec.md` und `docs/ops/monitoring-observability.md` verknüpft. Nutze diese Dokumente für Detailinformationen zu Queues, Thresholds und Storage‑Targets.
|
||||||
97
docs/ops/dr-tenant-event-restore.md
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
---
|
||||||
|
title: DR-Playbook – Tenant/Event versehentlich gelöscht
|
||||||
|
sidebar_label: DR – Tenant/Event
|
||||||
|
---
|
||||||
|
|
||||||
|
Dieses Playbook beschreibt, wie du vorgehst, wenn ein Tenant oder Event versehentlich gelöscht oder stark beschädigt wurde. Es baut auf den allgemeinen Hinweisen aus `docs/ops/backup-restore.md` auf.
|
||||||
|
|
||||||
|
> Wichtig: Diese Schritte sollten zuerst in einer **Staging-Umgebung** geübt werden. In Produktion nur nach klarer Freigabe und mit sauberer Dokumentation anwenden.
|
||||||
|
|
||||||
|
## 1. Schadensbild erfassen
|
||||||
|
|
||||||
|
Bevor du irgendetwas wiederherstellst:
|
||||||
|
|
||||||
|
- **Was genau ist betroffen?**
|
||||||
|
- Nur ein Event (z.B. versehentlich im Admin archiviert/gelöscht)?
|
||||||
|
- Mehrere Events eines Tenants?
|
||||||
|
- Der komplette Tenant (inkl. Benutzer, Events, Pakete)?
|
||||||
|
- **Welche Daten fehlen/fehlerhaft?**
|
||||||
|
- Fehlen nur Metadaten (Events, Fotos, Pakete) oder auch Medien‑Dateien?
|
||||||
|
- Gibt es noch Spuren im Admin/UI (z.B. leere Übersichten, aber Logs mit Fehlermeldungen)?
|
||||||
|
- **Zeitfenster eingrenzen**
|
||||||
|
- Wann wurde der Fehler bemerkt?
|
||||||
|
- Wann war der Zustand sicher noch korrekt (z.B. vor letztem Deploy / gestern Abend)?
|
||||||
|
|
||||||
|
Diese Informationen bestimmen, welches Backup verwendet werden sollte.
|
||||||
|
|
||||||
|
## 2. Logische vs. physische Schäden unterscheiden
|
||||||
|
|
||||||
|
- **Logischer Schaden**
|
||||||
|
- Falsche Flags (Status falsch, Event „archiviert“ statt „aktiv“).
|
||||||
|
- Inkompatible Paket‑Zuweisungen, aber Daten sind noch vorhanden.
|
||||||
|
- Lösbare Fälle oft ohne Restore durch gezielte Updates / Admin‑UI.
|
||||||
|
- **Physischer Schaden**
|
||||||
|
- Reihen in Kern‑Tabellen gelöscht (z.B. `events`, `photos`, `event_media_assets`, `tenants`).
|
||||||
|
- Medien‑Dateien im Storage gelöscht/überschrieben.
|
||||||
|
|
||||||
|
Nur bei physischen Schäden ist ein Restore aus Backup nötig. Logische Schäden sollten möglichst mit minimalinvasiven Korrekturen behoben werden.
|
||||||
|
|
||||||
|
## 3. Vorgehen bei einzelnen Events
|
||||||
|
|
||||||
|
### 3.1 Datenbank – Event-Datensätze identifizieren
|
||||||
|
|
||||||
|
1. **Event-IDs ermitteln**
|
||||||
|
- Aus Logs, alten Links, Metriken oder Backups.
|
||||||
|
2. **Querverweise prüfen**
|
||||||
|
- `events` (Basisdaten), `photos`, `event_media_assets`, `event_packages`, ggf. `event_join_tokens`.
|
||||||
|
3. **Temporäre Restore-DB nutzen**
|
||||||
|
- Erzeuge eine temporäre Datenbank (z.B. `fotospiel_restore`) und spiele den relevanten Backup‑Dump ein.
|
||||||
|
- Dort die betroffenen Event‑Datensätze suchen.
|
||||||
|
|
||||||
|
### 3.2 Selektiver Restore von Event-Daten
|
||||||
|
|
||||||
|
Empfohlenes Muster:
|
||||||
|
|
||||||
|
- In **Restore-DB**:
|
||||||
|
- Exportiere alle relevanten Zeilen für das Event (z.B. `events`, `photos`, `event_media_assets`) in SQL/CSV.
|
||||||
|
- In **Produktions-DB**:
|
||||||
|
- Prüfe, ob IDs kollidieren (z.B. neue Events seit dem Backup).
|
||||||
|
- Freie IDs und referentielle Integrität beachten; wenn IDs bereits vergeben sind, ist ein reiner Import meist nicht möglich → dann manuelle Rekonstruktion (neues Event + Medien erneut verknüpfen).
|
||||||
|
|
||||||
|
> Dieses Playbook beschreibt bewusst kein generisches „SQL-Skript“, weil die tatsächliche Struktur und IDs von der aktuellen Migration abhängen. Ziel ist, die **Vorgehensweise** zu standardisieren, nicht ein unüberlegtes Massen‑Update.
|
||||||
|
|
||||||
|
### 3.3 Medien-Dateien
|
||||||
|
|
||||||
|
1. In der Restore‑Umgebung prüfen, welche Pfade `event_media_assets` für das Event referenzieren.
|
||||||
|
2. Im Backup‑Storage nach diesen Pfaden suchen (Hot‑ und ggf. Archiv‑Bucket).
|
||||||
|
3. Fehlende Dateien in das produktive Storage‑Volume/Bucket an den erwarteten Pfad kopieren.
|
||||||
|
|
||||||
|
Wenn die Medien physisch nicht mehr vorhanden sind, ist nur eine teilweise Rekonstruktion möglich (z.B. Thumbnails ohne Originale) – das sollte mit dem Tenant klar kommuniziert werden.
|
||||||
|
|
||||||
|
## 4. Vorgehen bei Tenant-weiten Fehlern
|
||||||
|
|
||||||
|
Wenn ein kompletter Tenant versehentlich gelöscht wurde (inkl. Benutzer/Events):
|
||||||
|
|
||||||
|
1. **Einordnung**
|
||||||
|
- Handelt es sich um einen isolierten Tenant oder könnten mehrere betroffen sein (z.B. durch fehlerhaftes Skript)?
|
||||||
|
2. **Restore-Strategie wählen**
|
||||||
|
- _Variante A: Partial Restore_ – nur die Tabellenzeilen zum Tenant aus der Backup‑DB in die Produktions‑DB zurückführen.
|
||||||
|
- _Variante B: Backup-Spiegel_ – Tenant + zugehörige Medien in eine separate Umgebung wiederherstellen und dem Kunden dort einen temporären Zugang geben.
|
||||||
|
3. **Risikoabwägung**
|
||||||
|
- Partial Restore in eine laufende Produktions‑DB trägt höhere Risiken (Kollisionsgefahr mit neuen Daten).
|
||||||
|
- Spiegel‑Variante ist operativ aufwändiger, kann aber sicherer sein, wenn viele neue Daten seit dem Backup hinzugekommen sind.
|
||||||
|
|
||||||
|
> Welche Variante gewählt wird, sollte von Platform‑Ops + Produkt gemeinsam entschieden werden.
|
||||||
|
|
||||||
|
## 5. Kommunikation & Dokumentation
|
||||||
|
|
||||||
|
- **Mit dem betroffenen Tenant**
|
||||||
|
- Ehrlich kommunizieren, was passiert ist, was wiederherstellbar ist und welches Risiko ein Restore birgt.
|
||||||
|
- Zeitrahmen und mögliche Einschränkungen klar benennen.
|
||||||
|
- **Intern**
|
||||||
|
- Den gesamten Prozess in einem `docs/process/changes/*`‑Eintrag oder im Ticketing festhalten:
|
||||||
|
- Was, wann, warum schief ging.
|
||||||
|
- Welche Restore‑Schritte durchgeführt wurden.
|
||||||
|
- Welche Verbesserungen künftig notwendig sind (z.B. bessere Schutzmechanismen, zusätzliche Bestätigungen beim Löschen).
|
||||||
|
|
||||||
|
Dieses Playbook ist bewusst höher‑levelig gehalten; spezifische SQL‑ oder Tool‑Snippets sollten ergänzend in einem internen Notizsystem oder als separate Anhänge gepflegt werden, sobald eure Backup‑Pipelines stabil etabliert sind.
|
||||||
@@ -22,7 +22,7 @@ This runbook explains how to keep the guest notification centre healthy, roll ou
|
|||||||
Push deliveries are dispatched on the dedicated `notifications` queue. Ensure one of the queue workers listens to it:
|
Push deliveries are dispatched on the dedicated `notifications` queue. Ensure one of the queue workers listens to it:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docs/queue-supervisor/queue-worker.sh default,notifications
|
/var/www/html/scripts/queue-worker.sh default,notifications
|
||||||
```
|
```
|
||||||
|
|
||||||
If Horizon is in use just add `notifications` to the list of queues for at least one supervisor. Monitor `storage/logs/notifications.log` (channel `notifications`) for transport failures.
|
If Horizon is in use just add `notifications` to the list of queues for at least one supervisor. Monitor `storage/logs/notifications.log` (channel `notifications`) for transport failures.
|
||||||
|
|||||||
79
docs/ops/howto-dsgvo-delete-photo.md
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
---
|
||||||
|
id: howto-dsgvo-delete-photo
|
||||||
|
title: How-to – DSGVO-Löschung eines Fotos
|
||||||
|
sidebar_label: DSGVO – Foto löschen
|
||||||
|
---
|
||||||
|
|
||||||
|
Dieses How‑to beschreibt den operativen Ablauf, wenn ein Gast verlangt, dass ein konkretes Foto DSGVO‑konform gelöscht wird.
|
||||||
|
|
||||||
|
## 1. Anfrage & Berechtigung prüfen
|
||||||
|
|
||||||
|
Bevor du etwas löschst:
|
||||||
|
|
||||||
|
- Handelt der Gast über den Veranstalter (Tenant) oder direkt bei Fotospiel?
|
||||||
|
- Kann das Foto eindeutig identifiziert werden?
|
||||||
|
- Am besten via Link zur Galerie / Foto‑Detailseite.
|
||||||
|
- Alternativ via Screenshot + Event‑Name + Zeitfenster.
|
||||||
|
- Ist der Tenant (Veranstalter) einverstanden?
|
||||||
|
- In der Regel sollte die Entscheidung, ob ein Foto gelöscht wird, beim Tenant liegen, sofern der Vertrag dies vorsieht.
|
||||||
|
|
||||||
|
Alle relevanten Informationen und Entscheidungen sollten im Ticket erfasst werden.
|
||||||
|
|
||||||
|
## 2. Foto im System identifizieren
|
||||||
|
|
||||||
|
1. Über die Admin‑UI:
|
||||||
|
- In der Tenant Admin PWA den betroffenen Event öffnen.
|
||||||
|
- Foto über Moderations‑/Galerieansicht suchen.
|
||||||
|
- ID des Fotos notieren (sofern sichtbar) oder den direkten Admin‑Link verwenden.
|
||||||
|
2. Falls nötig, über DB/Logs:
|
||||||
|
- Anhand von Dateinamen/URLs aus Logs (`event_media_assets.path`, `photos.thumbnail_path`) das Foto lokalisieren.
|
||||||
|
|
||||||
|
## 3. Löschung über Admin-UI (präferiert)
|
||||||
|
|
||||||
|
Wenn die Admin‑Oberfläche eine Delete/Hide‑Funktion bietet:
|
||||||
|
|
||||||
|
1. Tenant Admin das Foto über das Moderationsinterface löschen lassen.
|
||||||
|
2. Sicherstellen, dass:
|
||||||
|
- Foto nicht mehr in Galerie/Moderationslisten erscheint.
|
||||||
|
- Share‑Links oder öffentliche Galerien das Foto nicht mehr anzeigen.
|
||||||
|
3. Falls es trotzdem noch angezeigt wird:
|
||||||
|
- Caches prüfen (Browser, CDN, ggf. Thumbnail‑Caches).
|
||||||
|
|
||||||
|
## 4. Technischer Löschpfad (Backend)
|
||||||
|
|
||||||
|
Falls eine UI‑Löschung nicht ausreicht oder du nachkontrollieren willst:
|
||||||
|
|
||||||
|
1. **Datenbank**
|
||||||
|
- `photos`:
|
||||||
|
- Prüfen, dass der Eintrag für das Foto gelöscht oder hinreichend anonymisiert wurde.
|
||||||
|
- `event_media_assets`:
|
||||||
|
- Alle Einträge, die auf dieses Foto (`photo_id`) zeigen, identifizieren.
|
||||||
|
- Pfade (`disk`, `path`) notieren.
|
||||||
|
2. **Storage**
|
||||||
|
- Für alle relevanten `EventMediaAsset`‑Einträge:
|
||||||
|
- Dateien im Hot‑/Archive‑Storage löschen (Original + Derivate/Thumbnails).
|
||||||
|
3. **Verknüpfungen**
|
||||||
|
- Sicherstellen, dass keine weiteren Verweise existieren:
|
||||||
|
- Likes/Statistiken für dieses Foto (z.B. `photo_likes`) – optional mit entfernen, sofern vorhanden.
|
||||||
|
|
||||||
|
> Hinweis: Wenn ihr `AccountAnonymizer` auf Tenant‑/User‑Ebene verwendet, löscht dieser im Regelfall großflächig Medien. Für Einzelfälle (ein Foto) ist der oben skizzierte Weg geeigneter.
|
||||||
|
|
||||||
|
## 5. Dokumentation & Bestätigung
|
||||||
|
|
||||||
|
- Im Ticket festhalten:
|
||||||
|
- Welches Foto (Event, ID/URL).
|
||||||
|
- Wer die Löschung veranlasst und genehmigt hat.
|
||||||
|
- Welche Schritte tatsächlich durchgeführt wurden (UI, DB, Storage).
|
||||||
|
- Tenant/Gast informieren:
|
||||||
|
- Bestätigung, dass das Foto aus Galerie und Speicher entfernt wurde.
|
||||||
|
- Hinweis, dass ggf. Browser‑/CDN‑Caches eine kurze Zeit nachlaufen können, aber keine neuen Zugriffe mehr möglich sind.
|
||||||
|
|
||||||
|
## 6. Präventive Verbesserungen
|
||||||
|
|
||||||
|
Wenn dieser Vorgang häufig vorkommt:
|
||||||
|
|
||||||
|
- Prüfen, ob die Admin‑UI einen klareren, selbstbedienbaren Weg zur Foto‑Löschung bietet.
|
||||||
|
- Sicherstellen, dass die Dokumentation für Tenant Admins (siehe Help‑Center) erklärt, wie sie Fotos eigenständig löschen und wie sich das auf Gäste auswirkt.
|
||||||
|
|
||||||
|
Dieses How‑to ergänzt `docs/ops/compliance-dsgvo-ops.md` um einen konkreten Einzelfall. Für komplexere Anonymisierungs‑Szenarien siehe den Abschnitt zum `AccountAnonymizer`.
|
||||||
|
|
||||||
83
docs/ops/howto-guest-upload-failing.md
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
---
|
||||||
|
id: howto-guest-upload-failing
|
||||||
|
title: How-to – Gäste können nicht hochladen
|
||||||
|
sidebar_label: Gäste können nicht hochladen
|
||||||
|
---
|
||||||
|
|
||||||
|
Dieses How‑to beschreibt, wie du vorgehst, wenn Gäste melden, dass sie keine Fotos mehr hochladen können (Fehler im Upload‑Flow oder „hängenbleibende“ Uploads).
|
||||||
|
|
||||||
|
## 1. Problem eingrenzen
|
||||||
|
|
||||||
|
Fragen an den Tenant/Support:
|
||||||
|
|
||||||
|
- Betrifft es **alle** Gäste oder nur einzelne?
|
||||||
|
- Betrifft es **alle** Events oder nur ein bestimmtes Event?
|
||||||
|
- Welche Fehlermeldung erscheint im Guest‑Frontend (so genau wie möglich, gerne mit Screenshot)?
|
||||||
|
- Seit wann tritt das Problem auf? (Zeitfenster)
|
||||||
|
|
||||||
|
Diese Informationen bestimmen, ob du in Richtung API/Rate‑Limit, Storage/Queues oder Event‑Konfiguration schauen musst.
|
||||||
|
|
||||||
|
## 2. Basischecks – API & App
|
||||||
|
|
||||||
|
1. **Public-API Status**
|
||||||
|
- Teste manuell einen Upload gegen ein Test‑Event oder reproduziere das Problem mit dem betroffenen Join‑Token.
|
||||||
|
- Achte auf HTTP‑Statuscodes im Browser‑Network‑Tab (4xx vs. 5xx).
|
||||||
|
2. **App- / Deployment-Status**
|
||||||
|
- Prüfe in Docker/Dokploy, ob App/Queue/Redis/DB‑Container gesund sind.
|
||||||
|
- Schaue in `storage/logs/laravel.log` nach offensichtlichen Exceptions rund um das gemeldete Zeitfenster.
|
||||||
|
|
||||||
|
Wenn die Public‑API generell 5xx liefert, greift eher das Public‑API‑Incident‑Playbook (`docs/ops/deployment/public-api-incident-playbook.md`).
|
||||||
|
|
||||||
|
## 3. Queues & Upload-Health
|
||||||
|
|
||||||
|
Wenn das Problem hauptsächlich Uploads betrifft (andere Funktionen laufen):
|
||||||
|
|
||||||
|
1. **Queue-Längen prüfen**
|
||||||
|
- In Horizon:
|
||||||
|
- `media-storage`, `media-security` und ggf. `notifications` Queue‑Längen ansehen.
|
||||||
|
- In Logs:
|
||||||
|
- Warnungen aus `storage:check-upload-queues` oder `storage-jobs` suchen.
|
||||||
|
2. **Upload-Health-Command**
|
||||||
|
- Sicherstellen, dass `storage:check-upload-queues` regelmäßig läuft (Cron / Scheduler).
|
||||||
|
- Manuell ausführen (in der App‑Container‑Shell):
|
||||||
|
```bash
|
||||||
|
php artisan storage:check-upload-queues
|
||||||
|
```
|
||||||
|
- Ausgaben/Logs prüfen:
|
||||||
|
- Meldungen zu „stalled“ Uploads, Events mit dauerhaft vielen Pending‑Assets.
|
||||||
|
|
||||||
|
## 4. Storage & Limit-Probleme
|
||||||
|
|
||||||
|
1. **Hot-Storage-Füllstand**
|
||||||
|
- Prüfen, ob das Storage‑Volume/Bucket nahe an 100 % ist (siehe `docs/ops/dr-storage-issues.md`).
|
||||||
|
- Wenn ja:
|
||||||
|
- Archivierung beschleunigen (`storage:archive-pending` verifizieren).
|
||||||
|
- Kurzfristig Speicher vergrößern oder Caches aufräumen.
|
||||||
|
2. **Paket-/Limit-Prüfungen**
|
||||||
|
- Wenn nur bestimmte Events betroffen sind:
|
||||||
|
- Paket‑Limits des Events prüfen (z.B. max_photos/max_guests).
|
||||||
|
- Event‑Status (abgelaufen/archiviert?) prüfen.
|
||||||
|
- Logs können Fehlercodes liefern wie „photo_limit_exceeded“ – diese deuten auf bewusst ausgelöste Limit‑Sperren hin, nicht auf technische Fehler.
|
||||||
|
|
||||||
|
## 5. Typische Muster & Gegenmaßnahmen
|
||||||
|
|
||||||
|
- **Hohe Fehlerrate beim Upload (5xx)**
|
||||||
|
- Hinweis auf API‑/Backend‑Problem:
|
||||||
|
- Siehe Public‑API‑Runbook und App‑Logs (Datenbank‑/Redis‑Fehler, Timeouts).
|
||||||
|
- **Uploads bleiben „ewig“ auf „wird verarbeitet“**
|
||||||
|
- Queues laufen nicht oder `media-storage`/`media-security` steckt fest:
|
||||||
|
- Horizon prüfen, ob Worker‑Container laufen.
|
||||||
|
- Ggf. Worker neu starten und Failed Jobs analysieren.
|
||||||
|
- **Nur ein Event betroffen, andere funktionieren**
|
||||||
|
- Meist Limit‑ oder Konfig‑Thema (Paket voll, Galerie abgelaufen, Event deaktiviert).
|
||||||
|
- Tenant‑Admin‑UI prüfen: Event‑Status, Paket‑Status, Data Lifecycle Einstellungen.
|
||||||
|
|
||||||
|
## 6. Kommunikation
|
||||||
|
|
||||||
|
- **An Tenant/Support zurückmelden**:
|
||||||
|
- Was war die Ursache? (z.B. Paket‑Limit, temporäre Überlastung, Storage‑Knappheit).
|
||||||
|
- Was wurde getan? (z.B. Paket angepasst, Queues neu gestartet, Storage erweitert).
|
||||||
|
- Ob und wie der Tenant/gäste weiteres tun müssen (z.B. Seite neu laden, später erneut probieren).
|
||||||
|
|
||||||
|
Für tiefere Ursachen rund um Storage siehe `docs/ops/media-storage-spec.md` und `docs/ops/dr-storage-issues.md`.
|
||||||
|
|
||||||
87
docs/ops/howto-photobooth-no-photos.md
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
---
|
||||||
|
id: howto-photobooth-no-photos
|
||||||
|
title: How-to – Photobooth lädt keine Fotos
|
||||||
|
sidebar_label: Photobooth lädt nichts
|
||||||
|
---
|
||||||
|
|
||||||
|
Dieses How‑to beschreibt, wie du vorgehst, wenn ein Tenant meldet, dass von der Photobooth keine Fotos im Event ankommen.
|
||||||
|
|
||||||
|
## 1. Problem eingrenzen
|
||||||
|
|
||||||
|
Fragen an den Tenant:
|
||||||
|
|
||||||
|
- Welcher Event ist betroffen? (Event‑ID oder Titel).
|
||||||
|
- Wird im Tenant‑Admin unter „Fotobox-Uploads“ angezeigt, dass die Photobooth aktiviert ist?
|
||||||
|
- Sieht der Photobooth‑Operator offensichtliche Fehler am Gerät (z.B. FTP‑Fehler, Timeout)?
|
||||||
|
- Seit wann kommt nichts mehr an? (Zeitfenster)
|
||||||
|
|
||||||
|
Diese Infos helfen dir, zwischen Konfigurations‑, FTP‑ oder Ingest‑Problem zu unterscheiden.
|
||||||
|
|
||||||
|
## 2. Konfiguration im Admin prüfen
|
||||||
|
|
||||||
|
1. Im Tenant-Admin:
|
||||||
|
- Den betroffenen Event öffnen.
|
||||||
|
- Prüfen, ob die Photobooth‑Funktion für diesen Event aktiviert ist.
|
||||||
|
2. Wenn Photobooth deaktiviert ist:
|
||||||
|
- Tenant bitten, sie im UI zu aktivieren (dies triggert die Provisionierung und Credentials).
|
||||||
|
- Danach erneut testen, ob Uploads ankommen.
|
||||||
|
|
||||||
|
## 3. FTP-/Control-Service überprüfen
|
||||||
|
|
||||||
|
Siehe auch `docs/ops/photobooth/control_service.md` und `docs/ops/photobooth/ops_playbook.md`.
|
||||||
|
|
||||||
|
1. **FTP-Erreichbarkeit**
|
||||||
|
- Host/Port aus den Photobooth‑Einstellungen entnehmen.
|
||||||
|
- Testverbindung (z.B. über lokales FTP‑Tool oder `nc`/`telnet`) herstellen:
|
||||||
|
- Port (z.B. 2121) erreichbar?
|
||||||
|
2. **Credentials validieren**
|
||||||
|
- Prüfen, ob Username/Passwort im Tenant‑Admin zu den Control‑Service‑Daten passen.
|
||||||
|
- Bei Verdacht auf Fehler:
|
||||||
|
- Im Admin die Zugangsdaten neu generieren lassen.
|
||||||
|
- Tenant/Photobooth‑Team informieren, dass sie die neuen Credentials konfigurieren müssen.
|
||||||
|
|
||||||
|
## 4. Ingest-Service & Scheduler prüfen
|
||||||
|
|
||||||
|
Die Photobooth legt Dateien zunächst in einem Import‑Pfad ab, der dann vom Ingest‑Service verarbeitet wird.
|
||||||
|
|
||||||
|
1. **Import-Verzeichnis prüfen**
|
||||||
|
- Pfad: üblicherweise `storage/app/photobooth/{tenant}/{event}` (siehe `docs/ops/photobooth/README.md`).
|
||||||
|
- In den Logs kontrollieren, ob neue Dateien dort landen.
|
||||||
|
2. **Ingest-Command**
|
||||||
|
- Sicherstellen, dass `photobooth:ingest` regelmäßig läuft (Scheduler/Cron):
|
||||||
|
```bash
|
||||||
|
php artisan photobooth:ingest --max-files=100
|
||||||
|
```
|
||||||
|
- Optional: für einen konkreten Event:
|
||||||
|
```bash
|
||||||
|
php artisan photobooth:ingest --event=EVENT_ID --max-files=50 -vv
|
||||||
|
```
|
||||||
|
- Logs auf Hinweise prüfen:
|
||||||
|
- Fehler beim Lesen der FTP‑Dateien.
|
||||||
|
- Probleme beim Schreiben in den Hot‑Storage.
|
||||||
|
3. **Queues**
|
||||||
|
- Verifizieren, dass relevante Queues laufen (falls Ingest Jobs dispatcht).
|
||||||
|
|
||||||
|
## 5. Typische Fehlerbilder & Lösungen
|
||||||
|
|
||||||
|
- **FTP erreicht, aber Import-Verzeichnis bleibt leer**
|
||||||
|
- Photobooth‑Software schreibt nicht an den erwarteten Pfad → Pfad in der Photobooth‑Konfiguration mit den Angaben aus `PHOTOBOOTH_IMPORT_ROOT` abgleichen.
|
||||||
|
- Evtl. Berechtigungsproblem im FTP‑Container (Perms/Ownership).
|
||||||
|
- **Import-Verzeichnis gefüllt, aber nichts im Event**
|
||||||
|
- `photobooth:ingest` läuft nicht oder bricht ab:
|
||||||
|
- Scheduler prüfen (`scheduler`‑Service in Docker/Dokploy).
|
||||||
|
- Kommando manuell ausführen und Fehler analysieren.
|
||||||
|
- **Fotos tauchen mit großer Verzögerung auf**
|
||||||
|
- Ingest läuft zu selten (Cron/Intervalle zu groß).
|
||||||
|
- Events haben hohe Medienlast → `--max-files` erhöhen oder Ingest häufiger anstoßen.
|
||||||
|
|
||||||
|
## 6. Kommunikation mit dem Tenant
|
||||||
|
|
||||||
|
- Sobald Ursache und Fix klar sind:
|
||||||
|
- Tenant informieren, ob es ein Konfig‑, Netzwerk‑ oder Ingest‑Problem war.
|
||||||
|
- Falls nötig, dem Photobooth‑Team neue Credentials/Anweisungen zukommen lassen.
|
||||||
|
- Falls einige Dateien irreversibel verloren gegangen sind:
|
||||||
|
- Transparent kommunizieren und ggf. Kulanzlösungen (z.B. Gutschrift) über Finance/Success abstimmen.
|
||||||
|
|
||||||
|
Nutze für tiefere Analysen die ausführlicheren Playbooks in `docs/ops/photobooth/ops_playbook.md`.
|
||||||
|
|
||||||
52
docs/ops/howto-tenant-full-export.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
---
|
||||||
|
title: How‑to – Tenant‑Komplett‑Export
|
||||||
|
sidebar_label: Tenant-Komplett-Export
|
||||||
|
---
|
||||||
|
|
||||||
|
Dieses How‑to beschreibt, wie du für einen Tenant kurz vor Vertragsende einen möglichst vollständigen Daten‑Export erstellst.
|
||||||
|
|
||||||
|
## 1. Anfrage prüfen
|
||||||
|
|
||||||
|
- Schriftliche Anfrage des Tenants (E‑Mail/Ticket).
|
||||||
|
- Klarer Scope:
|
||||||
|
- Nur Medien?
|
||||||
|
- Medien + Metadaten (Events, Gäste, Likes)?
|
||||||
|
- Billing‑Nachweise (Rechnungen)?
|
||||||
|
|
||||||
|
## 2. Medien‑Export
|
||||||
|
|
||||||
|
- Für jeden relevanten Event:
|
||||||
|
- Prüfen, ob alle Upload‑Jobs durch sind (`event_media_assets` ohne `pending`/`failed`).
|
||||||
|
- Archiv‑Export nutzen (sofern vorhanden) oder:
|
||||||
|
- Medien‑Ordner pro Event aus dem Storage exportieren.
|
||||||
|
- Thumbnails optional, Originale Pflicht.
|
||||||
|
|
||||||
|
## 3. Metadaten‑Export
|
||||||
|
|
||||||
|
- Events, Gäste, Likes, Kommentare nach Bedarf exportieren:
|
||||||
|
- Entweder über bestehende Export‑Funktion (CSV/JSON).
|
||||||
|
- Oder über einen einmaligen, internen Report (z.B. `php artisan make:report`‑ähnlicher Flow, falls vorhanden).
|
||||||
|
- Output als ZIP mit klarer Ordnerstruktur:
|
||||||
|
- `media/`
|
||||||
|
- `metadata/events.csv`
|
||||||
|
- `metadata/guests.csv`
|
||||||
|
|
||||||
|
## 4. Billing-Unterlagen
|
||||||
|
|
||||||
|
- Rechnungen / Zahlungsbelege:
|
||||||
|
- Paddle‑Belege (Links oder PDFs).
|
||||||
|
- Interne Rechnungs‑PDFs (falls generiert).
|
||||||
|
|
||||||
|
## 5. Nach dem Export
|
||||||
|
|
||||||
|
- Export dem Tenant sicher zur Verfügung stellen (z.B. Download‑Link mit Ablaufdatum).
|
||||||
|
- Dokumentieren:
|
||||||
|
- Datum des Exports.
|
||||||
|
- Umfang (welche Tabellen/Events enthalten).
|
||||||
|
- Speicherort und Aufbewahrungsdauer des Export‑Bundles.
|
||||||
|
|
||||||
|
Siehe auch:
|
||||||
|
|
||||||
|
- `docs/ops/compliance-dsgvo-ops.md`
|
||||||
|
- `docs/ops/backup-restore.md`
|
||||||
|
|
||||||
95
docs/ops/howto-tenant-package-not-active.md
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
---
|
||||||
|
id: howto-tenant-package-not-active
|
||||||
|
title: How-to – Zahlung erfolgreich, Paket nicht aktiv
|
||||||
|
sidebar_label: Zahlung ok, Paket nicht aktiv
|
||||||
|
---
|
||||||
|
|
||||||
|
Dieses How‑to beschreibt, wie du vorgehst, wenn ein Tenant meldet: „Zahlung war erfolgreich, aber mein Paket ist nicht aktiv / Galerie bleibt limitiert.“
|
||||||
|
|
||||||
|
## 1. Informationen vom Tenant einsammeln
|
||||||
|
|
||||||
|
Bevor du nachschaust:
|
||||||
|
|
||||||
|
- Tenant‑ID oder Tenant‑Slug.
|
||||||
|
- Betroffenes Paket (Name oder Beschreibung, z.B. „Pro‑Paket 79 €“).
|
||||||
|
- Zeitpunkt der Zahlung (Datum/Uhrzeit, ggf. Screenshot).
|
||||||
|
- Ggf. Auszug aus der Paddle‑Bestätigung (ohne vollständige Kartendaten!).
|
||||||
|
|
||||||
|
Diese Infos erlauben dir, die korrekte Transaktion sowohl in Paddle als auch im Backend zu finden.
|
||||||
|
|
||||||
|
## 2. Paddle-Status prüfen
|
||||||
|
|
||||||
|
1. Im Paddle‑Dashboard:
|
||||||
|
- Suche nach E‑Mail, Tenant‑Name oder dem vom Tenant genannten Transaktions‑Identifier.
|
||||||
|
- Stelle sicher, dass die Zahlung dort als „completed“/„paid“ markiert ist.
|
||||||
|
2. Notiere:
|
||||||
|
- Paddle‑Transaction‑ID und ggf. Checkout‑ID.
|
||||||
|
- Status (paid/processing/failed/cancelled).
|
||||||
|
|
||||||
|
Wenn Paddle die Zahlung nicht als erfolgreich zeigt, ist dies primär ein Finance‑/Customer‑Topic – ggf. mit Customer Support klären, ob eine neue Zahlung oder Klärung mit dem Kunden notwendig ist.
|
||||||
|
|
||||||
|
## 3. Backend-Status prüfen
|
||||||
|
|
||||||
|
Mit bestätigter Zahlung in Paddle:
|
||||||
|
|
||||||
|
1. `checkout_sessions`:
|
||||||
|
- Suche nach Sessions des Tenants (`tenant_id`) mit dem betroffenen `package_id`:
|
||||||
|
- Achte auf `status` (erwartet `completed`) und `provider = paddle`.
|
||||||
|
- Prüfe `provider_metadata` auf `paddle_last_event`, `paddle_status`, `paddle_checkout_id`.
|
||||||
|
- Wenn du die Session über Paddle‑Metadaten finden möchtest:
|
||||||
|
- `paddle_checkout_id` aus dem Webhook/Provider‑Metadata oder `transaction_id` verwenden.
|
||||||
|
2. `package_purchases`:
|
||||||
|
- Prüfe, ob ein Eintrag für `(tenant_id, package_id)` mit passender Provider‑Referenz existiert:
|
||||||
|
- z.B. `provider = 'paddle'`, `provider_id` = Transaction‑ID.
|
||||||
|
3. `tenant_packages`:
|
||||||
|
- Prüfe, ob es einen aktiven Eintrag für `(tenant_id, package_id)` gibt:
|
||||||
|
- `active = 1`, `expires_at` in der Zukunft.
|
||||||
|
|
||||||
|
## 4. Webhook-/Verarbeitungsstatus untersuchen
|
||||||
|
|
||||||
|
Wenn `checkout_sessions` noch nicht auf `completed` steht oder `tenant_packages` nicht aktualisiert wurden:
|
||||||
|
|
||||||
|
1. Logs prüfen:
|
||||||
|
- `storage/logs/laravel.log` und ggf. `billing`‑Channel.
|
||||||
|
- Suche nach Einträgen von `PaddleWebhookController` / `CheckoutWebhookService` rund um den Zahlungszeitpunkt.
|
||||||
|
2. Typische Ursachen:
|
||||||
|
- Webhook nicht zugestellt (Netzwerk/SSL).
|
||||||
|
- Webhook konnte die Session nicht auflösen (`[CheckoutWebhook] Paddle session not resolved`).
|
||||||
|
- Idempotenz‑Lock (`Paddle lock busy`) hat dazu geführt, dass Event nur teilweise verarbeitet wurde.
|
||||||
|
|
||||||
|
## 5. Korrektur-Schritte
|
||||||
|
|
||||||
|
### 5.1 Automatischer Replay (empfohlen)
|
||||||
|
|
||||||
|
1. Im Paddle‑Dashboard:
|
||||||
|
- Den betreffenden `transaction.*`‑Event finden.
|
||||||
|
- Webhook‑Replay auslösen.
|
||||||
|
2. In den Logs beobachten:
|
||||||
|
- Ob `CheckoutWebhookService::handlePaddleEvent()` diesmal die Session findet und `CheckoutAssignmentService::finalise()` ausführt.
|
||||||
|
3. Nochmal `checkout_sessions` und `tenant_packages` prüfen:
|
||||||
|
- Session sollte auf `completed` stehen, Paket aktiv sein.
|
||||||
|
|
||||||
|
### 5.2 Manuelle Korrektur (Notfall)
|
||||||
|
|
||||||
|
Nur anwenden, wenn klare Freigabe vorliegt und Paddle die Zahlung eindeutig als erfolgreich listet.
|
||||||
|
|
||||||
|
1. `tenant_packages` aktualisieren:
|
||||||
|
- Entweder neuen Eintrag anlegen oder bestehenden für `(tenant_id, package_id)` so setzen, dass:
|
||||||
|
- `active = 1`,
|
||||||
|
- `purchased_at` und `expires_at` zu Paddle‑Daten passen.
|
||||||
|
2. `package_purchases` ergänzen:
|
||||||
|
- Sicherstellen, dass die Zahlung als Zeile mit `provider = 'paddle'`, `provider_id = Transaction‑ID` und passender `price` existiert (für spätere Audits).
|
||||||
|
3. Konsistenz prüfen:
|
||||||
|
- Admin UI für Tenant öffnen und prüfen, ob Limits/Paketstatus jetzt korrekt angezeigt werden.
|
||||||
|
4. Dokumentation:
|
||||||
|
- Den Vorgang im Ticket oder in `docs/process/changes/*` (falls wiederkehrend) dokumentieren.
|
||||||
|
|
||||||
|
## 6. Kommunikation mit dem Tenant
|
||||||
|
|
||||||
|
- Sobald der Backend‑Status korrigiert ist:
|
||||||
|
- Kurz bestätigen, dass das Paket aktiv ist und welche Auswirkungen das hat (z.B. neue Limits, verlängerte Galerie).
|
||||||
|
- Falls Paddle die Zahlung nicht als erfolgreich führt:
|
||||||
|
- Ehrlich kommunizieren, dass laut Zahlungsprovider noch keine endgültige Zahlung vorliegt und welche Optionen es gibt (z.B. neue Zahlung, Klärung mit Bank/Kreditkarte).
|
||||||
|
|
||||||
|
Dieses How‑to sollte dem Support/On‑Call helfen, den gängigsten Billing‑Fehlerfall strukturiert abzuarbeiten. Für tiefere Ursachenanalysen siehe `docs/ops/billing-ops.md`.
|
||||||
|
|
||||||
79
docs/ops/incidents-major.md
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
---
|
||||||
|
title: Major Incidents & Eskalation
|
||||||
|
sidebar_label: Major Incidents
|
||||||
|
---
|
||||||
|
|
||||||
|
Diese Seite beschreibt, wie du bei größeren Störungen (SEV‑1/SEV‑2) vorgehst. Sie ergänzt die spezifischen Runbooks (Public API, Medien‑Pipeline, Photobooth) um einen einheitlichen Rahmen.
|
||||||
|
|
||||||
|
## 1. Incident-Klassifikation
|
||||||
|
|
||||||
|
- **SEV‑1 (kritisch)**
|
||||||
|
- Gäste können nicht mehr hochladen ODER keine Events/Galerien mehr öffnen.
|
||||||
|
- Tenant Admins können sich nicht einloggen oder keine Kernaktionen ausführen (Events verwalten, Medien moderieren).
|
||||||
|
- Datenverlust oder potenzieller Datenverlust (z.B. Löschjob auf falscher Storage‑Ebene).
|
||||||
|
- **SEV‑2 (hoch)**
|
||||||
|
- Teilweise Degradation (z.B. Photobooth‑Uploads hängen, Public‑API stark verlangsamt, eine Region betroffen).
|
||||||
|
- Kritische Background‑Jobs (Archivierung, AV/EXIF‑Scans, Zahlungs‑Webhooks) stauen sich, ohne dass Gäste sofort komplett blockiert sind.
|
||||||
|
- **SEV‑3 (mittel)**
|
||||||
|
- Einzelne Features gestört (Notification‑Center, Join‑Token‑Analytics, einzelne Admin‑Views).
|
||||||
|
- Workaround möglich (z.B. manuelle Nacharbeit durch Support).
|
||||||
|
|
||||||
|
> Wichtig: Jede Störung, die einen zahlenden Eventkunden am Tag des Events blockiert, sollte mindestens als SEV‑2, ggf. als SEV‑1 eingeordnet werden.
|
||||||
|
|
||||||
|
## 2. Erstmaßnahmen (Triage)
|
||||||
|
|
||||||
|
1. **Scope bestimmen**
|
||||||
|
- Welche Benutzer sind betroffen? (Alle Gäste, einzelne Tenants, nur Photobooth, nur Admins?)
|
||||||
|
- Betrifft es nur eine Umgebung (staging vs. production)?
|
||||||
|
2. **Schnell-Checks**
|
||||||
|
- Status von App, Queue, Redis, DB (Docker/Dokploy‑Übersicht prüfen).
|
||||||
|
- Horizon/Queues: sind relevante Queues leer, wachsend, „stuck“? Gibt es viele Failed Jobs?
|
||||||
|
- Logs für relevante Kanäle: `storage/logs/laravel.log`, spezielle Channels wie `storage-jobs`, `notifications`, `billing`, Nginx/Proxy‑Logs.
|
||||||
|
- Monitoring: externe Uptime‑Checks / Dashboards (z.B. Public‑API Latenz, Error‑Rate).
|
||||||
|
3. **Einordnung & Eskalation**
|
||||||
|
- SEV‑1/2: On‑Call informieren (Pager/Chat), Incident‑Kanal im Teamchat eröffnen.
|
||||||
|
- SEV‑3: Im Issue‑Tracker erfassen, ggf. gebündelt mit anderen Findings.
|
||||||
|
|
||||||
|
Nutze bei Public‑API‑Problems zusätzlich das `docs/ops/deployment/public-api-incident-playbook.md`.
|
||||||
|
|
||||||
|
## 3. Standard-Runbooks nach Bereich
|
||||||
|
|
||||||
|
- **Public API / Gast-Zugriff**
|
||||||
|
- Siehe `docs/ops/deployment/public-api-incident-playbook.md`.
|
||||||
|
- Typische Auslöser: Peaks, Abuse, externe Integrationen, Ratenlimits.
|
||||||
|
- **Medien-Pipeline / Uploads**
|
||||||
|
- Siehe `docs/ops/media-storage-spec.md` und `docs/ops/guest-notification-ops.md`.
|
||||||
|
- Fälle: Uploads bleiben im Pending, Archivjobs laufen nicht, Speicherkapazität erreicht, Gäste bekommen „Uploads hängen noch…“.
|
||||||
|
- **Photobooth**
|
||||||
|
- Siehe `docs/ops/photobooth/ops_playbook.md`.
|
||||||
|
- Fälle: FTP nicht erreichbar, Ingest nicht laufend, falsche Credentials, Security‑Vorfälle.
|
||||||
|
- **Abrechnung & Billing**
|
||||||
|
- Siehe `docs/ops/billing-ops.md`.
|
||||||
|
- Fälle: Paddle/RevenueCat‑Webhook‑Fehler, falsche Paket‑Zustände, doppelte/fehlende Buchungen.
|
||||||
|
|
||||||
|
Dieses Dokument verweist immer nur auf die jeweils tieferen Runbooks – bei konkreten Problemen gehst du dort in die Details.
|
||||||
|
|
||||||
|
## 4. Kommunikation
|
||||||
|
|
||||||
|
- **Intern (Team)**
|
||||||
|
- Eröffne einen dedizierten Incident‑Thread im Chat (mit Zeitstempel, SEV‑Level, Kurzbeschreibung).
|
||||||
|
- Halte dort Statusupdates fest (z.B. „17:05 – Upload‑Queue entstaut, weitere Beobachtung 30 min“).
|
||||||
|
- Notiere bewusst Entscheidungen (z.B. „19:10 – Feature X temporär deaktiviert“, „19:25 – Rollback auf vorheriges Release“).
|
||||||
|
- **Extern (Kunden)**
|
||||||
|
- Ab SEV‑2: Überlege einen kurzen Statushinweis (Status‑Seite oder manuelle Kommunikation an direkt betroffene Tenants).
|
||||||
|
- Bei Incidents während Events: Koordiniere mit Success/Support, um proaktiv auf Tenant Admins zuzugehen.
|
||||||
|
|
||||||
|
> TODO: Falls du eine Status‑Seite oder automatisierte E‑Mails hast, dokumentiere hier, wie und wann sie ausgelöst werden.
|
||||||
|
|
||||||
|
## 5. Nachbereitung (Postmortem)
|
||||||
|
|
||||||
|
Nach einem SEV‑1/2 Incident:
|
||||||
|
|
||||||
|
1. **Fakten sammeln** (Timeline, betroffene Tenants/Events, konkrete Auswirkungen).
|
||||||
|
2. **Ursache** (Root Cause) möglichst präzise identifizieren – auch dann, wenn direkt „nur“ Symptome gefixt wurden.
|
||||||
|
3. **Kurzfristige Maßnahmen** (Hotfixes, Konfig‑Anpassungen, zusätzliche Checks).
|
||||||
|
4. **Langfristige Maßnahmen** – sollten als Epics/Tasks in `docs/process/todo/*` bzw. `docs/process/changes/*` landen (inkl. Link zum Incident).
|
||||||
|
5. **Dokumentation aktualisieren**
|
||||||
|
- Relevante Runbooks (dieses Dokument, Public‑API‑Runbook, Storage‑Spec, Billing‑Ops, etc.) mit neuen Learnings ergänzen.
|
||||||
|
|
||||||
|
Ziel ist, dass die Time‑to‑Detect und Time‑to‑Resolve für ähnliche Probleme in Zukunft sinkt.
|
||||||
@@ -33,7 +33,7 @@ This document explains how customer photo uploads move through the Fotospiel pla
|
|||||||
| Component | Role |
|
| Component | Role |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `app` (Laravel FPM) | Accepts uploads, writes to the hot disk, and records metadata. |
|
| `app` (Laravel FPM) | Accepts uploads, writes to the hot disk, and records metadata. |
|
||||||
| `media-storage-worker` | Runs `/docs/queue-supervisor/queue-worker.sh media-storage`; consumes archival/restoration jobs and copies data between disks. Shares the same `app-code` volume so it sees `/var/www/html/storage`. |
|
| `media-storage-worker` | Runs `/scripts/queue-worker.sh media-storage`; consumes archival/restoration jobs and copies data between disks. Shares the same `app-code` volume so it sees `/var/www/html/storage`. |
|
||||||
| `queue` workers | Default queue consumers for non-storage background jobs. |
|
| `queue` workers | Default queue consumers for non-storage background jobs. |
|
||||||
| `media-security-worker` | Processes `ProcessPhotoSecurityScan` jobs (antivirus + EXIF scrub). |
|
| `media-security-worker` | Processes `ProcessPhotoSecurityScan` jobs (antivirus + EXIF scrub). |
|
||||||
| `scheduler` | Runs `php artisan schedule:work`, triggering `storage:archive-pending`, `storage:monitor`, queue health checks, etc. |
|
| `scheduler` | Runs `php artisan schedule:work`, triggering `storage:archive-pending`, `storage:monitor`, queue health checks, etc. |
|
||||||
@@ -92,7 +92,7 @@ This document explains how customer photo uploads move through the Fotospiel pla
|
|||||||
## Related Documentation
|
## Related Documentation
|
||||||
|
|
||||||
- `docs/prp/10-storage-media-pipeline.md` — canonical architecture diagram for storage tiers.
|
- `docs/prp/10-storage-media-pipeline.md` — canonical architecture diagram for storage tiers.
|
||||||
- `docs/ops/queue-workers.md` — how to run `media-storage` and `media-security` workers (scripts in `/docs/queue-supervisor/`).
|
- `docs/ops/queue-workers.md` — how to run `media-storage` and `media-security` workers (scripts live in `/scripts/`).
|
||||||
- `docs/ops/deployment/docker.md` / `docs/ops/deployment/dokploy.md` — container topology and volumes.
|
- `docs/ops/deployment/docker.md` / `docs/ops/deployment/dokploy.md` — container topology and volumes.
|
||||||
- `config/security.php`, `config/storage-monitor.php`, and `config/filesystems.php` for runtime knobs.
|
- `config/security.php`, `config/storage-monitor.php`, and `config/filesystems.php` for runtime knobs.
|
||||||
|
|
||||||
|
|||||||
146
docs/ops/monitoring-observability.md
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
---
|
||||||
|
title: Monitoring & Observability
|
||||||
|
sidebar_label: Monitoring
|
||||||
|
---
|
||||||
|
|
||||||
|
Dieses Dokument sammelt die wichtigsten Monitoring‑Punkte der Plattform und soll helfen, die richtigen Dashboards und Alerts aufzubauen.
|
||||||
|
|
||||||
|
## 1. Was sollte überwacht werden?
|
||||||
|
|
||||||
|
- **Verfügbarkeit**
|
||||||
|
- HTTP‑Checks auf zentrale Endpunkte (Landing, Join‑Token‑Flows, Guest Upload, Tenant Admin Login).
|
||||||
|
- Public‑API‑Checks (`/api/v1/events/{token}`, Galerie, Upload‑Endpoints).
|
||||||
|
- **Queues**
|
||||||
|
- Länge und Durchsatz der Queues `default`, `media-storage`, `media-security`, `notifications`.
|
||||||
|
- Age/Time‑in‑Queue, Anzahl der Failed Jobs.
|
||||||
|
- **Storage**
|
||||||
|
- Füllstand der Hot‑Storage‑Volumes/Buckets.
|
||||||
|
- Anzahlen/Status in `event_media_assets` (z.B. viele `pending` oder `failed`).
|
||||||
|
- **Fehler-Raten**
|
||||||
|
- HTTP 5xx/4xx spitzenweise, gruppiert nach Route/Service.
|
||||||
|
- Applikations‑Logs mit Error/Warning‑Level.
|
||||||
|
- **Billing & Webhooks**
|
||||||
|
- Fehlgeschlagene Paddle/RevenueCat‑Webhooks.
|
||||||
|
- Differenz zwischen erwarteten und verarbeiteten Zahlungen (optional).
|
||||||
|
|
||||||
|
## 2. Werkzeuge & Quellen
|
||||||
|
|
||||||
|
- **Horizon**
|
||||||
|
- Live‑Überblick über Laravel‑Queues.
|
||||||
|
- Alerts, wenn eine Queue zu lange Backlog aufbaut.
|
||||||
|
- **Docker/Dokploy**
|
||||||
|
- Container‑Health (Restart‑Loops, Ressourcennutzung).
|
||||||
|
- Service‑Healthchecks.
|
||||||
|
- **Logs**
|
||||||
|
- Laravel‑Logs (`storage/logs/*.log`), ggf. via Promtail/Loki oder ELK zentralisiert.
|
||||||
|
- Spezifische Channels (z.B. `storage-jobs`, `notifications`, `billing`).
|
||||||
|
- **Metriken**
|
||||||
|
- Falls vorhanden: Prometheus/Grafana‑Dashboards für App‑/DB‑/Redis‑Metriken.
|
||||||
|
|
||||||
|
> TODO: Wenn konkrete Dashboards in Grafana o.Ä. existieren, füge hier Screenshots/Links und eine kurze Erklärung der Panels ein.
|
||||||
|
|
||||||
|
## 3. Alarmierung & Schwellenwerte
|
||||||
|
|
||||||
|
Konkrete Schwellenwerte hängen von Traffic und Infrastruktur ab, aber folgende Muster haben sich bewährt:
|
||||||
|
|
||||||
|
- **Queues**
|
||||||
|
- `default`:
|
||||||
|
- Warnung: > 100 Jobs oder ältester Job > 5 Minuten.
|
||||||
|
- Kritisch: > 300 Jobs oder ältester Job > 15 Minuten.
|
||||||
|
- `media-storage`:
|
||||||
|
- Warnung: > 200 Jobs oder ältester Job > 10 Minuten.
|
||||||
|
- Kritisch: > 500 Jobs oder ältester Job > 30 Minuten.
|
||||||
|
- `media-security`:
|
||||||
|
- Warnung: > 50 Jobs oder ältester Job > 5 Minuten.
|
||||||
|
- Kritisch: > 150 Jobs oder ältester Job > 15 Minuten.
|
||||||
|
- `notifications`:
|
||||||
|
- Warnung: > 100 Jobs dauerhaft im Backlog.
|
||||||
|
- Kritisch: wenn die Queue dauerhaft wächst, während Events live laufen.
|
||||||
|
- **Uploads**
|
||||||
|
- Fehlerquote bei Upload‑Endpoints:
|
||||||
|
- Warnung: 2–5 % Fehler (HTTP 4xx/5xx) über 5‑minütiges Fenster.
|
||||||
|
- Kritisch: > 5–10 % Fehler in 5 Minuten.
|
||||||
|
- Anzahl „hängender“ Uploads:
|
||||||
|
- Warnung, wenn `storage:check-upload-queues` für denselben Event wiederholt Alarm schlägt (z.B. mehr als 5 benachrichtigte Events in 10 Minuten).
|
||||||
|
- **Public API**
|
||||||
|
- Latenz für `GET /api/v1/events/{token}` und `/photos`:
|
||||||
|
- Warnung: P95 > 500 ms über 5 Minuten.
|
||||||
|
- Kritisch: P95 > 1–2 s über 5 Minuten.
|
||||||
|
- Fehlerraten für diese Endpoints:
|
||||||
|
- Warnung: > 2 % 5xx über 5 Minuten.
|
||||||
|
- Kritisch: > 5 % 5xx über 5 Minuten.
|
||||||
|
- **Billing**
|
||||||
|
- Failed Webhooks:
|
||||||
|
- Warnung: mehr als N (z.B. 5–10) fehlgeschlagene Webhooks pro 10 Minuten.
|
||||||
|
- Kritisch: schneller Anstieg oder > 20 % Fehleranteil.
|
||||||
|
- Differenz zwischen erwarteten und verarbeiteten Zahlungen:
|
||||||
|
- Regelmäßige Reports (z.B. täglich) statt harter Alerts, aber auffällige Abweichungen sollten ein Incident auslösen.
|
||||||
|
|
||||||
|
## 4. Betriebliche Nutzung
|
||||||
|
|
||||||
|
- **Daily Checks**
|
||||||
|
- Horizon Queue‑Dashboard kurz prüfen.
|
||||||
|
- Logs auf neue Fehler‑/Warnmuster scannen.
|
||||||
|
- **Bei Incidents**
|
||||||
|
- Monitoring‑Daten helfen, Ursache und zeitlichen Verlauf zu rekonstruieren (siehe `docs/ops/incidents-major.md`).
|
||||||
|
|
||||||
|
## 5. Zusammenspiel mit bestehenden Kommandos
|
||||||
|
|
||||||
|
Einige Artisan‑Kommandos sind explizit für Monitoring/Health gedacht. Sie sollten in Cron/Scheduler oder externe Checks integriert werden:
|
||||||
|
|
||||||
|
- `storage:monitor` (falls vorhanden)
|
||||||
|
- Aggregiert Storage‑Auslastung und Queue‑Health basierend auf `config/storage-monitor.php`.
|
||||||
|
- Kann Alerts per Mail/Log triggern, wenn Schwellwerte überschritten werden.
|
||||||
|
- `storage:check-upload-queues`
|
||||||
|
- Überprüft, ob Upload‑bezogene Queues im erwarteten Rahmen liegen und triggert Gast‑Alerts bei Problemen (siehe `docs/ops/guest-notification-ops.md`).
|
||||||
|
- `storage:archive-pending`
|
||||||
|
- Kein klassisches Monitoring‑Kommando, aber relevant, um zu prüfen, ob Archivierungsjobs hinterherhinken (z.B. viele alte `hot`‑Assets).
|
||||||
|
|
||||||
|
Diese Kommandos sind kein Ersatz für echtes Monitoring, liefern aber wertvolle Signale, die in Dashboards und Alerts einfließen können.
|
||||||
|
|
||||||
|
## 6. Beispiele für Metrik- und Alert-Definitionen
|
||||||
|
|
||||||
|
Nachfolgend beispielhafte Formulierungen, wie Alerts unabhängig vom verwendeten Monitoring‑System aussehen könnten.
|
||||||
|
|
||||||
|
### 6.1 Queue-Backlog Alert (Pseudocode)
|
||||||
|
|
||||||
|
**Ziel**: Meldung, wenn `media-storage` zu lange Backlog aufbaut.
|
||||||
|
|
||||||
|
- Bedingung:
|
||||||
|
- `queue_length("media-storage") > 500` **OR**
|
||||||
|
- `oldest_job_age("media-storage") > 30min`
|
||||||
|
- Dauer:
|
||||||
|
- 2 aufeinanderfolgende Messintervalle (z.B. 2×5 Minuten).
|
||||||
|
- Aktion:
|
||||||
|
- Alarm an On‑Call + Hinweis auf `docs/ops/media-storage-spec.md` und `docs/ops/dr-storage-issues.md`.
|
||||||
|
|
||||||
|
### 6.2 Upload-Error-Rate Alert
|
||||||
|
|
||||||
|
**Ziel**: Upload‑Probleme für Gäste früh erkennen.
|
||||||
|
|
||||||
|
- Bedingung:
|
||||||
|
- Anteil `5xx` oder „applikationsspezifische Fehlercodes“ bei `POST /api/v1/events/*/upload` > 5 % in 5 Minuten.
|
||||||
|
- Aktion:
|
||||||
|
- Alarm an On‑Call, Link zum Public‑API‑Incident‑Playbook und Storage‑Runbook.
|
||||||
|
|
||||||
|
### 6.3 Public-API-Latenz Alert
|
||||||
|
|
||||||
|
**Ziel**: Langsame Galerien / Token‑Aufrufe frühzeitig sehen.
|
||||||
|
|
||||||
|
- Bedingung:
|
||||||
|
- `P95(latency(GET /api/v1/events/*)) > 1000ms` über 5 Minuten.
|
||||||
|
- Aktion:
|
||||||
|
- Alarm an On‑Call, eventuell automatische Skalierung oder Untersuchung (DB/Redis‑Last).
|
||||||
|
|
||||||
|
### 6.4 Billing-Webhook Alert
|
||||||
|
|
||||||
|
**Ziel**: Fehler bei Paddle/RevenueCat‑Webhook‑Verarbeitung erkennen.
|
||||||
|
|
||||||
|
- Bedingung:
|
||||||
|
- Mehr als 10 fehlgeschlagene Webhook‑Verarbeitungen innerhalb von 10 Minuten, oder Verhältnis `failed/success` > 0,2.
|
||||||
|
- Aktion:
|
||||||
|
- Alarm an On‑Call + Finance/Billing‑Verantwortliche, Verweis auf `docs/ops/billing-ops.md`.
|
||||||
|
|
||||||
|
Diese Beispiele sollen helfen, konkrete Regeln in eurem Monitoring‑Tool zu definieren. Die genauen Zahlen sollten anhand realer Traffic‑Muster feinjustiert werden.
|
||||||
|
|
||||||
|
Dieses Dokument ist bewusst technologie‑agnostisch formuliert – die konkrete Implementierung (Prometheus, Grafana, Loki, ELK, SaaS‑Monitoring) sollte hier nachgezogen und mit Beispielen ergänzt werden.
|
||||||
48
docs/ops/oncall-cheatsheet.md
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
title: On‑Call Cheat Sheet
|
||||||
|
sidebar_label: On‑Call Spickzettel
|
||||||
|
---
|
||||||
|
|
||||||
|
Dieser Spickzettel ist für On‑Call‑Personen gedacht, die im Incident schnell handeln müssen. Er konzentriert sich bewusst auf die wichtigsten Kommandos, Dashboards und Checks.
|
||||||
|
|
||||||
|
## 1. Top‑10 Kommandos
|
||||||
|
|
||||||
|
- App‑Container Logs (Laravel / Horizon):
|
||||||
|
- `docker compose logs -f app`
|
||||||
|
- `docker compose logs -f horizon`
|
||||||
|
- Queue‑Status:
|
||||||
|
- `php artisan queue:failed`
|
||||||
|
- `php artisan horizon:status`
|
||||||
|
- Storage‑Health:
|
||||||
|
- `php artisan storage:monitor`
|
||||||
|
- `php artisan storage:check-upload-queues`
|
||||||
|
- Datenbank‑Checks (Beispiele):
|
||||||
|
- `php artisan tinker` → gezielte Queries zu `events`, `event_media_assets`, `checkout_sessions`.
|
||||||
|
|
||||||
|
## 2. Erstdiagnose bei „Nichts geht mehr“
|
||||||
|
|
||||||
|
- Statusseite / Monitoring prüfen (HTTP‑Status, Fehler‑Rate, Queue‑Länge).
|
||||||
|
- `docker compose ps` → welche Services sind „unhealthy“ oder down?
|
||||||
|
- Logs der auffälligen Services anschauen (App, Queue, DB, Nginx).
|
||||||
|
- Kurz festhalten:
|
||||||
|
- Wann trat das Problem auf?
|
||||||
|
- Betrifft es **alle** Tenants oder einzelne?
|
||||||
|
- Nur Guest‑PWA, nur Tenant‑Admin oder beides?
|
||||||
|
|
||||||
|
## 3. Wichtigste Dashboards (Beispiele)
|
||||||
|
|
||||||
|
- API‑Fehler‑Rate (5xx, 4xx für Public API).
|
||||||
|
- Queue‑Backlog (`default`, `media-storage`, `media-security`, `notifications`).
|
||||||
|
- Response‑Time Guest‑/Tenant‑PWA.
|
||||||
|
- Paddle‑Webhook‑Fehler (falls im Monitoring abgebildet).
|
||||||
|
|
||||||
|
> Ergänze hier konkrete Links zu euren Grafana/Datadog‑Dashboards, sobald diese stabil sind.
|
||||||
|
|
||||||
|
## 4. Wann eskalieren?
|
||||||
|
|
||||||
|
- SEV‑1: Plattform weitgehend nicht nutzbar (> 15 Minuten Ausfall, viele Tenants betroffen).
|
||||||
|
- SEV‑2: Kritische Kernfunktion (Uploads, Logins, Zahlungen) länger als 30 Minuten gestört.
|
||||||
|
- SEV‑3: Einzelne Tenants oder Funktionen, Workaround vorhanden.
|
||||||
|
|
||||||
|
Siehe auch `docs/ops/incidents-major.md` für detaillierte SEV‑Definitionen und Kommunikationsregeln.
|
||||||
|
|
||||||
126
docs/ops/oncall-roles.md
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
---
|
||||||
|
title: Rollen & On-Call-Handbuch
|
||||||
|
sidebar_label: Rollen & On-Call
|
||||||
|
---
|
||||||
|
|
||||||
|
Dieses Dokument beschreibt, **wer** im Betrieb wofür zuständig ist und **wie** On‑Call‑Bereitschaft organisiert wird. Es ergänzt die technischen Runbooks um eine klare Verantwortungsebene.
|
||||||
|
|
||||||
|
> Hinweis: Konkrete Namen/Kontaktdaten sollten nicht in Git stehen, sondern getrennt (z.B. in einem internen Adressbuch oder Passwort‑Safe). Dieses Dokument definiert Rollen und Prozesse.
|
||||||
|
|
||||||
|
## 1. Rollenübersicht
|
||||||
|
|
||||||
|
### 1.1 Platform Ops
|
||||||
|
|
||||||
|
- Verantwortlich für:
|
||||||
|
- Infrastruktur (Docker/Dokploy‑Stacks, Netzwerke, TLS, Backups).
|
||||||
|
- Technische Verfügbarkeit der Services (App, Queues, DB, Redis, Storage).
|
||||||
|
- Umsetzung und Pflege der Runbooks unter `docs/ops/`.
|
||||||
|
- Typische Aufgaben:
|
||||||
|
- Deployments koordinieren (`docker-compose`, Dokploy, Migrations).
|
||||||
|
- Monitoring/Alerting pflegen (`ops/monitoring-observability.md`).
|
||||||
|
- Incident‑Response bei SEV‑1/SEV‑2 (`ops/incidents-major.md`).
|
||||||
|
|
||||||
|
### 1.2 On-Call Engineer
|
||||||
|
|
||||||
|
- Rolle, die im wechselnden Turnus (z.B. wöchentlich) On‑Call ist.
|
||||||
|
- Verantwortlich für:
|
||||||
|
- Reaktion auf laufende Alerts (Monitoring, Pager, Chat‑Bots).
|
||||||
|
- Erstes Triage nach `ops/incidents-major.md`.
|
||||||
|
- Eskalation an weitere Rollen (z.B. Platform Ops, Produkt, Security).
|
||||||
|
- Voraussetzungen:
|
||||||
|
- Zugriff auf Produktions‑Logs, Monitoring‑Dashboards, Dokploy/Horizon.
|
||||||
|
- Vertraut mit den wichtigsten Runbooks (Public‑API, Storage, Photobooth, Billing).
|
||||||
|
|
||||||
|
### 1.3 Support / Customer Success
|
||||||
|
|
||||||
|
- Verantwortlich für:
|
||||||
|
- Kontakt mit Tenants (E‑Mail/Telefon/Chat).
|
||||||
|
- Übersetzung technischer Probleme in Kundensprache.
|
||||||
|
- Sammeln aller relevanten Informationen, bevor an On‑Call/Platform Ops eskaliert wird.
|
||||||
|
- Typische Aufgaben:
|
||||||
|
- Tickets aus dem Help‑System triagieren.
|
||||||
|
- Proaktive Kommunikation bei Events („Wir haben ein Upload‑Problem identifiziert, wir arbeiten daran“).
|
||||||
|
|
||||||
|
### 1.4 Produkt / Engineering Leads
|
||||||
|
|
||||||
|
- Verantwortlich für:
|
||||||
|
- Entscheidungen bei Feature‑Flags, Rollbacks, Hotfix‑Releases.
|
||||||
|
- Priorisierung langfristiger Maßnahmen nach Incidents (`docs/process/roadmap.md`, `docs/process/todo/*`).
|
||||||
|
- Typische Aufgaben:
|
||||||
|
- Teilnahme an Postmortems.
|
||||||
|
- Freigabe von riskanteren Änderungen (z.B. große Migrations).
|
||||||
|
|
||||||
|
## 2. On-Call-Modell
|
||||||
|
|
||||||
|
### 2.1 Bereitschaftszeiten
|
||||||
|
|
||||||
|
Empfohlene Einteilung (anpassbar an dein Team):
|
||||||
|
|
||||||
|
- **Bürozeiten (z.B. 09:00–17:00)**
|
||||||
|
- On‑Call ist die jeweils zuständige Platform‑Ops‑Person des Tages.
|
||||||
|
- Reaktionsziel: 15 Minuten bei SEV‑1/2, 60 Minuten bei SEV‑3.
|
||||||
|
- **Außerhalb der Bürozeiten / Event-Spitzen**
|
||||||
|
- Optional: Rotierender On‑Call‑Dienst mit Rufbereitschaft.
|
||||||
|
- Reaktionsziel: nach individueller Vereinbarung (z.B. 30–60 Minuten bei SEV‑1).
|
||||||
|
|
||||||
|
> Wenn ihr keinen formalen 24/7‑Dienst habt, sollte klar dokumentiert sein, **wann** keine garantierte Reaktionszeit besteht (z.B. nachts/wochenends) und wie das Kunden gegenüber kommuniziert wird.
|
||||||
|
|
||||||
|
### 2.2 Rotation & Übergabe
|
||||||
|
|
||||||
|
- On‑Call‑Rotation (z.B. wöchentlich) im Teamtool (Kalender/Issue‑Tracker) pflegen.
|
||||||
|
- Vor Start einer Schicht:
|
||||||
|
- Offene Incidents und bekannte Problemzonen durchgehen.
|
||||||
|
- Sicherstellen, dass Aufrufwege funktionieren (Chat, Telefon, Pager).
|
||||||
|
- Nach Schicht:
|
||||||
|
- Kurze Übergabe an nächste On‑Call‑Person (offene Themen, laufende Beobachtungen).
|
||||||
|
|
||||||
|
## 3. Eskalationspfad bei Incidents
|
||||||
|
|
||||||
|
### 3.1 Standard-Eskalation (SEV-2/3)
|
||||||
|
|
||||||
|
1. On‑Call nimmt Alert entgegen, prüft grob die Lage (`ops/incidents-major.md` → Triage).
|
||||||
|
2. Wenn Problem lösbar erscheint:
|
||||||
|
- Runbooks anwenden (z.B. Public‑API‑Playbook, Medien‑Runbook, Photobooth‑Ops).
|
||||||
|
- Kundenkommunikation via Support abstimmen.
|
||||||
|
3. Wenn unklar oder größer:
|
||||||
|
- Platform‑Ops bzw. Engineering Lead im Chat markieren.
|
||||||
|
- Incident‑Thread mit Statusupdates führen.
|
||||||
|
|
||||||
|
### 3.2 SEV-1 (kritisch)
|
||||||
|
|
||||||
|
1. On‑Call ruft sofort **Platform‑Ops** und ggf. **Produkt‑Lead** in den Incident‑Thread.
|
||||||
|
2. Falls nötig, mit Produkt die Entscheidung für:
|
||||||
|
- Rollback auf letztes Release,
|
||||||
|
- temporäre Abschaltung einzelner Features (Feature‑Flags),
|
||||||
|
- Aktivierung einer Maintenance‑Seite
|
||||||
|
treffen.
|
||||||
|
3. Support/Success informieren betroffene Tenants mit kurzem Status und ETA (auch wenn ETA noch grob ist).
|
||||||
|
|
||||||
|
## 4. Tools & Zugänge
|
||||||
|
|
||||||
|
Für On‑Call/Platform‑Ops sollten mindestens folgende Zugänge eingerichtet und getestet sein:
|
||||||
|
|
||||||
|
- **Dokploy / Docker-Orchestrierung**
|
||||||
|
- Zugriff auf Compose‑Stacks, Logs, Health‑Checks.
|
||||||
|
- **Horizon / Queue-Monitoring**
|
||||||
|
- Zugriff auf `/horizon` (nur für SuperAdmins).
|
||||||
|
- **Logs**
|
||||||
|
- Zentralisierte Logs (Loki/ELK) oder SSH‑Zugriff zur Maschine mit `storage/logs`.
|
||||||
|
- **Monitoring/Alerts**
|
||||||
|
- Zugang zu Uptime‑/Monitoring‑Service (Status‑Dashboard, Alert‑Konfiguration).
|
||||||
|
|
||||||
|
> Stelle sicher, dass On‑Call‑Personen ausprobiert haben, ob sie diese Tools tatsächlich erreichen können (VPN, 2FA, etc.), bevor eine Schicht beginnt.
|
||||||
|
|
||||||
|
## 5. Verbindung zu den Runbooks
|
||||||
|
|
||||||
|
Bei einem Incident sollte die On‑Call‑Person immer vom **Betriebshandbuch** aus denken:
|
||||||
|
|
||||||
|
- Einstieg über `docs/ops/operations-manual.md` (Docusaurus‑Startseite).
|
||||||
|
- Je nach Symptome:
|
||||||
|
- **API-/Frontend-Probleme** → Public‑API‑Playbook (`ops/deployment/public-api-incident-playbook.md`), ggf. Marketing/Guest‑PWA‑Spezifikationen in `docs/prp/` (in PRP, nicht im Ops‑Bereich).
|
||||||
|
- **Upload/Storage-Probleme** → `ops/media-storage-spec.md`, `ops/guest-notification-ops.md`.
|
||||||
|
- **Photobooth** → `ops/photobooth/ops_playbook.md`.
|
||||||
|
- **Abrechnung** → `ops/billing-ops.md`.
|
||||||
|
- **DSGVO-Fälle** → `ops/compliance-dsgvo-ops.md`.
|
||||||
|
|
||||||
|
Dieses Dokument soll nicht alle technischen Details wiederholen, sondern sicherstellen, dass immer klar ist, **wer** reagiert und **welches** Runbook als nächstes geöffnet werden sollte.
|
||||||
131
docs/ops/operations-manual.md
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
---
|
||||||
|
title: Betriebshandbuch & Ops-Startseite
|
||||||
|
sidebar_label: Betriebshandbuch
|
||||||
|
slug: /
|
||||||
|
---
|
||||||
|
|
||||||
|
Willkommen im Betriebshandbuch von Fotospiel. Dieses Dokument ist der Einstiegspunkt für alle, die die Plattform betreiben: Infrastruktur‑Ops, On‑Call, Support mit erweiterten Rechten und Produkt‑Owner, die Auswirkungen von Änderungen verstehen möchten.
|
||||||
|
|
||||||
|
Ziel ist, dass du von hier aus schnell zu den relevanten Runbooks und Referenzen springen kannst.
|
||||||
|
|
||||||
|
## 1. Systemübersicht & Verantwortlichkeiten
|
||||||
|
|
||||||
|
- **Rollen & Verantwortlichkeiten**
|
||||||
|
- Wer ist für welche Ebene zuständig? (App‑Verfügbarkeit, Infrastruktur, Sicherheit, Abrechnung, Support.)
|
||||||
|
- Empfehlung: definiere mindestens _On‑Call_, _Plattform‑Ops_ und _Support (2nd Level)_ als feste Rollen – diese Seite ist für alle drei.
|
||||||
|
- **Systemlandschaft (High‑Level)**
|
||||||
|
- Laravel App + Nginx + Redis + MySQL.
|
||||||
|
- Async‑Pipeline: Queues (`default`, `media-storage`, `media-security`, `notifications`) und Horizon.
|
||||||
|
- Satelliten: Photobooth‑FTP + Control‑Service, Docs‑Site (`/internal-docs`), Monitoring/Dokploy.
|
||||||
|
- Externe Dienste: Paddle (Billing), RevenueCat (Mobile‑Abos), E‑Mail Provider, Logging/Monitoring (Loki/Grafana o.ä.).
|
||||||
|
|
||||||
|
> TODO: Ergänze ein Architekturdiagramm aus Sicht des Betriebs (z.B. als PNG oder PlantUML) und verlinke es hier.
|
||||||
|
|
||||||
|
## 2. Deployments & Infrastruktur
|
||||||
|
|
||||||
|
Diese Kapitel erklären, wie die Plattform in Docker/Dokploy betrieben wird.
|
||||||
|
|
||||||
|
- **Docker-Deployment (Compose‑Stack)**
|
||||||
|
- `docs/ops/deployment/docker.md` – Referenz für `docker-compose.yml`, Services, Volumes, Migrations und Scheduler‑Setup.
|
||||||
|
- **Dokploy-Deployment (PaaS)**
|
||||||
|
- `docs/ops/deployment/dokploy.md` – Wie die gleichen Services als Dokploy‑Compose‑Stacks betrieben werden, inkl. SuperAdmin‑Integration.
|
||||||
|
- **Join-Token-Analytics & Public API**
|
||||||
|
- `docs/ops/deployment/join-token-analytics.md` – Konfiguration der Analytics‑Pfade für Join‑Tokens.
|
||||||
|
- `docs/ops/deployment/public-api-incident-playbook.md` – Runbook für Public‑API‑Störungen (Rate‑Limits, Abuse, Outages).
|
||||||
|
- **Lokale Podman/Dev-URLs**
|
||||||
|
- `docs/ops/deployment/lokale-podman-adressen.md` – Übersicht über lokale Services/Ports bei Podman‑Setups.
|
||||||
|
|
||||||
|
Fragen zur Infrastruktur (Netzwerk, TLS, DNS, Backups) sollten immer zuerst gegen diese Dokumente geprüft werden.
|
||||||
|
|
||||||
|
## 3. Queues, Storage & Medien-Pipeline
|
||||||
|
|
||||||
|
Fotos sind das Herz des Produkts – entsprechend wichtig ist ein klarer Blick auf die Medien‑Pipeline.
|
||||||
|
|
||||||
|
- **Queues & Worker**
|
||||||
|
- `docs/ops/queue-workers.md` – Wie die Worker‑Container (`queue`, `media-storage-worker`, `media-security-worker`, `notifications`) gestartet, skaliert und überwacht werden.
|
||||||
|
- **Media Storage & Archivierung**
|
||||||
|
- `docs/ops/media-storage-spec.md` – Detaillierte Beschreibung, wie Uploads in den „hot“‑Storage laufen, wie `event_media_assets` gepflegt werden und wie Archive‑Jobs funktionieren.
|
||||||
|
- **Upload-Gesundheit & Notifications**
|
||||||
|
- `docs/ops/guest-notification-ops.md` – Runbook für das Notification‑Center, Push‑Registrierung und Upload‑Health‑Alerts.
|
||||||
|
|
||||||
|
> TODO: Ergänze ein zentrales „Storage & Queues Monitoring“-Kapitel mit konkreten Schwellenwerten und Alarmierung (z.B. Einbindung von Horizon, Redis‑Monitoring, Log-Channels).
|
||||||
|
|
||||||
|
## 4. Photobooth-Pipeline
|
||||||
|
|
||||||
|
Die Photobooth‑Integration hat eigene Betriebsanforderungen:
|
||||||
|
|
||||||
|
- `docs/ops/photobooth/README.md` – Überblick über Photobooth‑Setup und Datenfluss.
|
||||||
|
- `docs/ops/photobooth/control_service.md` – Steuer‑API (User‑Provisionierung, Credentials, Rate‑Limits).
|
||||||
|
- `docs/ops/photobooth/ops_playbook.md` – Operatives Playbook für Aktivierung, Fehleranalyse und Incident‑Response rund um Photobooth‑Uploads.
|
||||||
|
|
||||||
|
> Prüfe vor großen Events mit gebuchten Photobooths diese Playbooks und stelle sicher, dass Volumes, Credentials und Scheduler korrekt konfiguriert sind.
|
||||||
|
|
||||||
|
## 5. Störungs- & Incident-Runbooks
|
||||||
|
|
||||||
|
Die folgenden Dokumente sind deine erste Anlaufstelle im Incident‑Fall:
|
||||||
|
|
||||||
|
- **Major Incidents & Eskalation**
|
||||||
|
- `docs/ops/incidents-major.md` – genereller Rahmen (SEV‑Levels, Triage, Kommunikation, Postmortems) und Verweise auf die spezifischen Runbooks unten.
|
||||||
|
- **Public API Störungen**
|
||||||
|
- `docs/ops/deployment/public-api-incident-playbook.md` – Schritt‑für‑Schritt‑Plan bei Missbrauch, Fehlerspitzen oder Ausfällen der öffentlichen APIs.
|
||||||
|
- **Upload-/Medien-Probleme**
|
||||||
|
- `docs/ops/media-storage-spec.md` – Referenz, welche Queues/Jobs beteiligt sind und wie man Fehlerzustände erkennt (z.B. lange „pending“-Assets, gescheiterte Archivierung).
|
||||||
|
- `docs/ops/guest-notification-ops.md` – Upload‑Alerts und Gastbenachrichtigungen.
|
||||||
|
- **Photobooth-Incidents**
|
||||||
|
- `docs/ops/photobooth/ops_playbook.md` – Vorgehen bei ausfallendem FTP, Ingest‑Fehlern oder Sicherheitsvorfällen (Credentials).
|
||||||
|
|
||||||
|
Zusätzlich gibt es kurze „How‑to“-Runbooks für häufige Supportfälle:
|
||||||
|
|
||||||
|
- `docs/ops/howto-tenant-package-not-active.md` – Zahlung erfolgreich, Paket nicht aktiv.
|
||||||
|
- `docs/ops/howto-guest-upload-failing.md` – Gäste können nicht hochladen.
|
||||||
|
- `docs/ops/howto-photobooth-no-photos.md` – Photobooth‑Uploads landen nicht im Event.
|
||||||
|
- `docs/ops/howto-dsgvo-delete-photo.md` – DSGVO‑Löschung eines konkreten Fotos.
|
||||||
|
|
||||||
|
> TODO: Ergänze ein allgemeines „Major Incident“‑Kapitel (SEV‑1/2 Definition, Kommunikationskanäle, Vorlagen) und verlinke es hier.
|
||||||
|
|
||||||
|
## 6. Prozesse, Roadmap & Änderungen
|
||||||
|
|
||||||
|
Der Betreiber muss wissen, welche größeren Änderungen anstehen oder kürzlich live gegangen sind.
|
||||||
|
|
||||||
|
- **Prozess-Hub**
|
||||||
|
- `docs/process/README.md` – erklärt Struktur von `changes/`, `todo/` und `roadmap.md`.
|
||||||
|
- **Roadmap & Epics**
|
||||||
|
- `docs/process/roadmap.md` – Überblick über aktive Epics (z.B. Security Hardening, Paddle‑Migration, Streaming‑Uploads) und kürzlich abgeschlossene Themen.
|
||||||
|
- `docs/process/todo/security-hardening-epic.md` – Security‑Hardening‑Plan mit Bezug zu Ops (Signierte URLs, AV/EXIF, Monitoring‑Workstreams).
|
||||||
|
- Paddle‑Themen: `docs/process/todo/paddle-migration.md`, `docs/process/todo/paddle-catalog-sync.md`.
|
||||||
|
- **Changes & Retro-Notizen**
|
||||||
|
- `docs/process/changes/*` – Session‑Notizen, Refactor‑Pläne und Lessons Learned (z.B. Checkout‑Refactor, Registrierung‑Fixes).
|
||||||
|
|
||||||
|
Als Betreiber lohnt es sich, bei größeren Deployments kurz in `roadmap.md` und den passenden `changes/*` zu schauen, um Seiteneffekte zu antizipieren.
|
||||||
|
|
||||||
|
## 7. Tests, Qualität & Releases
|
||||||
|
|
||||||
|
Stabile Releases sind Grundvoraussetzung für ruhigen Betrieb:
|
||||||
|
|
||||||
|
- **E2E-Tests & Qualität**
|
||||||
|
- `docs/testing/e2e.md` – beschreibt, welche End‑to‑End‑Tests existieren und wie sie als Smoke‑Suite für Releases verwendet werden können.
|
||||||
|
- **Release-Prozess (Entwurf)**
|
||||||
|
- `docs/ops/releases.md` – Checkliste für CI‑Pipelines, Staging‑Deploy, Prod‑Rollout, Smoke‑Tests und Rollback‑Überlegungen.
|
||||||
|
|
||||||
|
## 8. Nächste Schritte für das Betriebshandbuch
|
||||||
|
|
||||||
|
Die folgenden Kapitel sind als eigenständige Runbooks angelegt und sollten mit der Zeit weiter gefüllt werden:
|
||||||
|
|
||||||
|
- **Rollen & On-Call-Handbuch**
|
||||||
|
- `docs/ops/oncall-roles.md` – definiert Platform‑Ops, On‑Call, Support und Produktrollen sowie Eskalationswege.
|
||||||
|
- **On-Call Cheat Sheet**
|
||||||
|
- `docs/ops/oncall-cheatsheet.md` – schnelle Übersicht über wichtige Kommandos, Logs und Dashboards für Incidents.
|
||||||
|
- **Support & Eskalation**
|
||||||
|
- `docs/ops/support-escalation-guide.md` – beschreibt, welche Informationen Support von Kunden einsammeln sollte, bevor an Ops eskaliert wird.
|
||||||
|
- **Backup & Restore / Disaster Recovery**
|
||||||
|
- `docs/ops/backup-restore.md` – Was gesichert werden muss, Restore‑Szenarien und DR‑Übungen.
|
||||||
|
- **DSGVO & Compliance-Operationen**
|
||||||
|
- `docs/ops/compliance-dsgvo-ops.md` – Praktische Abläufe für Auskunfts‑/Löschanfragen, Retention und Dokumentation.
|
||||||
|
- **Billing & Zahlungs-Operationen**
|
||||||
|
- `docs/ops/billing-ops.md` – Umgang mit Zahlungsproblemen, Webhook‑Fehlern und Paket‑Inkonsistenzen.
|
||||||
|
- **Monitoring & Observability**
|
||||||
|
- `docs/ops/monitoring-observability.md` – Welche Signale, Metriken und Alerts es geben sollte.
|
||||||
|
- **Architektur-Diagramme**
|
||||||
|
- `docs/ops/diagrams.md` – Mermaid‑Diagramme für Media‑Pipeline und Checkout/Billing‑Flow.
|
||||||
|
|
||||||
|
Das Betriebshandbuch bleibt damit ein lebendes Dokument. Neue Runbooks sollten unter `docs/ops/` entstehen und hier verlinkt werden, damit Operatoren einen klaren Einstiegspunkt behalten.
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
## Docker Queue & Horizon Setup
|
## Docker Queue & Horizon Setup
|
||||||
|
|
||||||
This directory bundles ready-to-use entrypoint scripts and deployment notes for running Fotospiel’s queue workers inside Docker containers. The examples assume you already run the main application in Docker (e.g. via `docker-compose.yml`) and share the same application image for workers. The shell scripts referenced below remain under `/docs/queue-supervisor/` so existing Dockerfile references stay valid.
|
This directory bundles ready-to-use entrypoint scripts and deployment notes for running Fotospiel’s queue workers inside Docker containers. The examples assume you already run the main application in Docker (e.g. via `docker-compose.yml`) and share the same application image for workers. Queue entrypoints now live in `/scripts/` inside the container so every service can execute the same shell scripts.
|
||||||
|
|
||||||
### 1. Prepare the application image
|
### 1. Prepare the application image
|
||||||
|
|
||||||
Make sure the worker scripts are copied into the image and marked as executable:
|
Make sure the worker scripts are copied into the image and marked as executable:
|
||||||
|
|
||||||
```dockerfile
|
```dockerfile
|
||||||
# Dockerfile
|
# Dockerfile (excerpt)
|
||||||
COPY docs/queue-supervisor /var/www/html/docs/queue-supervisor
|
COPY scripts /var/www/html/scripts
|
||||||
RUN chmod +x /var/www/html/docs/queue-supervisor/*.sh
|
RUN chmod +x /var/www/html/scripts/*.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
If you keep the project root mounted as a volume during development the `chmod` step can be skipped because the files will inherit host permissions.
|
If you keep the project root mounted as a volume during development the `chmod` step can be skipped because the files will inherit host permissions.
|
||||||
@@ -31,7 +31,7 @@ services:
|
|||||||
QUEUE_TRIES: 3 # optional overrides
|
QUEUE_TRIES: 3 # optional overrides
|
||||||
QUEUE_SLEEP: 3
|
QUEUE_SLEEP: 3
|
||||||
command: >
|
command: >
|
||||||
/var/www/html/docs/queue-supervisor/queue-worker.sh default
|
/var/www/html/scripts/queue-worker.sh default
|
||||||
|
|
||||||
media-storage-worker:
|
media-storage-worker:
|
||||||
image: fotospiel-app
|
image: fotospiel-app
|
||||||
@@ -44,7 +44,7 @@ services:
|
|||||||
QUEUE_TRIES: 5
|
QUEUE_TRIES: 5
|
||||||
QUEUE_SLEEP: 5
|
QUEUE_SLEEP: 5
|
||||||
command: >
|
command: >
|
||||||
/var/www/html/docs/queue-supervisor/queue-worker.sh media-storage
|
/var/www/html/scripts/queue-worker.sh media-storage
|
||||||
|
|
||||||
media-security-worker:
|
media-security-worker:
|
||||||
image: fotospiel-app
|
image: fotospiel-app
|
||||||
@@ -57,7 +57,7 @@ services:
|
|||||||
QUEUE_TRIES: 3
|
QUEUE_TRIES: 3
|
||||||
QUEUE_SLEEP: 5
|
QUEUE_SLEEP: 5
|
||||||
command: >
|
command: >
|
||||||
/var/www/html/docs/queue-supervisor/queue-worker.sh media-security
|
/var/www/html/scripts/queue-worker.sh media-security
|
||||||
```
|
```
|
||||||
|
|
||||||
Scale workers by increasing `deploy.replicas` (Swarm) or adding `scale` counts (Compose v2).
|
Scale workers by increasing `deploy.replicas` (Swarm) or adding `scale` counts (Compose v2).
|
||||||
@@ -79,7 +79,7 @@ services:
|
|||||||
APP_ENV: ${APP_ENV:-production}
|
APP_ENV: ${APP_ENV:-production}
|
||||||
QUEUE_CONNECTION: redis
|
QUEUE_CONNECTION: redis
|
||||||
command: >
|
command: >
|
||||||
/var/www/html/docs/queue-supervisor/horizon.sh
|
/var/www/html/scripts/horizon.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Expose Horizon via your web proxy and protect it with authentication (the app already guards `/horizon` behind the super admin panel login if configured).
|
Expose Horizon via your web proxy and protect it with authentication (the app already guards `/horizon` behind the super admin panel login if configured).
|
||||||
|
|||||||
48
docs/ops/releases.md
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
title: Releases & Deployments
|
||||||
|
sidebar_label: Releases & Deployments
|
||||||
|
---
|
||||||
|
|
||||||
|
Dieses Dokument beschreibt, wie Releases vorbereitet und durchgeführt werden und welche Tests aus Ops‑Sicht Pflicht sind.
|
||||||
|
|
||||||
|
## 1. Vor dem Release
|
||||||
|
|
||||||
|
- Changelog grob durchsehen (Feature‑/Bugfix‑Umfang verstehen).
|
||||||
|
- Datenbank‑Migrations prüfen:
|
||||||
|
- Sind `up`/`down` sauber und idempotent?
|
||||||
|
- Gibt es lange laufende Migrations (Index‑Builds)?
|
||||||
|
- Konfig‑Änderungen:
|
||||||
|
- Neue ENV‑Variablen in `dokploy`/Compose hinterlegt?
|
||||||
|
- Secrets über Secret‑Store / Dokploy‑UI konfiguriert?
|
||||||
|
|
||||||
|
## 2. Pflicht‑Tests vor Prod‑Deploy
|
||||||
|
|
||||||
|
- **PHPUnit**:
|
||||||
|
- `php artisan test` oder mindestens relevante Suites (z.B. „Checkout“, „Storage“).
|
||||||
|
- **Frontend‑Build**:
|
||||||
|
- `npm run build` (bzw. CI‑Job).
|
||||||
|
- **E2E‑Smoke‑Tests** (siehe `docs/testing/e2e.md`):
|
||||||
|
- Guest‑Flow: Event beitreten, Foto hochladen, Anzeige prüfen.
|
||||||
|
- Tenant‑Flow: Login, Event anlegen, Medienübersicht öffnen.
|
||||||
|
|
||||||
|
## 3. Deployment‑Ablauf (Beispiel Dokploy)
|
||||||
|
|
||||||
|
- Neues Image wird gebaut und getaggt (z.B. `fotospiel-app:2025-11-20`).
|
||||||
|
- Dokploy‑Stack aktualisieren:
|
||||||
|
- App‑Container.
|
||||||
|
- Queue/Horizon‑Container.
|
||||||
|
- Docs‑Container (falls betroffen).
|
||||||
|
- Nach dem Deploy:
|
||||||
|
- `php artisan migrate --force`.
|
||||||
|
- Queues prüfen (`horizon:status`, `queue:failed`).
|
||||||
|
- Schnelle Smoke‑Tests in Prod (nur lesende Aktionen oder Test‑Tenant).
|
||||||
|
|
||||||
|
## 4. Rollback‑Strategie
|
||||||
|
|
||||||
|
- Vor dem Deploy aktuellen Datenbank‑Snapshot sicherstellen.
|
||||||
|
- Vorheriges Image‑Tag notieren.
|
||||||
|
- Rollback:
|
||||||
|
- Dokploy/Compose auf vorheriges Image zurückdrehen.
|
||||||
|
- Falls Migrations rückwärtskompatibel: ggf. `migrate:rollback`.
|
||||||
|
- Incident‑Eintrag mit Ursache und Lessons Learned ergänzen.
|
||||||
|
|
||||||
50
docs/ops/support-escalation-guide.md
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
---
|
||||||
|
title: Support → Ops Eskalationsleitfaden
|
||||||
|
sidebar_label: Support-Eskalation
|
||||||
|
---
|
||||||
|
|
||||||
|
Dieses Dokument beschreibt, welche Informationen der Support einsammeln sollte, bevor ein Ticket an Ops eskaliert wird. Ziel: weniger Ping‑Pong, schnellere Lösung.
|
||||||
|
|
||||||
|
## 1. Pflichtinfos pro Ticket
|
||||||
|
|
||||||
|
- **Tenant‑ID** bzw. Tenant‑Slug.
|
||||||
|
- **Event‑ID** bzw. Event‑Slug.
|
||||||
|
- **Zeitstempel** der Beobachtung (lokale Zeit + Zeitzone).
|
||||||
|
- **Betroffene User**:
|
||||||
|
- Gast‑Session ID (falls verfügbar).
|
||||||
|
- E‑Mail (für Tenant‑Admins).
|
||||||
|
- **Umgebung**:
|
||||||
|
- Browser + Version.
|
||||||
|
- Betriebssystem / Device.
|
||||||
|
- Mobil / Desktop.
|
||||||
|
- **Screenshots / Screenrecording**:
|
||||||
|
- Fehlermeldungen.
|
||||||
|
- UI‑Zustand (z.B. Upload hängt bei 90 %).
|
||||||
|
|
||||||
|
## 2. Typische Fälle & Zusatzinfos
|
||||||
|
|
||||||
|
- **Upload schlägt fehl**
|
||||||
|
- URL des Join‑Links.
|
||||||
|
- Anzahl betroffener Gäste (einige / viele / alle).
|
||||||
|
- Grobe Dateigröße (Handyfoto, stark komprimiert, RAW etc.).
|
||||||
|
- **Photobooth‑Fotos fehlen**
|
||||||
|
- Name/Typ der Photobooth.
|
||||||
|
- Zeitpunkt der letzten sichtbaren Fotos.
|
||||||
|
- Ob die Photobooth selbst Fehler anzeigt.
|
||||||
|
- **Paket nicht aktiv / Limits falsch**
|
||||||
|
- Bestellnummer / Paddle‑Checkout‑ID (falls vorhanden).
|
||||||
|
- Zeitpunkt der Zahlung.
|
||||||
|
- Welches Paket wurde erwartet?
|
||||||
|
|
||||||
|
## 3. Wie an Ops übergeben?
|
||||||
|
|
||||||
|
- Ticket im Tracker mit Label „ops“ versehen.
|
||||||
|
- Kurzes Summary in ein bis zwei Sätzen:
|
||||||
|
- „Gäste können seit 18:30 Uhr im Event XYZ keine Fotos hochladen. Fehler: ‚Upload fehlgeschlagen‘.“
|
||||||
|
- Alle oben genannten Pflichtinfos als strukturierte Liste ergänzen.
|
||||||
|
|
||||||
|
Siehe auch:
|
||||||
|
|
||||||
|
- `docs/ops/oncall-roles.md`
|
||||||
|
- `docs/ops/oncall-cheatsheet.md`
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ const config = {
|
|||||||
routeBasePath: '/',
|
routeBasePath: '/',
|
||||||
sidebarPath: require.resolve('./sidebars.js'),
|
sidebarPath: require.resolve('./sidebars.js'),
|
||||||
include: ['**/*.md', '**/*.mdx'],
|
include: ['**/*.md', '**/*.mdx'],
|
||||||
exclude: ['site/**', 'archive/**', '**/_drafts/**'],
|
exclude: ['site/**', 'help/**', 'agents/**', 'content/**', 'archive/**', '**/_drafts/**'],
|
||||||
editUrl: undefined,
|
editUrl: undefined,
|
||||||
showLastUpdateAuthor: true,
|
showLastUpdateAuthor: true,
|
||||||
showLastUpdateTime: true,
|
showLastUpdateTime: true,
|
||||||
|
|||||||
@@ -4,7 +4,145 @@
|
|||||||
|
|
||||||
/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
|
/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
|
||||||
const sidebars = {
|
const sidebars = {
|
||||||
docsSidebar: [{ type: 'autogenerated', dirName: '.' }],
|
docsSidebar: [
|
||||||
|
// Ops first: Betriebshandbuch + alle Runbooks
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'Ops & Betrieb',
|
||||||
|
collapsed: false,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'Grundlagen',
|
||||||
|
collapsed: false,
|
||||||
|
items: [
|
||||||
|
'ops/operations-manual',
|
||||||
|
'ops/oncall-roles',
|
||||||
|
'ops/oncall-cheatsheet',
|
||||||
|
'ops/support-escalation-guide',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'Incidents & DR',
|
||||||
|
items: [
|
||||||
|
'ops/incidents-major',
|
||||||
|
'ops/backup-restore',
|
||||||
|
'ops/dr-tenant-event-restore',
|
||||||
|
'ops/dr-storage-issues',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'Medien & Upload',
|
||||||
|
items: [
|
||||||
|
'ops/media-storage-spec',
|
||||||
|
'ops/guest-notification-ops',
|
||||||
|
'ops/queue-workers',
|
||||||
|
'ops/howto-guest-upload-failing',
|
||||||
|
'ops/howto-photobooth-no-photos',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'Photobooth',
|
||||||
|
items: [
|
||||||
|
'ops/photobooth/README',
|
||||||
|
'ops/photobooth/control_service',
|
||||||
|
'ops/photobooth/ops_playbook',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'Billing',
|
||||||
|
items: [
|
||||||
|
'ops/billing-ops',
|
||||||
|
'ops/howto-tenant-package-not-active',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'DSGVO & Compliance',
|
||||||
|
items: [
|
||||||
|
'ops/compliance-dsgvo-ops',
|
||||||
|
'ops/howto-dsgvo-delete-photo',
|
||||||
|
'ops/howto-tenant-full-export',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'Deployment',
|
||||||
|
items: [
|
||||||
|
'ops/deployment/docker',
|
||||||
|
'ops/deployment/dokploy',
|
||||||
|
'ops/deployment/join-token-analytics',
|
||||||
|
'ops/deployment/lokale-podman-adressen',
|
||||||
|
'ops/deployment/public-api-incident-playbook',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'Releases & Tests',
|
||||||
|
items: [
|
||||||
|
'ops/releases',
|
||||||
|
'testing/e2e',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'Monitoring & Diagramme',
|
||||||
|
items: [
|
||||||
|
'ops/monitoring-observability',
|
||||||
|
'ops/diagrams',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// Prozesse, Roadmap, Changes
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'Prozess & Roadmap',
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
'process/README',
|
||||||
|
'process/roadmap',
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'TODO / Epics',
|
||||||
|
items: [
|
||||||
|
'process/todo/security-hardening-epic',
|
||||||
|
'process/todo/paddle-migration',
|
||||||
|
'process/todo/paddle-catalog-sync',
|
||||||
|
'process/todo/localized-seo-hreflang-strategy',
|
||||||
|
'process/todo/media-streaming-upload-refactor',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'Changes',
|
||||||
|
items: [
|
||||||
|
'process/changes/2025-09-08-session',
|
||||||
|
'process/changes/2025-10-02-registration-role-fixes',
|
||||||
|
'process/changes/2025-10-05-checkout-refactor-todo',
|
||||||
|
'process/changes/2025-10-09-paypal-sdk-migration',
|
||||||
|
'process/changes/2025-10-10-tenant-admin-onboarding-plan',
|
||||||
|
'process/changes/2025-11-08-coupon-ops',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// Testing / Qualität
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'Testing',
|
||||||
|
collapsed: true,
|
||||||
|
items: ['testing/e2e'],
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = sidebars;
|
module.exports = sidebars;
|
||||||
|
|||||||