Frontend Neustart — Design Spec
Kontext
Abschnitt betitelt „Kontext“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.
Entscheidungen
Abschnitt betitelt „Entscheidungen“| Entscheidung | Begruendung |
|---|---|
| TanStack Start (SSR) | Konsistenz mit geplantem Stack, SSR fuer schnelles erstes Laden |
| Nitro Nightly | TanStack 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 4 | Mantine fuer Komponenten, Tailwind fuer Utilities |
| Zod 4 als Schema Source of Truth | Validierung + TypeScript Types aus einem Schema |
| i18next nur DE | Stufe 1 braucht keine weiteren Sprachen |
| Kein State Management | TanStack Query reicht fuer Server State |
Architektur
Abschnitt betitelt „Architektur“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).
Projekt-Struktur
Abschnitt betitelt „Projekt-Struktur“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)Auth-Flow
Abschnitt betitelt „Auth-Flow“- User oeffnet
/→_app.tsxbeforeLoadruft Session-Check Server Function auf - Keine gueltige Session → Redirect zu
/login /login→LoginFormsendet Email + Passwort anlogin()Server Function- Server Function ruft
POST /api/public/auth/loginam Go-Backend auf - Wenn
two_fa_required: true→ Redirect zu/verify-2fa?attempt={attempt_id} - Wenn direkt
session_id→ Cookie vom Backend-Response an Browser weiterreichen → Redirect zu/ /verify-2fa→TwoFactorFormsendet Code + attempt_id anverify2fa()Server Function- Server Function ruft
POST /api/public/auth/verify-2faam Go-Backend auf - Erfolg → Cookie weiterreichen → Redirect zu
/ - Dashboard zeigt “Willkommen, {display_name}“
Cookie-Handling (BFF)
Abschnitt betitelt „Cookie-Handling (BFF)“Die Server Functions muessen Cookies zwischen Browser und Go-Backend forwarden:
- Login/2FA Response:
Set-CookieHeader vom Go-Backend extrahieren und an den Browser weiterreichen - Geschuetzte Calls:
CookieHeader vom Browser-Request an das Go-Backend forwarden - Logout:
Set-CookiemitMaxAge=0vom Backend an Browser weiterreichen
Backend-Konfiguration
Abschnitt betitelt „Backend-Konfiguration“- Env-Variable:
BACKEND_URL(nur server-seitig) - Dev:
http://localhost:8080 - Produktion:
http://backend:8080(Docker-interner Hostname)
Backend API (Go)
Abschnitt betitelt „Backend API (Go)“POST /api/public/auth/login
Abschnitt betitelt „POST /api/public/auth/login“Request: { "email": "string", "password": "string" }
Response: { "session_id?": "UUID", "attempt_id?": "UUID", "two_fa_required": "boolean" }
Errors: 400, 401, 403, 429, 500
POST /api/public/auth/verify-2fa
Abschnitt betitelt „POST /api/public/auth/verify-2fa“Request: { "attempt_id": "UUID", "code": "string (6 digits)" }
Response: { "session_id": "UUID" }
Errors: 400, 401, 500
GET /api/public/auth/session
Abschnitt betitelt „GET /api/public/auth/session“Cookie: session=<UUID>
Response: { "account_id": "UUID", "email": "string", "display_name": "string", "role": "string", "tenant_id?": "UUID" }
Errors: 401
POST /api/protected/auth/logout
Abschnitt betitelt „POST /api/protected/auth/logout“Cookie: session=<UUID>
Response: { "status": "logged out" }
Errors: 401, 500
Session Cookie (vom Go-Backend gesetzt)
Abschnitt betitelt „Session Cookie (vom Go-Backend gesetzt)“- Name:
session - HttpOnly: true
- Secure: true
- SameSite: Lax
- MaxAge: 86400 (24h)
- Path:
/
- Primary Color: Teal/Cyan Palette (
#20c997Basis) - 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
deals einzige Sprache - Namespaces:
common,auth,dashboard - Alle sichtbaren Texte ueber Translation Keys
Scripts
Abschnitt betitelt „Scripts“| Script | Befehl | Zweck |
|---|---|---|
| dev | vite dev --port 3000 | Entwicklungsserver |
| build | vite build | Produktions-Build |
| preview | vite preview | Preview des Builds |
| test | vitest run | Tests |
| lint | eslint src | Lint |
| lint:fix | eslint src --fix | Lint mit Auto-Fix |
| format | prettier --write src | Formatierung |
| format:check | prettier --check src | Formatierung pruefen |
Nicht enthalten (bewusst)
Abschnitt betitelt „Nicht enthalten (bewusst)“- 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)