No description
  • TypeScript 80.5%
  • Svelte 14.1%
  • JavaScript 4.2%
  • CSS 0.8%
  • FreeMarker 0.2%
Find a file
Robin Franke 70ad1dfd35
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
fix(frontend): locale-aware currency input und zentrale formatPriceDisplay
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.
2026-07-22 19:27:55 +02:00
.gitea feat(infra): HTTPS + Frontend HTTP/2 via undici + E2E-Adaption (#307) 2026-07-19 20:01:50 +00:00
.husky ci(root): husky-pre-commit + Lint im Integrationstest 2026-06-26 19:41:32 +02:00
.opencode refactor(frontend): zentrale Test-Infrastruktur + Component-Rendering-Tests + Coverage (#269) 2026-07-08 19:46:04 +02:00
backend feat(backend): Contact-Encrypt-Helper mit Regressionsschutz 2026-07-22 18:04:47 +02:00
docs feat(infra): Keycloak --optimized Default-Start via Dockerfile-Build (#312) 2026-07-20 14:17:15 +00:00
e2e fix(frontend): Radio/Checkbox-Optionen bei neuen Vorlagen, Button-type-Korrektur (#322) 2026-07-21 21:29:08 +00:00
frontend fix(frontend): locale-aware currency input und zentrale formatPriceDisplay 2026-07-22 19:27:55 +02:00
keycloak feat(infra): Keycloak --optimized Default-Start via Dockerfile-Build (#312) 2026-07-20 14:17:15 +00:00
scripts feat(infra): HTTPS + Frontend HTTP/2 via undici + E2E-Adaption (#307) 2026-07-19 20:01:50 +00:00
shared/design-tokens fix(keycloak): Hintergrundfarbe an Landingpage angeglichen 2026-06-28 12:09:47 +02:00
.env.example feat(backend,frontend,infra): Production-Seed-Guard, Setup-Wizard, docker-compose.prod.yml (#314) 2026-07-20 16:34:46 +00:00
.env.prod.example feat(backend,frontend,infra): Production-Seed-Guard, Setup-Wizard, docker-compose.prod.yml (#314) 2026-07-20 16:34:46 +00:00
.gitattributes chore(infra): CI-Workflows bei Draft-PRs skippen + .gitattributes fuer konsistente Line-Endings (#291) 2026-07-10 11:49:10 +02:00
.gitignore feat(infra): HTTPS + Frontend HTTP/2 via undici + E2E-Adaption (#307) 2026-07-19 20:01:50 +00:00
.nvmrc chore(backend,frontend): upgrade node to 24 LTS (Krypton) 2026-07-02 19:09:38 +02:00
.prettierignore feat: ESLint (flat config) + Prettier implementiert 2026-06-26 15:23:31 +02:00
.prettierrc feat: ESLint (flat config) + Prettier implementiert 2026-06-26 15:23:31 +02:00
AGENTS.md feat(backend): User-Locale bei KC-User-Create setzen (locale=de) (#295) 2026-07-11 14:09:49 +02:00
docker-compose.prod.yml feat(backend,frontend,infra): Production-Seed-Guard, Setup-Wizard, docker-compose.prod.yml (#314) 2026-07-20 16:34:46 +00:00
docker-compose.yml feat(infra): HTTPS + Frontend HTTP/2 via undici + E2E-Adaption (#307) 2026-07-19 20:01:50 +00:00
lint-staged.config.js feat(e2e): Phase 2 Vollausbau + testid-Pflicht in allen Templates 2026-07-05 00:53:07 +02:00
logo.svg refactor(brand): Hexagon-Logo mit Doppelrand, Schatten und gebogenem Text 2026-07-01 22:19:54 +02:00
package-lock.json chore(deps): npm-Abhaengigkeiten auf aktuellste Minor/Patch-Staende aktualisiert (#299) 2026-07-19 12:46:56 +00:00
package.json chore(deps): npm-Abhaengigkeiten auf aktuellste Minor/Patch-Staende aktualisiert (#299) 2026-07-19 12:46:56 +00:00
projektstatistik-events.json chore(doc): reworded Zentrale Identitaet 2026-07-05 13:40:39 +02:00
README.md feat(backend,frontend,infra): Production-Seed-Guard, Setup-Wizard, docker-compose.prod.yml (#314) 2026-07-20 16:34:46 +00:00

FamilienFeierPlaner

Personen-, Kontakt- und Event-Management-System (Gaesteliste & Zahlungsabgleich).

Architektur

  • Frontend — SvelteKit (BFF via adapter-cloudflare, Fallback adapter-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 via keycloak/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.mjs fuer 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:

  1. Realm-Import via --import-realm: Realm familienfeierplaner, Clients (backend-api, frontend-app, swagger-ui) und Realm-Rollen (app-admin, event-admin, template-admin) werden angelegt.
  2. 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 & SecurityAutorisierungsmatrixBetrieb & Wartung

Technisches Design

docs/design.md

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:

backend/openapi.yaml

Konventionen

AGENTS.md