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:
- Neue FastAPI-Dependency
require_admin_keyprueft den HeaderX-Admin-KeygegenAPI_KEY_ADMIN. Fehlt der Header oder stimmt der Wert nicht ->401 Unauthorized. - 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. - Nicht geschuetzt:
GET /health(wird fuer einfaches Monitoring/ Uptime-Checks offen gehalten - liefert ohnehin keine sensiblen Daten). - CORS wird von
allow_origins=["*"]aufallow_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/chatmit{conversation_id: conversationId, message}. Antwort haengt User- und Assistant-Nachricht anmessagesan, setztconversationIdaus 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/statsundGET /api/v1/admin/health/detailedparallel 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.tsbehandelt (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 FastAPITestClient. - Frontend: Unit-Test (Vitest) fuer
api.ts- prueft, dass derX-Admin-Key-Header auslocalStoragegesetzt wird und dass eine 401-Antwort denlocalStorage-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.