mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2026-04-24 06:05:19 +00:00
Reflector UI (v2)
Vite + React 19 + TypeScript SPA, served at /v2 behind Caddy. Lives alongside the existing Next.js app in ../www while the migration is in progress.
Stack
- Vite + React 19 + TypeScript
- Tailwind v4 with Greyhaven design tokens (
src/styles/greyhaven.css) - React Router v7, routes mounted under
/v2/* - TanStack Query + openapi-fetch with types generated from the backend OpenAPI spec
- nuqs for URL-backed page/search state on
/browse - react-oidc-context (OIDC Authorization Code + PKCE) for the JWT auth backend
- Password-form fallback for the
passwordauth backend (POST /v1/auth/login)
Local development
cd ui
pnpm install
# Point the dev server at your local backend (defaults to http://localhost:1250).
cp .env.example .env
# Edit VITE_OIDC_AUTHORITY / VITE_OIDC_CLIENT_ID if your backend runs in JWT mode.
pnpm dev # http://localhost:3001/v2/
pnpm build # production bundle in dist/
pnpm typecheck # tsc --noEmit
pnpm openapi # regenerate src/api/schema.d.ts from the running backend
pnpm openapi hits http://127.0.0.1:1250/openapi.json — start the backend first (cd ../server && uv run -m reflector.app --reload).
Auth modes
The SPA auto-detects the backend's auth backend:
- JWT (OIDC/SSO via Authentik): set
VITE_OIDC_AUTHORITYandVITE_OIDC_CLIENT_ID. The app does the Authorization Code + PKCE flow; Authentik hosts the login page. Register a Public OAuth client whose redirect URI ishttps://<your-domain>/v2/auth/callback. No client secret is baked into the bundle. - Password: leave the OIDC env vars blank. The app shows an in-page email/password form that posts to
/v1/auth/loginand stores the returned JWT insessionStorage. - None: backend returns a fake user for every request; the SPA treats that as authenticated.
Deployment (selfhosted)
docker-compose.selfhosted.yml defines a ui service that builds this directory and serves the static bundle from nginx on port 80. Caddy routes /v2/* to ui:80 and leaves the root path on the existing web service.
Pass OIDC config as build args (Vite inlines VITE_* at build time):
VITE_OIDC_AUTHORITY=https://auth.example/application/o/reflector/ \
VITE_OIDC_CLIENT_ID=reflector-ui \
docker compose -f docker-compose.selfhosted.yml build ui
docker compose -f docker-compose.selfhosted.yml up -d ui
Pages shipped in this pass
/— Home / Create new transcript (single-form shipping variant)/browse— transcript list with FTS search, source/room/trash filters, pagination/rooms— rooms list, create, edit, delete/welcome— logged-out landing (OIDC mode)/login— password login form (password mode)/auth/callback— OIDC redirect target
Not yet ported:
- Transcript detail / playback
- Meeting / live join
- Settings, API keys
- Tags sidebar (backend model doesn't exist yet)
- Progress streaming over WebSocket
Directory map
src/
api/ fetch client, generated OpenAPI types
auth/ AuthProvider, RequireAuth, OIDC config
components/
browse/ TranscriptRow, FilterBar, Pagination
home/ LanguagePair, RoomPicker
icons.tsx lucide-react wrapper (compat with prototype I.* shape)
layout/ AppShell, AppSidebar, TopBar
rooms/ RoomsTable, RoomFormDialog, DeleteRoomDialog
ui/ primitives (Button, StatusDot, StatusBadge, SidebarItem, …)
hooks/ useRooms, useTranscripts
lib/ utils, format helpers, types
pages/ HomePage, BrowsePage, RoomsPage, LoggedOut, LoginForm, AuthCallback
styles/ greyhaven.css, reflector-forms.css, index.css (Tailwind entry)