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

6.2 KiB

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.