jarvis-assist/docs/superpowers/specs/2026-09-12-web-frontend-des...

140 lines
6.2 KiB
Markdown

# JARVIS Web Frontend (Phase 3b) - Design
**Datum:** 2026-09-12
**Status:** Approved, bereit fuer Implementierungsplan
## Kontext
Phase 2 (Claude-Chat) und Phase 3a (Knowledge Base via pgvector) sind live auf
`https://jarvis.mbo-tech-it.de` (API) mit Traefik-Routing ueber den bereits
vorhandenen, geteilten Reverse-Proxy des VPS. `JARVIS_HANDOFF.md` listet
Phase 3 "Frontend" mit vier Punkten (React/Vue Web UI, Authentication UI,
Chat Interface, Dashboard) - das wurde bewusst aufgeteilt: eine
"Authentication UI" braucht ein Auth-System, das serverseitig nicht
existiert (kein Login/JWT-Flow). Diese Spec deckt Chat + Dashboard als eine
gemeinsame Web-App ab; ein vollwertiges Auth-System bleibt ein separates
Follow-up.
## Sicherheitsproblem, das diese Phase mitloest
`/api/v1/chat` hat aktuell keinerlei Zugriffsschutz und CORS steht auf `*`.
Eine oeffentlich erreichbare Chat-UI unter einer bekannten Domain waere ohne
Schutz von jedem nutzbar, der die Domain findet, und wuerde Claude-API-
Guthaben verbrauchen. Diese Spec fuehrt deshalb einen einfachen
Shared-Secret-Schutz ein (kein vollwertiges User-System - das ist bewusst
YAGNI fuer den aktuellen Umfang, siehe Out of Scope).
## Domain-Aufteilung
- `jarvis.mbo-tech-it.de` -> Web-Frontend (neu, dieser Service)
- `api.jarvis.mbo-tech-it.de` -> JARVIS-API (Umzug von der Hauptdomain)
Kein neuer DNS-Eintrag noetig: der Wildcard-Record `*.jarvis.mbo-tech-it.de`
(siehe Traefik-Integration in `JARVIS_HANDOFF.md`) deckt `api.jarvis...`
bereits ab.
## Zugriffsschutz: Shared Secret statt vollwertiges Auth-System
Wiederverwendung des bereits in `.env` vorhandenen `API_KEY_ADMIN` (kein
neues Secret noetig). Ablauf:
1. Neue FastAPI-Dependency `require_admin_key` prueft den Header
`X-Admin-Key` gegen `API_KEY_ADMIN`. Fehlt der Header oder stimmt der Wert
nicht -> `401 Unauthorized`.
2. Angewendet auf: `POST /api/v1/chat`, `GET /api/v1/conversations/{id}`,
`POST/GET /api/v1/documents`, `POST/GET /api/v1/tasks`,
`POST /api/v1/workflows/trigger`, `GET /api/v1/admin/stats`,
`GET /api/v1/admin/health/detailed`.
3. **Nicht** geschuetzt: `GET /health` (wird fuer einfaches Monitoring/
Uptime-Checks offen gehalten - liefert ohnehin keine sensiblen Daten).
4. CORS wird von `allow_origins=["*"]` auf
`allow_origins=["https://jarvis.mbo-tech-it.de"]` eingeschraenkt, da wir
an dieser Stelle ohnehin an der Zugriffskontrolle arbeiten.
Im Frontend: Login-Screen mit einem Passwort-Feld. Eingabe wird nicht
client-seitig validiert, sondern per Testaufruf gegen
`GET /api/v1/admin/stats` mit dem eingegebenen Wert als `X-Admin-Key`
geprueft. Bei `200` wird der Wert in `localStorage` (Key
`jarvis_admin_key`) gespeichert und die Hauptansicht angezeigt. Bei `401`
Fehlermeldung im Login-Screen. Jeder weitere API-Call haengt den
gespeicherten Wert als `X-Admin-Key`-Header an; ein `401` auf irgendeinem
Call loescht den `localStorage`-Eintrag und zeigt wieder den Login-Screen.
## Frontend-Architektur
React + Vite, kein zusaetzliches Routing (nur zwei Views, ein einfacher
State-Switch reicht - YAGNI). Struktur:
```
web/
src/
main.tsx # Einstiegspunkt
App.tsx # Login-Gate + Nav zwischen Chat/Dashboard
api.ts # fetch-Wrapper: haengt X-Admin-Key an, wirft
# bei 401 einen speziellen Fehler, den App.tsx
# abfaengt um zurueck zum Login zu wechseln
components/
Login.tsx # Passwort-Feld, Testaufruf, Fehleranzeige
Chat.tsx # Nachrichtenliste + Eingabefeld
Dashboard.tsx # Stats-Karten + Health-Tabelle
index.html
package.json
vite.config.ts
Dockerfile # Multi-Stage: node:20-slim build -> nginx:alpine serve
nginx.conf # SPA-Fallback (alle Routen -> index.html)
```
`VITE_API_URL` (Build-Zeit-Env-Var) zeigt auf
`https://api.jarvis.mbo-tech-it.de`.
### Chat (`Chat.tsx`)
- Lokaler State: `messages: {role, content}[]`, `conversationId: number | null`.
- Eingabefeld + Senden-Button (auch Enter-Taste). Waehrend eine Antwort
aussteht: Eingabe gesperrt, Ladeindikator.
- Sendet `POST /api/v1/chat` mit `{conversation_id: conversationId, message}`.
Antwort haengt User- und Assistant-Nachricht an `messages` an, setzt
`conversationId` aus der Response.
- Fehler (z.B. 503 wegen fehlendem Claude-Guthaben) werden als Systemzeile
im Chatverlauf angezeigt, nicht stillschweigend verschluckt.
### Dashboard (`Dashboard.tsx`)
- Beim Mount: `GET /api/v1/admin/stats` und
`GET /api/v1/admin/health/detailed` parallel laden.
- Stats als drei Zahlen-Karten (Conversations/Tasks/Documents).
- Health als Tabelle: Service-Name, Status (ok/error als farbiges Badge),
Response-Time. Manueller "Aktualisieren"-Button (kein Auto-Polling - fuer
den aktuellen Umfang unnoetig, YAGNI).
## Fehlerbehandlung
- Netzwerkfehler (API nicht erreichbar): Inline-Fehlermeldung statt
unbehandeltem Absturz, sowohl in Chat als auch Dashboard.
- 401 an beliebiger Stelle: zentral in `api.ts` behandelt (siehe oben),
nicht in jeder Komponente einzeln.
- Leere Eingabe im Chat: Senden-Button deaktiviert, kein Request.
## Testing
- Backend: Unit-Test fuer `require_admin_key` (gueltiger Key -> durchgelassen,
fehlender Key -> 401, falscher Key -> 401) mit FastAPI `TestClient`.
- Frontend: Unit-Test (Vitest) fuer `api.ts` - prueft, dass der
`X-Admin-Key`-Header aus `localStorage` gesetzt wird und dass eine
401-Antwort den `localStorage`-Eintrag entfernt.
- Manueller End-to-End-Test im Browser nach Deployment: Login mit falschem
Passwort (Fehler sichtbar), Login mit richtigem Passwort, eine
Chat-Nachricht senden und Antwort sehen, zum Dashboard wechseln und Stats/
Health sehen (passend zum bisherigen Muster in diesem Projekt - kein
automatisierter Browser-Test-Runner vorhanden).
## Out of Scope (bewusst nicht Teil dieser Phase)
- Vollwertiges User-/Auth-System (Login pro Benutzer, JWT, Rollen) - das ist
weiterhin offener Punkt in Phase 3 von `JARVIS_HANDOFF.md`, unabhaengig von
dieser Spec.
- Tasks/Documents-Verwaltung im Frontend (Erstellen/Anzeigen ueber die UI) -
nur Chat + Dashboard in dieser Phase.
- Auto-Refresh/Live-Updates im Dashboard.
- Mobile-optimiertes Layout ueber einfache Responsivitaet hinaus.