From 9afcaa78362783fffa11968b1193a07f173216ff Mon Sep 17 00:00:00 2001 From: Codex Agent Date: Thu, 20 Nov 2025 12:31:21 +0100 Subject: [PATCH] further rework to the documentation --- docker-compose.dokploy.yml | 6 +- docker-compose.yml | 6 +- docker/app/entrypoint.sh | 4 +- .../ADR-0006-tenant-admin-pwa.md | 0 .../{ => archive}/plan-superadmin-filament.md | 0 ...rp-addendum-2025-09-08-tenant-admin-pwa.md | 0 docs/{ => archive}/prp/01-architecture.md | 0 docs/{ => archive}/prp/02-tenancy.md | 0 docs/{ => archive}/prp/03-api.md | 0 .../prp/04-data-model-migrations.md | 0 docs/{ => archive}/prp/05-admin-superadmin.md | 0 docs/{ => archive}/prp/06-tenant-admin-pwa.md | 0 .../prp/07-guest-pwa-routes-components.md | 0 docs/{ => archive}/prp/07-guest-pwa.md | 0 docs/{ => archive}/prp/08-billing.md | 0 .../prp/09-security-compliance.md | 0 .../prp/10-storage-media-pipeline.md | 0 docs/{ => archive}/prp/11-ops-ci-cd.md | 0 docs/{ => archive}/prp/11-public-gallery.md | 0 docs/{ => archive}/prp/12-i18n.md | 0 .../prp/13-backend-authentication.md | 0 .../prp/14-freemium-business-model.md | 0 docs/{ => archive}/prp/15-packages-design.md | 0 docs/{ => archive}/prp/99-glossary.md | 0 docs/{ => archive}/prp/README.md | 0 ...marketing-checkout-payment-architecture.md | 0 .../prp/marketing-frontend-unification.md | 0 .../prp/packages-ui-improvements.md | 0 docs/{ => archive}/prp/public-entrypoints.md | 0 .../prp/tenant-app-specs/README.md | 0 .../prp/tenant-app-specs/api-usage.md | 0 .../prp/tenant-app-specs/capacitor-setup.md | 0 .../prp/tenant-app-specs/functional-specs.md | 0 .../prp/tenant-app-specs/pages-ui-legacy.md | 0 .../prp/tenant-app-specs/pages-ui.md | 0 .../prp/tenant-app-specs/settings-config.md | 0 docs/{ => archive}/screenshots/1start.png | Bin docs/{ => archive}/screenshots/2start.png | Bin .../screenshots/3emotionpicker.png | Bin .../screenshots/4-taskscreen.png | Bin .../screenshots/5-camerapage.png | Bin .../screenshots/6-general-landing-page.png | Bin .../screenshots/7-event-landing-page.png | Bin .../screenshots/free-step1-home.png | Bin .../screenshots/free-step1-packages.png | Bin .../screenshots/free-step2-packages.png | Bin .../screenshots/paid-end-step1-packages.png | Bin .../screenshots/paid-res-step1-packages.png | Bin .../01-welcome-hero.svg | 0 .../02-how-it-works.svg | 0 .../03-package-selection.svg | 0 .../04-order-summary.svg | 0 .../05-event-setup.svg | 0 .../tenant-admin-onboarding/README.md | 0 .../wireframes/PWA_Wireframes.txt | 0 docs/{ => archive}/wireframes/guest-pwa.md | 0 docs/{ => content}/legal/agb-de.md | 0 docs/{ => content}/legal/agb-en.md | 0 docs/{ => content}/legal/datenschutz-de.md | 0 docs/{ => content}/legal/datenschutz-en.md | 0 docs/{ => content}/legal/impressum-de.md | 0 docs/{ => content}/legal/impressum-en.md | 0 docs/ops/README.md | 2 +- docs/ops/backup-restore.md | 94 +++++++++++ docs/ops/billing-ops.md | 133 ++++++++++++++++ docs/ops/compliance-dsgvo-ops.md | 90 +++++++++++ docs/ops/deployment/docker.md | 2 +- docs/ops/deployment/dokploy.md | 12 +- docs/ops/diagrams.md | 42 +++++ docs/ops/dr-storage-issues.md | 74 +++++++++ docs/ops/dr-tenant-event-restore.md | 97 ++++++++++++ docs/ops/guest-notification-ops.md | 2 +- docs/ops/howto-dsgvo-delete-photo.md | 79 ++++++++++ docs/ops/howto-guest-upload-failing.md | 83 ++++++++++ docs/ops/howto-photobooth-no-photos.md | 87 +++++++++++ docs/ops/howto-tenant-full-export.md | 52 +++++++ docs/ops/howto-tenant-package-not-active.md | 95 ++++++++++++ docs/ops/incidents-major.md | 79 ++++++++++ docs/ops/media-storage-spec.md | 4 +- docs/ops/monitoring-observability.md | 146 ++++++++++++++++++ docs/ops/oncall-cheatsheet.md | 48 ++++++ docs/ops/oncall-roles.md | 126 +++++++++++++++ docs/ops/operations-manual.md | 131 ++++++++++++++++ docs/ops/queue-workers.md | 16 +- docs/ops/releases.md | 48 ++++++ docs/ops/support-escalation-guide.md | 50 ++++++ docs/site/docusaurus.config.js | 2 +- docs/site/sidebars.js | 140 ++++++++++++++++- {docs/queue-supervisor => scripts}/horizon.sh | 0 .../queue-worker.sh | 0 90 files changed, 1721 insertions(+), 29 deletions(-) rename docs/{adr => archive}/ADR-0006-tenant-admin-pwa.md (100%) rename docs/{ => archive}/plan-superadmin-filament.md (100%) rename docs/{ => archive}/prp-addendum-2025-09-08-tenant-admin-pwa.md (100%) rename docs/{ => archive}/prp/01-architecture.md (100%) rename docs/{ => archive}/prp/02-tenancy.md (100%) rename docs/{ => archive}/prp/03-api.md (100%) rename docs/{ => archive}/prp/04-data-model-migrations.md (100%) rename docs/{ => archive}/prp/05-admin-superadmin.md (100%) rename docs/{ => archive}/prp/06-tenant-admin-pwa.md (100%) rename docs/{ => archive}/prp/07-guest-pwa-routes-components.md (100%) rename docs/{ => archive}/prp/07-guest-pwa.md (100%) rename docs/{ => archive}/prp/08-billing.md (100%) rename docs/{ => archive}/prp/09-security-compliance.md (100%) rename docs/{ => archive}/prp/10-storage-media-pipeline.md (100%) rename docs/{ => archive}/prp/11-ops-ci-cd.md (100%) rename docs/{ => archive}/prp/11-public-gallery.md (100%) rename docs/{ => archive}/prp/12-i18n.md (100%) rename docs/{ => archive}/prp/13-backend-authentication.md (100%) rename docs/{ => archive}/prp/14-freemium-business-model.md (100%) rename docs/{ => archive}/prp/15-packages-design.md (100%) rename docs/{ => archive}/prp/99-glossary.md (100%) rename docs/{ => archive}/prp/README.md (100%) rename docs/{ => archive}/prp/marketing-checkout-payment-architecture.md (100%) rename docs/{ => archive}/prp/marketing-frontend-unification.md (100%) rename docs/{ => archive}/prp/packages-ui-improvements.md (100%) rename docs/{ => archive}/prp/public-entrypoints.md (100%) rename docs/{ => archive}/prp/tenant-app-specs/README.md (100%) rename docs/{ => archive}/prp/tenant-app-specs/api-usage.md (100%) rename docs/{ => archive}/prp/tenant-app-specs/capacitor-setup.md (100%) rename docs/{ => archive}/prp/tenant-app-specs/functional-specs.md (100%) rename docs/{ => archive}/prp/tenant-app-specs/pages-ui-legacy.md (100%) rename docs/{ => archive}/prp/tenant-app-specs/pages-ui.md (100%) rename docs/{ => archive}/prp/tenant-app-specs/settings-config.md (100%) rename docs/{ => archive}/screenshots/1start.png (100%) rename docs/{ => archive}/screenshots/2start.png (100%) rename docs/{ => archive}/screenshots/3emotionpicker.png (100%) rename docs/{ => archive}/screenshots/4-taskscreen.png (100%) rename docs/{ => archive}/screenshots/5-camerapage.png (100%) rename docs/{ => archive}/screenshots/6-general-landing-page.png (100%) rename docs/{ => archive}/screenshots/7-event-landing-page.png (100%) rename docs/{ => archive}/screenshots/free-step1-home.png (100%) rename docs/{ => archive}/screenshots/free-step1-packages.png (100%) rename docs/{ => archive}/screenshots/free-step2-packages.png (100%) rename docs/{ => archive}/screenshots/paid-end-step1-packages.png (100%) rename docs/{ => archive}/screenshots/paid-res-step1-packages.png (100%) rename docs/{ => archive}/screenshots/tenant-admin-onboarding/01-welcome-hero.svg (100%) rename docs/{ => archive}/screenshots/tenant-admin-onboarding/02-how-it-works.svg (100%) rename docs/{ => archive}/screenshots/tenant-admin-onboarding/03-package-selection.svg (100%) rename docs/{ => archive}/screenshots/tenant-admin-onboarding/04-order-summary.svg (100%) rename docs/{ => archive}/screenshots/tenant-admin-onboarding/05-event-setup.svg (100%) rename docs/{ => archive}/screenshots/tenant-admin-onboarding/README.md (100%) rename docs/{ => archive}/wireframes/PWA_Wireframes.txt (100%) rename docs/{ => archive}/wireframes/guest-pwa.md (100%) rename docs/{ => content}/legal/agb-de.md (100%) rename docs/{ => content}/legal/agb-en.md (100%) rename docs/{ => content}/legal/datenschutz-de.md (100%) rename docs/{ => content}/legal/datenschutz-en.md (100%) rename docs/{ => content}/legal/impressum-de.md (100%) rename docs/{ => content}/legal/impressum-en.md (100%) create mode 100644 docs/ops/backup-restore.md create mode 100644 docs/ops/billing-ops.md create mode 100644 docs/ops/compliance-dsgvo-ops.md create mode 100644 docs/ops/diagrams.md create mode 100644 docs/ops/dr-storage-issues.md create mode 100644 docs/ops/dr-tenant-event-restore.md create mode 100644 docs/ops/howto-dsgvo-delete-photo.md create mode 100644 docs/ops/howto-guest-upload-failing.md create mode 100644 docs/ops/howto-photobooth-no-photos.md create mode 100644 docs/ops/howto-tenant-full-export.md create mode 100644 docs/ops/howto-tenant-package-not-active.md create mode 100644 docs/ops/incidents-major.md create mode 100644 docs/ops/monitoring-observability.md create mode 100644 docs/ops/oncall-cheatsheet.md create mode 100644 docs/ops/oncall-roles.md create mode 100644 docs/ops/operations-manual.md create mode 100644 docs/ops/releases.md create mode 100644 docs/ops/support-escalation-guide.md rename {docs/queue-supervisor => scripts}/horizon.sh (100%) rename {docs/queue-supervisor => scripts}/queue-worker.sh (100%) diff --git a/docker-compose.dokploy.yml b/docker-compose.dokploy.yml index 40bc03c..540b565 100644 --- a/docker-compose.dokploy.yml +++ b/docker-compose.dokploy.yml @@ -117,7 +117,7 @@ services: queue: 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: <<: *app-env SKIP_CODE_SYNC: "1" @@ -134,7 +134,7 @@ services: media-storage-worker: 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: <<: *app-env QUEUE_TRIES: 5 @@ -169,7 +169,7 @@ services: horizon: 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: <<: *app-env SKIP_CODE_SYNC: "1" diff --git a/docker-compose.yml b/docker-compose.yml index 0d48f3b..ceaeb1c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -42,7 +42,7 @@ services: queue: 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: - docker/.env.docker environment: @@ -56,7 +56,7 @@ services: media-storage-worker: 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: - docker/.env.docker environment: @@ -85,7 +85,7 @@ services: horizon: image: fotospiel-app:latest - command: /var/www/html/docs/queue-supervisor/horizon.sh + command: /var/www/html/scripts/horizon.sh env_file: - docker/.env.docker environment: diff --git a/docker/app/entrypoint.sh b/docker/app/entrypoint.sh index f2ba6cd..1a9016b 100644 --- a/docker/app/entrypoint.sh +++ b/docker/app/entrypoint.sh @@ -27,8 +27,8 @@ sync_code() { } ensure_helper_scripts() { - if compgen -G "$APP_TARGET/docs/queue-supervisor/*.sh" > /dev/null; then - chmod +x "$APP_TARGET"/docs/queue-supervisor/*.sh || true + if compgen -G "$APP_TARGET/scripts/*.sh" > /dev/null; then + chmod +x "$APP_TARGET"/scripts/*.sh || true fi } diff --git a/docs/adr/ADR-0006-tenant-admin-pwa.md b/docs/archive/ADR-0006-tenant-admin-pwa.md similarity index 100% rename from docs/adr/ADR-0006-tenant-admin-pwa.md rename to docs/archive/ADR-0006-tenant-admin-pwa.md diff --git a/docs/plan-superadmin-filament.md b/docs/archive/plan-superadmin-filament.md similarity index 100% rename from docs/plan-superadmin-filament.md rename to docs/archive/plan-superadmin-filament.md diff --git a/docs/prp-addendum-2025-09-08-tenant-admin-pwa.md b/docs/archive/prp-addendum-2025-09-08-tenant-admin-pwa.md similarity index 100% rename from docs/prp-addendum-2025-09-08-tenant-admin-pwa.md rename to docs/archive/prp-addendum-2025-09-08-tenant-admin-pwa.md diff --git a/docs/prp/01-architecture.md b/docs/archive/prp/01-architecture.md similarity index 100% rename from docs/prp/01-architecture.md rename to docs/archive/prp/01-architecture.md diff --git a/docs/prp/02-tenancy.md b/docs/archive/prp/02-tenancy.md similarity index 100% rename from docs/prp/02-tenancy.md rename to docs/archive/prp/02-tenancy.md diff --git a/docs/prp/03-api.md b/docs/archive/prp/03-api.md similarity index 100% rename from docs/prp/03-api.md rename to docs/archive/prp/03-api.md diff --git a/docs/prp/04-data-model-migrations.md b/docs/archive/prp/04-data-model-migrations.md similarity index 100% rename from docs/prp/04-data-model-migrations.md rename to docs/archive/prp/04-data-model-migrations.md diff --git a/docs/prp/05-admin-superadmin.md b/docs/archive/prp/05-admin-superadmin.md similarity index 100% rename from docs/prp/05-admin-superadmin.md rename to docs/archive/prp/05-admin-superadmin.md diff --git a/docs/prp/06-tenant-admin-pwa.md b/docs/archive/prp/06-tenant-admin-pwa.md similarity index 100% rename from docs/prp/06-tenant-admin-pwa.md rename to docs/archive/prp/06-tenant-admin-pwa.md diff --git a/docs/prp/07-guest-pwa-routes-components.md b/docs/archive/prp/07-guest-pwa-routes-components.md similarity index 100% rename from docs/prp/07-guest-pwa-routes-components.md rename to docs/archive/prp/07-guest-pwa-routes-components.md diff --git a/docs/prp/07-guest-pwa.md b/docs/archive/prp/07-guest-pwa.md similarity index 100% rename from docs/prp/07-guest-pwa.md rename to docs/archive/prp/07-guest-pwa.md diff --git a/docs/prp/08-billing.md b/docs/archive/prp/08-billing.md similarity index 100% rename from docs/prp/08-billing.md rename to docs/archive/prp/08-billing.md diff --git a/docs/prp/09-security-compliance.md b/docs/archive/prp/09-security-compliance.md similarity index 100% rename from docs/prp/09-security-compliance.md rename to docs/archive/prp/09-security-compliance.md diff --git a/docs/prp/10-storage-media-pipeline.md b/docs/archive/prp/10-storage-media-pipeline.md similarity index 100% rename from docs/prp/10-storage-media-pipeline.md rename to docs/archive/prp/10-storage-media-pipeline.md diff --git a/docs/prp/11-ops-ci-cd.md b/docs/archive/prp/11-ops-ci-cd.md similarity index 100% rename from docs/prp/11-ops-ci-cd.md rename to docs/archive/prp/11-ops-ci-cd.md diff --git a/docs/prp/11-public-gallery.md b/docs/archive/prp/11-public-gallery.md similarity index 100% rename from docs/prp/11-public-gallery.md rename to docs/archive/prp/11-public-gallery.md diff --git a/docs/prp/12-i18n.md b/docs/archive/prp/12-i18n.md similarity index 100% rename from docs/prp/12-i18n.md rename to docs/archive/prp/12-i18n.md diff --git a/docs/prp/13-backend-authentication.md b/docs/archive/prp/13-backend-authentication.md similarity index 100% rename from docs/prp/13-backend-authentication.md rename to docs/archive/prp/13-backend-authentication.md diff --git a/docs/prp/14-freemium-business-model.md b/docs/archive/prp/14-freemium-business-model.md similarity index 100% rename from docs/prp/14-freemium-business-model.md rename to docs/archive/prp/14-freemium-business-model.md diff --git a/docs/prp/15-packages-design.md b/docs/archive/prp/15-packages-design.md similarity index 100% rename from docs/prp/15-packages-design.md rename to docs/archive/prp/15-packages-design.md diff --git a/docs/prp/99-glossary.md b/docs/archive/prp/99-glossary.md similarity index 100% rename from docs/prp/99-glossary.md rename to docs/archive/prp/99-glossary.md diff --git a/docs/prp/README.md b/docs/archive/prp/README.md similarity index 100% rename from docs/prp/README.md rename to docs/archive/prp/README.md diff --git a/docs/prp/marketing-checkout-payment-architecture.md b/docs/archive/prp/marketing-checkout-payment-architecture.md similarity index 100% rename from docs/prp/marketing-checkout-payment-architecture.md rename to docs/archive/prp/marketing-checkout-payment-architecture.md diff --git a/docs/prp/marketing-frontend-unification.md b/docs/archive/prp/marketing-frontend-unification.md similarity index 100% rename from docs/prp/marketing-frontend-unification.md rename to docs/archive/prp/marketing-frontend-unification.md diff --git a/docs/prp/packages-ui-improvements.md b/docs/archive/prp/packages-ui-improvements.md similarity index 100% rename from docs/prp/packages-ui-improvements.md rename to docs/archive/prp/packages-ui-improvements.md diff --git a/docs/prp/public-entrypoints.md b/docs/archive/prp/public-entrypoints.md similarity index 100% rename from docs/prp/public-entrypoints.md rename to docs/archive/prp/public-entrypoints.md diff --git a/docs/prp/tenant-app-specs/README.md b/docs/archive/prp/tenant-app-specs/README.md similarity index 100% rename from docs/prp/tenant-app-specs/README.md rename to docs/archive/prp/tenant-app-specs/README.md diff --git a/docs/prp/tenant-app-specs/api-usage.md b/docs/archive/prp/tenant-app-specs/api-usage.md similarity index 100% rename from docs/prp/tenant-app-specs/api-usage.md rename to docs/archive/prp/tenant-app-specs/api-usage.md diff --git a/docs/prp/tenant-app-specs/capacitor-setup.md b/docs/archive/prp/tenant-app-specs/capacitor-setup.md similarity index 100% rename from docs/prp/tenant-app-specs/capacitor-setup.md rename to docs/archive/prp/tenant-app-specs/capacitor-setup.md diff --git a/docs/prp/tenant-app-specs/functional-specs.md b/docs/archive/prp/tenant-app-specs/functional-specs.md similarity index 100% rename from docs/prp/tenant-app-specs/functional-specs.md rename to docs/archive/prp/tenant-app-specs/functional-specs.md diff --git a/docs/prp/tenant-app-specs/pages-ui-legacy.md b/docs/archive/prp/tenant-app-specs/pages-ui-legacy.md similarity index 100% rename from docs/prp/tenant-app-specs/pages-ui-legacy.md rename to docs/archive/prp/tenant-app-specs/pages-ui-legacy.md diff --git a/docs/prp/tenant-app-specs/pages-ui.md b/docs/archive/prp/tenant-app-specs/pages-ui.md similarity index 100% rename from docs/prp/tenant-app-specs/pages-ui.md rename to docs/archive/prp/tenant-app-specs/pages-ui.md diff --git a/docs/prp/tenant-app-specs/settings-config.md b/docs/archive/prp/tenant-app-specs/settings-config.md similarity index 100% rename from docs/prp/tenant-app-specs/settings-config.md rename to docs/archive/prp/tenant-app-specs/settings-config.md diff --git a/docs/screenshots/1start.png b/docs/archive/screenshots/1start.png similarity index 100% rename from docs/screenshots/1start.png rename to docs/archive/screenshots/1start.png diff --git a/docs/screenshots/2start.png b/docs/archive/screenshots/2start.png similarity index 100% rename from docs/screenshots/2start.png rename to docs/archive/screenshots/2start.png diff --git a/docs/screenshots/3emotionpicker.png b/docs/archive/screenshots/3emotionpicker.png similarity index 100% rename from docs/screenshots/3emotionpicker.png rename to docs/archive/screenshots/3emotionpicker.png diff --git a/docs/screenshots/4-taskscreen.png b/docs/archive/screenshots/4-taskscreen.png similarity index 100% rename from docs/screenshots/4-taskscreen.png rename to docs/archive/screenshots/4-taskscreen.png diff --git a/docs/screenshots/5-camerapage.png b/docs/archive/screenshots/5-camerapage.png similarity index 100% rename from docs/screenshots/5-camerapage.png rename to docs/archive/screenshots/5-camerapage.png diff --git a/docs/screenshots/6-general-landing-page.png b/docs/archive/screenshots/6-general-landing-page.png similarity index 100% rename from docs/screenshots/6-general-landing-page.png rename to docs/archive/screenshots/6-general-landing-page.png diff --git a/docs/screenshots/7-event-landing-page.png b/docs/archive/screenshots/7-event-landing-page.png similarity index 100% rename from docs/screenshots/7-event-landing-page.png rename to docs/archive/screenshots/7-event-landing-page.png diff --git a/docs/screenshots/free-step1-home.png b/docs/archive/screenshots/free-step1-home.png similarity index 100% rename from docs/screenshots/free-step1-home.png rename to docs/archive/screenshots/free-step1-home.png diff --git a/docs/screenshots/free-step1-packages.png b/docs/archive/screenshots/free-step1-packages.png similarity index 100% rename from docs/screenshots/free-step1-packages.png rename to docs/archive/screenshots/free-step1-packages.png diff --git a/docs/screenshots/free-step2-packages.png b/docs/archive/screenshots/free-step2-packages.png similarity index 100% rename from docs/screenshots/free-step2-packages.png rename to docs/archive/screenshots/free-step2-packages.png diff --git a/docs/screenshots/paid-end-step1-packages.png b/docs/archive/screenshots/paid-end-step1-packages.png similarity index 100% rename from docs/screenshots/paid-end-step1-packages.png rename to docs/archive/screenshots/paid-end-step1-packages.png diff --git a/docs/screenshots/paid-res-step1-packages.png b/docs/archive/screenshots/paid-res-step1-packages.png similarity index 100% rename from docs/screenshots/paid-res-step1-packages.png rename to docs/archive/screenshots/paid-res-step1-packages.png diff --git a/docs/screenshots/tenant-admin-onboarding/01-welcome-hero.svg b/docs/archive/screenshots/tenant-admin-onboarding/01-welcome-hero.svg similarity index 100% rename from docs/screenshots/tenant-admin-onboarding/01-welcome-hero.svg rename to docs/archive/screenshots/tenant-admin-onboarding/01-welcome-hero.svg diff --git a/docs/screenshots/tenant-admin-onboarding/02-how-it-works.svg b/docs/archive/screenshots/tenant-admin-onboarding/02-how-it-works.svg similarity index 100% rename from docs/screenshots/tenant-admin-onboarding/02-how-it-works.svg rename to docs/archive/screenshots/tenant-admin-onboarding/02-how-it-works.svg diff --git a/docs/screenshots/tenant-admin-onboarding/03-package-selection.svg b/docs/archive/screenshots/tenant-admin-onboarding/03-package-selection.svg similarity index 100% rename from docs/screenshots/tenant-admin-onboarding/03-package-selection.svg rename to docs/archive/screenshots/tenant-admin-onboarding/03-package-selection.svg diff --git a/docs/screenshots/tenant-admin-onboarding/04-order-summary.svg b/docs/archive/screenshots/tenant-admin-onboarding/04-order-summary.svg similarity index 100% rename from docs/screenshots/tenant-admin-onboarding/04-order-summary.svg rename to docs/archive/screenshots/tenant-admin-onboarding/04-order-summary.svg diff --git a/docs/screenshots/tenant-admin-onboarding/05-event-setup.svg b/docs/archive/screenshots/tenant-admin-onboarding/05-event-setup.svg similarity index 100% rename from docs/screenshots/tenant-admin-onboarding/05-event-setup.svg rename to docs/archive/screenshots/tenant-admin-onboarding/05-event-setup.svg diff --git a/docs/screenshots/tenant-admin-onboarding/README.md b/docs/archive/screenshots/tenant-admin-onboarding/README.md similarity index 100% rename from docs/screenshots/tenant-admin-onboarding/README.md rename to docs/archive/screenshots/tenant-admin-onboarding/README.md diff --git a/docs/wireframes/PWA_Wireframes.txt b/docs/archive/wireframes/PWA_Wireframes.txt similarity index 100% rename from docs/wireframes/PWA_Wireframes.txt rename to docs/archive/wireframes/PWA_Wireframes.txt diff --git a/docs/wireframes/guest-pwa.md b/docs/archive/wireframes/guest-pwa.md similarity index 100% rename from docs/wireframes/guest-pwa.md rename to docs/archive/wireframes/guest-pwa.md diff --git a/docs/legal/agb-de.md b/docs/content/legal/agb-de.md similarity index 100% rename from docs/legal/agb-de.md rename to docs/content/legal/agb-de.md diff --git a/docs/legal/agb-en.md b/docs/content/legal/agb-en.md similarity index 100% rename from docs/legal/agb-en.md rename to docs/content/legal/agb-en.md diff --git a/docs/legal/datenschutz-de.md b/docs/content/legal/datenschutz-de.md similarity index 100% rename from docs/legal/datenschutz-de.md rename to docs/content/legal/datenschutz-de.md diff --git a/docs/legal/datenschutz-en.md b/docs/content/legal/datenschutz-en.md similarity index 100% rename from docs/legal/datenschutz-en.md rename to docs/content/legal/datenschutz-en.md diff --git a/docs/legal/impressum-de.md b/docs/content/legal/impressum-de.md similarity index 100% rename from docs/legal/impressum-de.md rename to docs/content/legal/impressum-de.md diff --git a/docs/legal/impressum-en.md b/docs/content/legal/impressum-en.md similarity index 100% rename from docs/legal/impressum-en.md rename to docs/content/legal/impressum-en.md diff --git a/docs/ops/README.md b/docs/ops/README.md index 7da4483..aaec6dd 100644 --- a/docs/ops/README.md +++ b/docs/ops/README.md @@ -8,6 +8,6 @@ This section consolidates everything platform operators need: deployment guides, - `photobooth/` — FTP ingest service docs and ops playbooks. - `media-storage-spec.md` — Upload/archival flow overview. - `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. diff --git a/docs/ops/backup-restore.md b/docs/ops/backup-restore.md new file mode 100644 index 0000000..caa49a1 --- /dev/null +++ b/docs/ops/backup-restore.md @@ -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. diff --git a/docs/ops/billing-ops.md b/docs/ops/billing-ops.md new file mode 100644 index 0000000..c34423a --- /dev/null +++ b/docs/ops/billing-ops.md @@ -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. diff --git a/docs/ops/compliance-dsgvo-ops.md b/docs/ops/compliance-dsgvo-ops.md new file mode 100644 index 0000000..b32f46a --- /dev/null +++ b/docs/ops/compliance-dsgvo-ops.md @@ -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. diff --git a/docs/ops/deployment/docker.md b/docs/ops/deployment/docker.md index 8f09b80..f0392b8 100644 --- a/docs/ops/deployment/docker.md +++ b/docs/ops/deployment/docker.md @@ -67,7 +67,7 @@ If you already have data, skip the seeder or seed only new records. ## 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 docker compose up -d --scale queue=2 --scale media-storage-worker=2 diff --git a/docs/ops/deployment/dokploy.md b/docs/ops/deployment/dokploy.md index fdf3077..293d890 100644 --- a/docs/ops/deployment/dokploy.md +++ b/docs/ops/deployment/dokploy.md @@ -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`. | | **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. | -| **Horizon (optional)** | Run `docs/queue-supervisor/horizon.sh` for dashboard + metrics. | +| **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 `/scripts/horizon.sh` for dashboard + metrics. | | **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. | | **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** - Duplicate the image. - Commands: - - `docs/queue-supervisor/queue-worker.sh default` - - `docs/queue-supervisor/queue-worker.sh media-storage` - - `docs/queue-supervisor/queue-worker.sh media-security` - - Optionally create a dedicated container for Horizon using `docs/queue-supervisor/horizon.sh`. + - `/var/www/html/scripts/queue-worker.sh default` + - `/var/www/html/scripts/queue-worker.sh media-storage` + - `/var/www/html/scripts/queue-worker.sh media-security` + - Optionally create a dedicated container for Horizon using `/var/www/html/scripts/horizon.sh`. 4. **vsftpd + Photobooth control** - Nutze deinen bestehenden Docker-Compose-Stack (z. B. `docker-compose.dokploy.yml`) oder dedizierte Compose-Applikationen. diff --git a/docs/ops/diagrams.md b/docs/ops/diagrams.md new file mode 100644 index 0000000..41bec92 --- /dev/null +++ b/docs/ops/diagrams.md @@ -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 +``` + diff --git a/docs/ops/dr-storage-issues.md b/docs/ops/dr-storage-issues.md new file mode 100644 index 0000000..a0b9324 --- /dev/null +++ b/docs/ops/dr-storage-issues.md @@ -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. diff --git a/docs/ops/dr-tenant-event-restore.md b/docs/ops/dr-tenant-event-restore.md new file mode 100644 index 0000000..3a50ce1 --- /dev/null +++ b/docs/ops/dr-tenant-event-restore.md @@ -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. diff --git a/docs/ops/guest-notification-ops.md b/docs/ops/guest-notification-ops.md index 3b58e49..09d4f89 100644 --- a/docs/ops/guest-notification-ops.md +++ b/docs/ops/guest-notification-ops.md @@ -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: ```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. diff --git a/docs/ops/howto-dsgvo-delete-photo.md b/docs/ops/howto-dsgvo-delete-photo.md new file mode 100644 index 0000000..8db42fa --- /dev/null +++ b/docs/ops/howto-dsgvo-delete-photo.md @@ -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`. + diff --git a/docs/ops/howto-guest-upload-failing.md b/docs/ops/howto-guest-upload-failing.md new file mode 100644 index 0000000..e7010a0 --- /dev/null +++ b/docs/ops/howto-guest-upload-failing.md @@ -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`. + diff --git a/docs/ops/howto-photobooth-no-photos.md b/docs/ops/howto-photobooth-no-photos.md new file mode 100644 index 0000000..7563111 --- /dev/null +++ b/docs/ops/howto-photobooth-no-photos.md @@ -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`. + diff --git a/docs/ops/howto-tenant-full-export.md b/docs/ops/howto-tenant-full-export.md new file mode 100644 index 0000000..04f361e --- /dev/null +++ b/docs/ops/howto-tenant-full-export.md @@ -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` + diff --git a/docs/ops/howto-tenant-package-not-active.md b/docs/ops/howto-tenant-package-not-active.md new file mode 100644 index 0000000..f2863e5 --- /dev/null +++ b/docs/ops/howto-tenant-package-not-active.md @@ -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`. + diff --git a/docs/ops/incidents-major.md b/docs/ops/incidents-major.md new file mode 100644 index 0000000..1d18c42 --- /dev/null +++ b/docs/ops/incidents-major.md @@ -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. diff --git a/docs/ops/media-storage-spec.md b/docs/ops/media-storage-spec.md index 35272dd..04f37c4 100644 --- a/docs/ops/media-storage-spec.md +++ b/docs/ops/media-storage-spec.md @@ -33,7 +33,7 @@ This document explains how customer photo uploads move through the Fotospiel pla | Component | Role | | --- | --- | | `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. | | `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. | @@ -92,7 +92,7 @@ This document explains how customer photo uploads move through the Fotospiel pla ## Related Documentation - `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. - `config/security.php`, `config/storage-monitor.php`, and `config/filesystems.php` for runtime knobs. diff --git a/docs/ops/monitoring-observability.md b/docs/ops/monitoring-observability.md new file mode 100644 index 0000000..e8dbc81 --- /dev/null +++ b/docs/ops/monitoring-observability.md @@ -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. diff --git a/docs/ops/oncall-cheatsheet.md b/docs/ops/oncall-cheatsheet.md new file mode 100644 index 0000000..0990e55 --- /dev/null +++ b/docs/ops/oncall-cheatsheet.md @@ -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. + diff --git a/docs/ops/oncall-roles.md b/docs/ops/oncall-roles.md new file mode 100644 index 0000000..7d1aa14 --- /dev/null +++ b/docs/ops/oncall-roles.md @@ -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. diff --git a/docs/ops/operations-manual.md b/docs/ops/operations-manual.md new file mode 100644 index 0000000..1336255 --- /dev/null +++ b/docs/ops/operations-manual.md @@ -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. diff --git a/docs/ops/queue-workers.md b/docs/ops/queue-workers.md index 8967542..187f5d0 100644 --- a/docs/ops/queue-workers.md +++ b/docs/ops/queue-workers.md @@ -1,15 +1,15 @@ ## 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 Make sure the worker scripts are copied into the image and marked as executable: ```dockerfile -# Dockerfile -COPY docs/queue-supervisor /var/www/html/docs/queue-supervisor -RUN chmod +x /var/www/html/docs/queue-supervisor/*.sh +# Dockerfile (excerpt) +COPY scripts /var/www/html/scripts +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. @@ -31,7 +31,7 @@ services: QUEUE_TRIES: 3 # optional overrides QUEUE_SLEEP: 3 command: > - /var/www/html/docs/queue-supervisor/queue-worker.sh default + /var/www/html/scripts/queue-worker.sh default media-storage-worker: image: fotospiel-app @@ -44,7 +44,7 @@ services: QUEUE_TRIES: 5 QUEUE_SLEEP: 5 command: > - /var/www/html/docs/queue-supervisor/queue-worker.sh media-storage + /var/www/html/scripts/queue-worker.sh media-storage media-security-worker: image: fotospiel-app @@ -57,7 +57,7 @@ services: QUEUE_TRIES: 3 QUEUE_SLEEP: 5 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). @@ -79,7 +79,7 @@ services: APP_ENV: ${APP_ENV:-production} QUEUE_CONNECTION: redis 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). diff --git a/docs/ops/releases.md b/docs/ops/releases.md new file mode 100644 index 0000000..995beb2 --- /dev/null +++ b/docs/ops/releases.md @@ -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. + diff --git a/docs/ops/support-escalation-guide.md b/docs/ops/support-escalation-guide.md new file mode 100644 index 0000000..1c6a50a --- /dev/null +++ b/docs/ops/support-escalation-guide.md @@ -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` + diff --git a/docs/site/docusaurus.config.js b/docs/site/docusaurus.config.js index 128a680..76f36a9 100644 --- a/docs/site/docusaurus.config.js +++ b/docs/site/docusaurus.config.js @@ -35,7 +35,7 @@ const config = { routeBasePath: '/', sidebarPath: require.resolve('./sidebars.js'), include: ['**/*.md', '**/*.mdx'], - exclude: ['site/**', 'archive/**', '**/_drafts/**'], + exclude: ['site/**', 'help/**', 'agents/**', 'content/**', 'archive/**', '**/_drafts/**'], editUrl: undefined, showLastUpdateAuthor: true, showLastUpdateTime: true, diff --git a/docs/site/sidebars.js b/docs/site/sidebars.js index 8ce5c2c..85ac0c1 100644 --- a/docs/site/sidebars.js +++ b/docs/site/sidebars.js @@ -4,7 +4,145 @@ /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ 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; diff --git a/docs/queue-supervisor/horizon.sh b/scripts/horizon.sh similarity index 100% rename from docs/queue-supervisor/horizon.sh rename to scripts/horizon.sh diff --git a/docs/queue-supervisor/queue-worker.sh b/scripts/queue-worker.sh similarity index 100% rename from docs/queue-supervisor/queue-worker.sh rename to scripts/queue-worker.sh