Zum Inhalt springen

Frontend Neustart — Design Spec

Das CMS-Frontend wird komplett neu aufgebaut. Das alte Frontend (68 Dateien) wurde gegen ein Java-Backend entwickelt, das durch ein Go-Backend ersetzt wurde. Nur das Auth-Feature (Login, 2FA, Session, Logout) steht im Backend. Das Frontend startet mit dem Minimum-Set: Login, 2FA-Code-Eingabe, Dashboard-Shell.

EntscheidungBegruendung
TanStack Start (SSR)Konsistenz mit geplantem Stack, SSR fuer schnelles erstes Laden
Nitro NightlyTanStack Start 1.x benoetigt Nitro v3 Features
BFF-Pattern (Server Functions)Go-Backend ist intern (Docker-Netzwerk), nicht public. Browser spricht nur mit TanStack Start.
Mantine 8 + Tailwind 4Mantine fuer Komponenten, Tailwind fuer Utilities
Zod 4 als Schema Source of TruthValidierung + TypeScript Types aus einem Schema
i18next nur DEStufe 1 braucht keine weiteren Sprachen
Kein State ManagementTanStack Query reicht fuer Server State
Browser --> TanStack Start (public via Cloudflare Tunnel) --> Go Backend (internes Docker-Netzwerk)

Der Browser spricht nie direkt mit dem Go-Backend. Alle API-Calls laufen ueber TanStack Start Server Functions (BFF-Pattern).

cms/frontend/
├── app.config.ts # TanStack Start App Config
├── eslint.config.js # ESLint 9 Flat Config
├── package.json # Dependencies
├── prettier.config.js # Prettier Config
├── tsconfig.json # TypeScript strict, path alias @/*
├── vite.config.ts # TanStack Start + Tailwind + DevTools
├── .nvmrc # Node 22
└── src/
├── api/ # Server Functions (BFF-Schicht)
│ └── auth.ts # login(), verify2fa(), session(), logout()
├── components/
│ ├── auth/
│ │ ├── LoginForm.tsx # Email + Passwort Form
│ │ └── TwoFactorForm.tsx # 6-stelliger Code Form
│ └── layout/
│ ├── AppShell.tsx # Mantine AppShell (Sidebar + Header)
│ └── GuestLayout.tsx # Zentriertes Layout fuer Login/2FA
├── hooks/
│ └── useSession.ts # Session Query Hook
├── i18n/
│ ├── index.ts # i18next Setup
│ └── de.ts # Deutsche Texte
├── lib/
│ ├── server/
│ │ └── backend.ts # fetch-Wrapper zum Go-Backend (nur Server)
│ └── schemas.ts # Zod Schemas (Auth Request/Response)
├── routes/
│ ├── __root.tsx # MantineProvider, QueryClient, DevTools
│ ├── _auth.tsx # Layout Route: GuestLayout
│ ├── _auth.login.tsx # Login Page
│ ├── _auth.verify-2fa.tsx # 2FA Code Eingabe
│ ├── _app.tsx # Layout Route: AppShell (Auth-geschuetzt)
│ └── _app.index.tsx # Dashboard (Platzhalter)
├── router.tsx # Router + QueryClient Setup
├── styles.css # Tailwind 4 + Mantine Overrides
└── theme.ts # Mantine Theme (Brand-Palette)
  1. User oeffnet /_app.tsx beforeLoad ruft Session-Check Server Function auf
  2. Keine gueltige Session → Redirect zu /login
  3. /loginLoginForm sendet Email + Passwort an login() Server Function
  4. Server Function ruft POST /api/public/auth/login am Go-Backend auf
  5. Wenn two_fa_required: true → Redirect zu /verify-2fa?attempt={attempt_id}
  6. Wenn direkt session_id → Cookie vom Backend-Response an Browser weiterreichen → Redirect zu /
  7. /verify-2faTwoFactorForm sendet Code + attempt_id an verify2fa() Server Function
  8. Server Function ruft POST /api/public/auth/verify-2fa am Go-Backend auf
  9. Erfolg → Cookie weiterreichen → Redirect zu /
  10. Dashboard zeigt “Willkommen, {display_name}“

Die Server Functions muessen Cookies zwischen Browser und Go-Backend forwarden:

  • Login/2FA Response: Set-Cookie Header vom Go-Backend extrahieren und an den Browser weiterreichen
  • Geschuetzte Calls: Cookie Header vom Browser-Request an das Go-Backend forwarden
  • Logout: Set-Cookie mit MaxAge=0 vom Backend an Browser weiterreichen
  • Env-Variable: BACKEND_URL (nur server-seitig)
  • Dev: http://localhost:8080
  • Produktion: http://backend:8080 (Docker-interner Hostname)

Request: { "email": "string", "password": "string" } Response: { "session_id?": "UUID", "attempt_id?": "UUID", "two_fa_required": "boolean" } Errors: 400, 401, 403, 429, 500

Request: { "attempt_id": "UUID", "code": "string (6 digits)" } Response: { "session_id": "UUID" } Errors: 400, 401, 500

Cookie: session=<UUID> Response: { "account_id": "UUID", "email": "string", "display_name": "string", "role": "string", "tenant_id?": "UUID" } Errors: 401

Cookie: session=<UUID> Response: { "status": "logged out" } Errors: 401, 500

  • Name: session
  • HttpOnly: true
  • Secure: true
  • SameSite: Lax
  • MaxAge: 86400 (24h)
  • Path: /
  • Primary Color: Teal/Cyan Palette (#20c997 Basis)
  • Font: Plus Jakarta Sans (geometric, professional)
  • Border Radius: xs=4, sm=6, md=8, lg=12, xl=16
  • Component Defaults: size=sm, Button fontWeight=600
  • i18next mit de als einzige Sprache
  • Namespaces: common, auth, dashboard
  • Alle sichtbaren Texte ueber Translation Keys
ScriptBefehlZweck
devvite dev --port 3000Entwicklungsserver
buildvite buildProduktions-Build
previewvite previewPreview des Builds
testvitest runTests
linteslint srcLint
lint:fixeslint src --fixLint mit Auto-Fix
formatprettier --write srcFormatierung
format:checkprettier --check srcFormatierung pruefen
  • Kein Zustand/State Management (TanStack Query reicht)
  • Keine Feature-Routes ausser Dashboard-Platzhalter
  • Keine komplexen Layouts (nur GuestLayout + AppShell)
  • Kein Vite Proxy (Server Functions uebernehmen Backend-Kommunikation)
  • Keine Tests im ersten Schritt (Struktur steht, Tests folgen mit Features)