smart-interview-ui
Purpose
smart-interview-ui is the live interview session room — the screen where recruiters and candidates actually meet on video (the LiveKit path). It is a plain React single-page app (Create React App, JavaScript — not Next.js) that is embedded by the main interviews-ui app inside an iframe; it is not a standalone destination users navigate to directly.
Architecture
The app is a CRA 5 / React 18 SPA with no server-side rendering. Entry flows src/index.js → src/App.js → src/pages/AIInterviewAgent.jsx, and the room itself lives under src/livekit/. It supports two video flows: a legacy WebRTC + Socket.io room, and the current LiveKit-based room.
At runtime it talks to four things:
- LiveKit SFU (WebSocket) — audio/video via
livekit-clientand@livekit/components-react, connecting to<LIVEKIT_WS_URL>.src/livekit/endpoints.jsresolves the URL and forceswss://when the page is served over HTTPS. - video-streaming-server (VSS) — the Node.js backend that mints LiveKit access tokens (never this app) and handles room operations:
POST .../livekit/token,record/start,record/stop, plus bucket listing and a LiveKit webhook endpoint. Base URL comes from<TOKEN_API_URL>; calls go throughsrc/livekit/tokenService.js. - Socket.io / WebRTC signalling —
src/hooks/useSocket.jsanduseWebRTC.jsfor the legacy realtime flow. - bot-backend observer — optional telemetry to
<OBSERVER_API_URL>, gated by a flag (local-only).
The parent app (interviews-ui) passes a sessionId via query params or an origin-validated postMessage (allowed origins listed in <PARENT_ORIGINS>). The session room forwards that as an X-Session-Id header on VSS calls (src/livekit/correlation.js). The Java backend is the authority for sessionId.
App routes
| Route | Purpose |
|---|---|
/ | Legacy join/home screen — enter room ID and nickname (also accepts ?room=/?roomId=, ?name=/?nickname=/?userId=); then shows the WebRTC/Socket.io room. |
/room | Redirects to /livekit. |
/livekit | LiveKit join page — enter room name and identity. |
/livekit/room | LiveKit in-call room: video grid, controls, screen share, recording, chat. |
Key components
| Path | Owns |
|---|---|
src/index.js, src/App.js | App entry and top-level routing. |
src/pages/AIInterviewAgent.jsx | Main page component reached from the entry chain. |
src/livekit/ | The LiveKit room: endpoints.js (env/URL resolution — all new config goes here, not ad-hoc process.env reads), tokenService.js (VSS token + room API calls), correlation.js (attaches X-Session-Id to VSS calls; fail-open on missing/malformed values). |
src/hooks/useSocket.js, src/hooks/useWebRTC.js | Socket.io / WebRTC signalling for the legacy flow. |
Feature surface: multi-user video rooms, mute/unmute, camera on/off, screen sharing, real-time chat, user status indicators, and LiveKit recording with token-based auth.
Local development
Prerequisites: Node.js + npm, and a running video-streaming-server instance so the /livekit/token and /livekit/record/* endpoints are available.
npm install
npm start # CRA dev server (FAST_REFRESH off, host-check disabled for iframe embed)
npm run build # production build → build/
CI=true npm test # run the jest suite once and exit
Run a single test file with:
CI=true npm test -- src/livekit/correlation.test.js
There is no standalone lint command — react-scripts runs the built-in react-app ESLint config during start/build.
Local dev config may live in .env.local (gitignored). The source of truth for deployed environments is managed in the hosting platform's per-branch environment variables (set by the platform CLI), not in the repo.
Environment variables
All env reads use the CRA REACT_APP_* convention and resolve through src/livekit/endpoints.js / config.js. Every value is baked into the public client bundle at build time — treat them all as public config; real secrets stay server-side (VSS / user-management).
| Variable | Purpose |
|---|---|
REACT_APP_TOKEN_API_URL | Base URL for the VSS LiveKit API (token, record start/stop). No trailing slash. e.g. <TOKEN_API_URL> |
REACT_APP_LIVEKIT_WS_URL | LiveKit WebSocket (SFU) endpoint, e.g. <LIVEKIT_WS_URL> |
REACT_APP_PARENT_ORIGINS | Allowed embedding origins for the postMessage sessionId handshake, e.g. <PARENT_ORIGINS> |
REACT_APP_OBSERVER_API_URL | bot-backend observer telemetry endpoint (local-only), e.g. <OBSERVER_API_URL> |
REACT_APP_OBSERVER_ENABLED | Flag gating the observer telemetry. |
Gotchas
npm testis watch mode by default (react-scripts/jest). Always useCI=true npm testto run once and exit — never leave the watcher running.- Don't rely on the WS URL fallback.
endpoints.jscontains hardcoded fallbacks for the LiveKit WebSocket URL (a stale raw address and an HTTPS host guess). Always setREACT_APP_LIVEKIT_WS_URLexplicitly per environment. - It's an iframe app.
npm startdeliberately disables the dev-server host check and FAST_REFRESH so the parent app can embed it. IfREACT_APP_PARENT_ORIGINSdoesn't list the embedding origin, thepostMessagesessionIdis silently dropped. - No secrets, ever. Every
REACT_APP_*value ships in the public JS bundle. Never put a real secret in one; LiveKit tokens are minted server-side in VSS, never in this app. - JavaScript only. There is no
tsconfig; the loneMeetingLeftView.d.tsis a stray ambient type — don't infer a TypeScript migration from it. - CRA 5 is in maintenance. Don't
eject, and don't introduce a new bundler or framework without approval.