Files
vollversammlung/Doku/infrastruktur_planung.md

13 KiB
Raw Permalink Blame History

Infrastruktur-Planung — Vollversammlung

Inhaltsverzeichnis

  1. docker-compose.yaml Services
  2. Netzwerk & Volumes
  3. api/ Verzeichnis Vorbereitungen
  4. frontend/ Verzeichnis Vorbereitungen
  5. Multi-Tenant Scoping Infrastruktur-Ansatz
  6. OAuth (Vonova) Infrastruktur
  7. Lokaler Start
  8. Health Checks
  9. .env.example Konfigurationen
  10. Checkliste Nächste Schritte

1. docker-compose.yaml Services

Sechs Services, alle über vollversammlung_net vernetzt:

Service Image Externer Port Internes Ziel Rolle
postgres-db postgres:16-alpine kein (intern) :5432 PostgreSQL-Datenbank, MandantID-gescoped
symfony-fpm php:8.3-fpm-alpine (custom) kein (über nginx) :9000 PHP-FPM Backend-Runtime
nginx nginx:alpine 8080 → :80 Reverse Proxy, routet /api/* zu symfony-fpm, statisch zu frontend
mercure-hub dunglas/mercure 3000 → :80 Event-Streaming für Logging in Echtzeit
mailpit axllent/mailpit 8025 (UI), intern :1025 (SMTP) E-Mail Testing
vite-dev node:22-alpine 5173 → :5173 React Dev Server mit Hot Module Replacement

2. Netzwerk & Volumes

Netzwerk

networks:
  vollversammlung_net:
    driver: bridge

Alle Services joined auf vollversammlung_net. Nur nginx, mercure-hub, vite-dev und mailpit haben externe Port-Mappings. postgres-db und symfony-fpm sind ausschließlich intern erreichbar.

Volumes

Named Volumes:

Name Mount-Punkt (symfony-fpm) / Ziel Zweck
pg_data /var/lib/postgresql/data (post-db) Persistente PostgreSQL-Daten
symfony_var /app/var/ Symfony cache, logs

Bind-mounts (Development only):

Host-Pfad Container Mount-Pfad Zweck
./api/src symfony-fpm /app/src Live-Coding PHP-Code
./frontend/src vite-dev /app/src Live-Coding React-Code

3. api/ Verzeichnis Vorbereitungen

Dateistruktur

api/
├── .env.example                 (Commit-Sicherheit, keine Secrets)
├── .env.local                   (gitignored - lokale Secrets)
├── .dockerignore                (Docker Build-Kontext filtering)
├── composer.json                (Symfony Flex setup)
├── Dockerfile.dev               (php:8.3-fpm-alpine Base)
├── Dockerfile.prod              (Multi-stage build für Produktion)
├── docker-entrypoint.sh         (Healthcheck + Init-Script)
├── public/
│   └── index.php                (Symfony Entry Point über FPM)
├── config/
│   ├── packages/                (framework.yaml, mercure.yaml, doctrine.yaml etc.)
│   └── routes/                  (API Routes YAML)
├── migrations/                  (Doctrine Migration-Dateien)
├── src/
│   ├── Controller/              (Manuelle Controller  kein API Platform!)
│   ├── Entity/                  (Domain Entities mit MandantID-Eigenschaft)
│   ├── Repository/              (Doctrine Repositories mit Tenant-Scoping)
│   ├── Service/                 (Business Logic Services)
│   ├── Middleware/              (MandantId-Middleware + OAuth-Processor)
│   └── Kernel.php
├── templates/                   (Twig Templates)
├── var/                         (cache, logs  via Volume gemountet)
├── var/cache                    → symfony_var Volume
├── var/log                      → symfony_var Volume
└── bin/console                  (Symfony Command-Wrapper)

PHP-Erweiterungen (Dockerfile.dev)

  • pdo_pgsql Doctrine DBAL-Konnektivität
  • gd Bildverarbeitung
  • intl Internationalisierung
  • mbregex Multibyte-Regex

4. frontend/ Verzeichnis Vorbereitungen

Dateistruktur

frontend/
├── .env.example                 (Commit-Sicherheit)
├── .env.local                   (gitignored - lokale Secrets)
├── .dockerignore                (Build-Kontext filtering)
├── package.json                 (React + Vite + TypeScript Dependencies)
├── vite.config.ts               (Dev Server + Proxy zu nginx/symfony-fpm)
├── tsconfig.json                (TypeScript Konfiguration)
├── Dockerfile.dev               (node:22-alpine Base)
├── public/                      (Statische Assets  favicon, manifest etc.)
└── src/
    ├── components/              (UI-Komponenten  z.B. Anwesenheitsliste, Statistiken)
    ├── pages/                   (Seiten  Dashboard, Veranstaltungsdetail etc.)
    ├── stores/                  (Zustandsmanagement  KEINE OAuth-Tokens!)
    ├── utils/                   (HTTP-Client, Auth-Helfer etc.)
    ├── App.tsx                  (Root-Komponente)
    └── main.tsx                 (Entry Point)

Vite Proxy-Konfiguration (vite.config.ts)

  • /api/*http://nginx:80/api/* (Reverse-Proxy auf Symfony)
  • /.well-known/mercurehttp://mercure-hub:3000/.well-known/mercure

Damit kommuniziert der Vite Dev Server (localhost:5173) nahtlos mit Backend-Services.


5. Multi-Tenant Scoping Infrastruktur-Ansatz

Kein Schema-per-Tenant. Stattdessen Row-Level-Scoping über mandant_id in ALLEN Tabellen.

Schicht Mechanismus
Client MandantID per HTTP Header X-Mandant-ID an API senden. Token NUR in HttpOnly, Secure, SameSite=lax Cookies.
Nginx Forwarded X-Mandant-ID Header via proxy_set_header an symfony-fpm weiter.
Symfony Middleware Extrahiert MandantID aus Cookie/Header → setzt als RequestAttribute.
Doctrine Layer Alle Entities erben Base-Entity mit mandant_id. Repositories scopen automatisch WHERE mandant_id = :mandantId.

Security-Garantie

  • Tenant-überschreitende Operationen technisch unmöglich ohne explizites WithoutScope-Attribut (standardmäßig nicht gewährt).
  • Keine email/benutzername-Felder in Personen-Entity Identität stammt ausschließlich aus OAuth.

6. OAuth (Vonova) Konfiguration

Backend Environment Variables (.env.local api/)

OAUTH_PROVIDER_NAME=vonova
OAUTH_AUTHORIZATION_ENDPOINT=https://oauth.vonova.de/oauth/authorize
OAUTH_TOKEN_ENDPOINT=https://oauth.vonova.de/oauth/token
OAUTH_CLIENT_ID=<from Vonova admin>
OAUTH_CLIENT_SECRET=<from Vonova admin>
OAUTH_REDIRECT_URI=http://localhost:8080/api/oauth/callback
OAUTH_SCOPES=read profile mandant_id

Frontend Environment Variables (frontend/.env.example)

VITE_OAUTH_AUTHORIZATION_ENDPOINT=https://oauth.vonova.de/oauth/authorize
VITE_OAUTH_TOKEN_ENDPOINT=https://oauth.vonova.de/oauth/token

OAuth-Flow (Kurzbeschreibung)

  1. Frontend leitet User zu Vonova Authorization Endpoint weiter.
  2. Vonova authentifiziert User, gibt Authorisierungscode an OAUTH_REDIRECT_URI zurück.
  3. Backend tauscht Code gegen Access-Token am Token Endpoint ein.
  4. Backend extrahiert MandantID und Profil-Informationen aus dem Token-Payload.
  5. Backend generiert Session-Cookie (HttpOnly, Secure, SameSite=lax).
  6. Folge-Requests enthalten Cookie automatisch → authentifizierte Api-Zugriffe.

Wichtig: Tokens werden NIEMALS auf dem Client gespeichert (kein localStorage/sessionStorage).


7. Lokaler Start

Setup

# Projekt rootVerzeichnis:

# Env-Templates kopieren (keine Secrets generieren  Werte aus .env.example übernehmen):
cp api/.env.example api/.env.local
cp frontend/.env.example frontend/.env.local

# APP_SECRET und JWT-Keys in .env.local ergänzen:
docker compose exec symfony-fpm php bin/console security:encode-password
# → Output als APP_SECRET eintragen

# Mercure-JWT-Keys manuell generieren (Mindestlänge 32 Zeichen, kryptografisch sicher):
# z.B.: openssl rand -base64 64

Services starten

# Alle Services im Hintergrund starten:
docker compose up -d

# Database-Migrations ausführen (erster Start):
docker compose exec symfony-fpm php bin/console doctrine:migrations:migrate --no-interaction

Verfügbare Endpunkte

URL Service
http://localhost:8080/ Frontend + API (via nginx)
http://localhost:3000/ Mercure Hub Debug Interface
http://localhost:5173/ Vite Dev Server (Hot Reload)
http://localhost:8025/ Mailpit E-Mail Testing UI

8. Health Checks

Jeder kritische Service verfügt über einen healthcheck in docker-compose.yaml:

Service Check-Methode Command
postgres-db pg_isready pg_isready -U vv_admin -d vollversammlung
mercure-hub HTTP-Well-Known-Path curl -sf http://localhost/.well-known/mercure
symfony-fpm API Health Endpoint (vom Backend-Coder zu implementieren) curl -sf http://localhost:8000/api/health
nginx Proxy-Pass zu symfony-fpm health curl -sf http://localhost:8080/api/health

9. .env.example Konfigurationen

api/.env.example

# =========================================
# Vollversammlung  Backend Environment
# Kopieren nach .env.local und anpassen!
# =========================================

APP_ENV=dev
APP_DEBUG=1
APP_SECRET=<symfony secret:generate oder security:encode-password>

DATABASE_URL="postgresql://vv_admin:<password>@postgres-db:5432/vollversammlung?serverVersion=16&charset=utf8mb4"

MERCURE_PUBLISHER_JWT_KEY=<openssl rand -base64 64>
MERCURE_SUBSCRIBER_JWT_KEY=<openssl rand -base64 64>
MERCURE_URL=http://mercure-hub/.well-known/mercure
MERCURE_PUBLIC_URL=http://localhost:3000/.well-known/mercure

MAILER_DSN=smtp://mailpit:1025

# =========================================
# Vonova OAuth
# =========================================
OAUTH_PROVIDER_NAME=vonova
OAUTH_AUTHORIZATION_ENDPOINT=https://oauth.vonova.de/oauth/authorize
OAUTH_TOKEN_ENDPOINT=https://oauth.vonova.de/oauth/token
OAUTH_CLIENT_ID=<from vonova admin>
OAUTH_CLIENT_SECRET=<from vonova admin>
OAUTH_REDIRECT_URI=http://localhost:8080/api/oauth/callback
OAUTH_SCOPES=read profile mandant_id

frontend/.env.example

# =========================================
# Vollversammlung  Frontend Environment
# Kopieren nach .env.local und anpassen!
# =========================================

VITE_API_URL=http://localhost:8080/api
VITE_MERCURE_URL=http://localhost:3000/.well-known/mercure
VITE_OAUTH_AUTHORIZATION_ENDPOINT=https://oauth.vonova.de/oauth/authorize
VITE_OAUTH_TOKEN_ENDPOINT=https://oauth.vonova.de/oauth/token

10. Multi-Environment Konfiguration

Variable dev (.env.local) uat/staging (.env.staging) prod (.env.prod)
APP_ENV dev uat prod
APP_DEBUG 1 0 0
CORS_ALLOW_ORIGIN http://localhost:5173 https://uat.company.com https://www.company.com
MAILER_DSN smtp://mailpit:1025 prod SMTP credentials prod SMTP credentials
DATABASE_URL localhost via docker externe PG-Instanz externe PG-Instanz
MERCURE_PUBLISHER_JWT_KEY dev-key (openssl rand) Vault-sekret Vault/AWS Secrets Manager
TRUSTED_HOSTS ^localhost$ ^uat\.example\.com$ ^www\.example\.com$
TRUSTED_PROXIES 127.0.0.1,10.0.0.0/8 Load Balancer IPs

11. Checkliste — Nächste Schritte zur Implementierung

Infrastruktur (DevOps-Coder)

  • docker-compose.yaml mit allen 6 Services schreiben
  • Networks & Volumes definieren
  • Health-Checks pro Service implementieren
  • nginx Konfiguration für Proxy-Routing erstellen
  • .env.example in api/ und frontend/ anlegen
  • Dockerfiles (dev + prod) pro Service erstellen

Backend (Backend-Coder)

  • Symfony-Projektstruktur in api/ aufsetzen
  • composer.json mit Symfony Flex konfigurieren
  • Entity-Base für Multi-Tenant Scoping erstellen (mandant_id column)
  • Doctrine Repositories mit automatischem Tenant-Scoping implementieren
  • MandantId-Middleware schreiben (Cookie/Header → RequestAttribute)
  • Vonova OAuth-Flow implementieren (Authorization Code Grant + PKCE)
  • Session-Cookie Generation nach OAuth-Flow (HttpOnly, Secure, SameSite=lax)
  • /api/health Endpoint erstellen
  • Migrationen für alle Templates schreiben

Frontend (Frontend-Coder)

  • React/Vite/TypeScript Setup in frontend/
  • OAuth Login-Flow im Browser implementieren (Authorization Code + PKCE mit redirect)
  • Auth-Stores ohne token storage (nur Cookie-basiert)
  • HTTP-Client mit MandantID-Header-Injection schreiben
  • Mercure-Client für Event-Subscription einrichten
  • Key-Pages: Dashboard, Veranstaltungsdetail, Anwesenheitsübersicht

Stand: Juni 2026 — Infrastruktur-Planung Vollversammlung