Skip to main content

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.jssrc/App.jssrc/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-client and @livekit/components-react, connecting to <LIVEKIT_WS_URL>. src/livekit/endpoints.js resolves the URL and forces wss:// 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 through src/livekit/tokenService.js.
  • Socket.io / WebRTC signallingsrc/hooks/useSocket.js and useWebRTC.js for 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

RoutePurpose
/Legacy join/home screen — enter room ID and nickname (also accepts ?room=/?roomId=, ?name=/?nickname=/?userId=); then shows the WebRTC/Socket.io room.
/roomRedirects to /livekit.
/livekitLiveKit join page — enter room name and identity.
/livekit/roomLiveKit in-call room: video grid, controls, screen share, recording, chat.

Key components

PathOwns
src/index.js, src/App.jsApp entry and top-level routing.
src/pages/AIInterviewAgent.jsxMain 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.jsSocket.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).

VariablePurpose
REACT_APP_TOKEN_API_URLBase URL for the VSS LiveKit API (token, record start/stop). No trailing slash. e.g. <TOKEN_API_URL>
REACT_APP_LIVEKIT_WS_URLLiveKit WebSocket (SFU) endpoint, e.g. <LIVEKIT_WS_URL>
REACT_APP_PARENT_ORIGINSAllowed embedding origins for the postMessage sessionId handshake, e.g. <PARENT_ORIGINS>
REACT_APP_OBSERVER_API_URLbot-backend observer telemetry endpoint (local-only), e.g. <OBSERVER_API_URL>
REACT_APP_OBSERVER_ENABLEDFlag gating the observer telemetry.

Gotchas

  • npm test is watch mode by default (react-scripts/jest). Always use CI=true npm test to run once and exit — never leave the watcher running.
  • Don't rely on the WS URL fallback. endpoints.js contains hardcoded fallbacks for the LiveKit WebSocket URL (a stale raw address and an HTTPS host guess). Always set REACT_APP_LIVEKIT_WS_URL explicitly per environment.
  • It's an iframe app. npm start deliberately disables the dev-server host check and FAST_REFRESH so the parent app can embed it. If REACT_APP_PARENT_ORIGINS doesn't list the embedding origin, the postMessage sessionId is 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 lone MeetingLeftView.d.ts is 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.