- TypeScript 80.5%
- Svelte 14.1%
- JavaScript 4.2%
- CSS 0.8%
- FreeMarker 0.2%
|
All checks were successful
CI Backend / lint-backend (pull_request) Successful in 1m1s
CI Frontend / lint-frontend (pull_request) Successful in 1m27s
CI Meta / conventional-commit (pull_request) Successful in 1s
E2E / e2e (pull_request) Successful in 5m51s
CI Backend / test-backend (pull_request) Successful in 4m4s
CI Frontend / test-frontend (pull_request) Successful in 2m6s
CI Frontend / lint-frontend (push) Successful in 1m31s
CI Meta / conventional-commit (push) Has been skipped
CI Frontend / test-frontend (push) Successful in 2m6s
Aktuell: form-editor.svelte nutzt <input type='number'> fuers Euro-Feld, was zu drei Bugs fuehrt - (1) deutsches Komma wird nicht akzeptiert, (2) select-all+type bricht zusammen, (3) mehr als 2 Nachkommastellen moeglich. Preisformatierung ist inkonsistent ueber drei Components verteilt (toFixed, toLocaleString, eigene formatEuroCent-Funktion). Neu: Zentrale Utility format-currency.ts mit parsePriceInput() und formatPriceDisplay(), die via Intl.NumberFormat automatisch locale-korrekt arbeitet (keine hardcodierte Liste). form-editor.svelte: type='text'+inputmode='decimal', Draft-Tracking waehrend Tippen, erst onBlur-Kommittierung nach Cent. form-fields.svelte/events-page: alle Preis-Anzeigen einheitlich ueber formatPriceDisplay(). formatPriceDisplay inkludiert EUR-Symbol via style:'currency'. Format- und Parsing-Logik ist zentral und bei neuen Locales automatisch korrekt. refs #324 Aktuelles: Preis-Eingabe und -Anzeige inkonsistent und nicht locale-faehig. Neu: format-currency.ts zentralisiert beide Funktionen. 1251 Tests pass, 75 Test-Files. |
||
|---|---|---|
| .gitea | ||
| .husky | ||
| .opencode | ||
| backend | ||
| docs | ||
| e2e | ||
| frontend | ||
| keycloak | ||
| scripts | ||
| shared/design-tokens | ||
| .env.example | ||
| .env.prod.example | ||
| .gitattributes | ||
| .gitignore | ||
| .nvmrc | ||
| .prettierignore | ||
| .prettierrc | ||
| AGENTS.md | ||
| docker-compose.prod.yml | ||
| docker-compose.yml | ||
| lint-staged.config.js | ||
| logo.svg | ||
| package-lock.json | ||
| package.json | ||
| projektstatistik-events.json | ||
| README.md | ||
FamilienFeierPlaner
Personen-, Kontakt- und Event-Management-System (Gaesteliste & Zahlungsabgleich).
Architektur
- Frontend — SvelteKit (BFF via
adapter-cloudflare, Fallbackadapter-node) + shadcn-svelte + Tailwind CSS v4 + Orval (API-Typgenerierung) - Backend — Node.js (Fastify) REST API + Prisma (ORM/Migrationen) + jose (JWT-Validierung) + AES-256-GCM (Application-Layer-Verschluesselung)
- Datenbank — PostgreSQL mit Gruppen/Haushalte, Composite-Rollen, Event-Planning-Mode, Soft-Delete
- Auth — Keycloak (OAuth2 / PKCE) mit Person-User-Trennung, Composite-Realm-Rollen (app-admin, event-admin, template-admin)
Verzeichnisstruktur
├── shared/design-tokens/tokens.css Gemeinsame CSS-Custom-Properties (Farben, Schriften, Abstaende)
├── frontend/ SvelteKit-App (BFF via adapter-cloudflare) + shadcn-svelte + Tailwind + Orval
├── backend/ Fastify REST API + Prisma + jose + OpenAPI-Spezifikation
├── keycloak/ Keycloak-Realm-Export + Custom Login-Theme (Design-Tokens-konform)
├── scripts/patterns/ Log-Pattern-Dateien fuer den Integrationstest (docker-test.mjs)
└── docs/ Technische Spezifikationen (Design-Dokument)
Erste Schritte
Voraussetzungen: Docker & Docker Compose
# Umgebungsvariablen aus Vorlage kopieren und ENCRYPTION_KEY anpassen
cp .env.example .env
# Backend-Abhaengigkeiten installieren (fuer lokale TypeScript-Entwicklung)
cd backend && npm install
# Vollen Stack starten
docker compose up --build
Damit werden gestartet:
- PostgreSQL (Port 5432) — relationale Datenbank
- Keycloak (Port 8080 HTTP / Port 8443 HTTPS, Admin:
admin/admin) — Authentifizierung (automatische Umschaltung viakeycloak/entrypoint.sh) - Fastify-Backend (Port 3000) — REST API inkl. Swagger-UI (
/docs) mit OAuth2-Integration (HTTP/1.1; mit TLS-Zertifikaten in/certs/automatisch HTTP/2 + HTTPS) - Frontend (Port 5173 Dev / Port 8788 wrangler pages dev) — SvelteKit + Tailwind + shadcn-svelte (Docker-Container nutzt
server.mjsfuer optionales HTTPS)
HTTPS im Docker-Stack (optional)
Fuer HTTPS im Docker-Stack muessen TLS-Zertifikate generiert werden:
node scripts/generate-dev-certs.mjs
Danach starten Backend, Frontend und Keycloak automatisch mit HTTPS (Port 3000 / 8443). Ohne Zertifikate laeuft alles auf HTTP (unveraendertes Verhalten).
Hintergrund: Der Integrationstest (npm run test:integration) erkennt automatisch, ob dev/certs/ existiert und schaltet auf HTTPS um.
Keycloak-Provisioning
Beim ersten Backend-Start wird Keycloak automatisch vorkonfiguriert:
- Realm-Import via
--import-realm: Realmfamilienfeierplaner, Clients (backend-api,frontend-app,swagger-ui) und Realm-Rollen (app-admin,event-admin,template-admin) werden angelegt. - Benutzer-Provisioning via Keycloak Admin API: Fuenf Testbenutzer werden mit zufaelligen 12-Zeichen-Passwoertern angelegt und die Passwoerter auf der Konsole ausgegeben.
Alle Benutzer erhalten beim Anlegen in Keycloak automatisch locale=de als User-Locale (gesteuert ueber DEFAULT_USER_LOCALE in backend/src/lib/i18n-config.ts).
| Benutzer | Rolle |
|---|---|
| Max Mustermann | App-Admin |
| Petra Musterfrau | App-Benutzer |
| Thomas Koenig | App-Benutzer |
| Lisa Winter | App-Benutzer |
| Anna Schmidt | App-Benutzer |
Frontend-Entwicklung
Das Frontend ist eine SvelteKit-App mit Tailwind CSS und shadcn-svelte:
cd frontend
# Abhaengigkeiten installieren (nach dem ersten Klonen; postinstall generiert automatisch API-Typen)
npm install
# Dev-Server starten (Port 5173, HMR via Vite)
npm run dev
# Produktions-Build (adapter-cloudflare)
npm run build
# Cloudflare-Pages-Preview (Build + wrangler pages dev, Port 8788)
npm run preview:pages
wrangler pages dev: Fuer den Production-Preview benoetigt wrangler
eigene Umgebungsvariablen — siehe .dev.vars.example (Kopie als .dev.vars).
Integrationstest
Ein automatisiertes Skript baut den gesamten Container-Stack, startet ihn und scannt Build-Output sowie Container-Logs auf Fehler und Warnungen. Anschliessend werden Playwright-E2E-Tests ausgefuehrt:
npm run test:integration
Das Skript erkennt automatisch, ob docker oder podman installiert ist. Mit Podman kann auch via Umgebungsvariable explizit gesteuert werden:
CONTAINER_CMD=podman npm run test:integration
Voraussetzung: Docker & Docker Compose (bzw. Podman + podman-compose), laufende Container-Engine.
Fuer reine Backend-Build-Tests (Lint + Kompilieren + Unit-Tests, ohne Container):
npm --prefix backend run test:build
E2E-Tests (Playwright)
Nach dem Health-Check fuehrt npm run test:integration automatisch Playwright-E2E-Tests aus.
Die E2E-Tests koennen auch manuell gestartet werden:
docker compose --profile integration-test run --rm e2e-runner npx playwright test
Health-Check pruefen:
curl http://localhost:3000/health
# {"status":"ok","database":"connected","keycloak":"reachable"}
Fahrplan
Der aktuelle Fortschritt wird in Gitea Milestones getrackt. Einzelne Aufgaben sind als Gitea Issues hinterlegt.
Sicherheit & Betrieb
Keycloak Hardening: Nicht genutzte Module (u.a. impersonation, token-exchange, docker, scripts) sind via keycloak/keycloak.conf im Image deaktiviert. Details zu allen deaktivierten Features:
→ Authentifizierung & Security — Gehaertete Keycloak-Konfiguration
Seit dem optimierten Image-Build (kc.sh build im Dockerfile) sind die deaktivierten Features build-time festgeschrieben. Zur Laufzeit ueber KC_FEATURES_DISABLED ueberschreiben erfordert KEYCLOAK_OPTIMIZED=false (deaktiviert den --optimized-Start, sodass die Runtime-Config greift).
Weitere Details zu Verschluesselung, Authentifizierung, Autorisierung, Audit-Log und Backup: → Authentifizierung & Security → Autorisierungsmatrix → Betrieb & Wartung
Technisches Design
Produktionsbetrieb
Fuer den Produktionsbetrieb ist ein Reverse-Proxy vor Keycloak und Frontend zwingend erforderlich (TLS-Terminierung, Port-Forwarding). Der Container-Stack (docker-compose.prod.yml) bindet alle Ports nur auf 127.0.0.1.
Beispiel: nginx
server {
listen 443 ssl;
server_name app.meine-domain.de;
ssl_certificate /etc/ssl/certs/meine-domain.de.crt;
ssl_certificate_key /etc/ssl/private/meine-domain.de.key;
location / {
proxy_pass http://127.0.0.1:5173;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 443 ssl;
server_name auth.meine-domain.de;
ssl_certificate /etc/ssl/certs/meine-domain.de.crt;
ssl_certificate_key /etc/ssl/private/meine-domain.de.key;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Beispiel: traefik (Labels in docker-compose.prod.yml)
Fuege folgende Labels zu den Services keycloak und frontend hinzu:
keycloak:
labels:
- "traefik.enable=true"
- "traefik.http.routers.keycloak.rule=Host(`auth.meine-domain.de`)"
- "traefik.http.routers.keycloak.tls=true"
- "traefik.http.services.keycloak.loadbalancer.server.port=8080"
frontend:
labels:
- "traefik.enable=true"
- "traefik.http.routers.frontend.rule=Host(`app.meine-domain.de`)"
- "traefik.http.routers.frontend.tls=true"
- "traefik.http.services.frontend.loadbalancer.server.port=3000"
Beispiel: cloudflared (Tunnel)
# Tunnel fuer die App (Port 5173)
cloudflared tunnel --url http://localhost:5173
# Tunnel fuer Keycloak (Port 8080) — separater Tunnel oder eigener Eintrag
cloudflared tunnel --url http://localhost:8080
Ergaenze anschliessend die DNS-Eintraege (CNAME) fuer app.meine-domain.de und auth.meine-domain.de in der Cloudflare-Zone.
Erstmalige Einrichtung
Nach dem Start des Produktions-Stacks (docker compose -f docker-compose.prod.yml up -d) ist das System unter https://app.meine-domain.de/setup bereit fuer die Einrichtung des ersten Admin-Users.
Der Setup-Webassistent fragt ein Einmal-Token ab, das beim Backend-Start in das Server-Log geschrieben wird:
# Token aus dem Log auslesen
docker logs -f familienfeierplaner-backend-api-1 2>&1 | grep "SETUP_TOKEN"
# Das Token wird nur beim ERSTEN Start generiert und nach erfolgreicher
# Einrichtung geloescht (Single-Use).
Das Token erscheint im Backend-Log in folgender Form:
ERSTSTART — Setup-Token: a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
Nach Eingabe des Tokens und Ausfuellen des Formulars wird der erste Admin-User (Person + Keycloak-User mit app-admin-Rolle) angelegt. Der User ist sofort aktiv und kann sich unter /login anmelden.
Hinweis: In der Entwicklungsumgebung (
APP_ENV=development) entfaellt die Token-Abfrage, da dort der Dev-Seed mit bekannten Testzugangsdaten zum Einsatz kommt.
API-Spezifikation
Die gesamte REST-API ist als OpenAPI 3.1-Spezifikation dokumentiert: