# 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.