Relay Server Frontend
React + TypeScript frontend for relay server discovery and onboarding.
Tech Stack
- React 19
- TypeScript
- Vite 7
- Tailwind CSS 4
- shadcn/ui (Radix-based)
- Lucide React
- @ssgoi/react
- React Compiler (
babel-plugin-react-compiler, enabled invite.config.ts)
Core Behavior
The Go relay is API-only. This frontend is a standalone Vite app that talks to the relay over the JSON API and does not receive server-side injected lease data.
- Public presentation state is loaded from
/ui/state. - Operator presentation policy state is loaded from
/ui/policy/state. - All JSON API responses use the
{ ok, data?, error? }envelope parsed bysrc/lib/apiClient.ts. VITE_PORTAL_API_BASE_URLpoints the frontend at the public edge origin or deployment base path, not at/api;/api,/ui,/sdk, and/discoveryare sibling paths. Admin auth uses a bearer token returned by/api/admin/auth/login.
Project Structure
frontend/
src/
components/
hooks/
useServerList.ts
useAdmin.ts
useList.ts
useAuth.ts
lib/
apiClient.ts
apiPaths.ts
metadata.ts
pages/
Admin.tsx
ServerDetail.tsx
ServerList.tsx
types/
api.ts
App.tsx
main.tsx
index.css
api/
server.ts
index.html
package.json
tsconfig.json
tsconfig.api.json
vite.config.ts
Install and Build
cd frontend
npm install
npm run build
Build output goes to frontend/dist/.
Development
cd frontend
npm run dev
Default dev URL: http://localhost:5173.
To run against another origin, build or run the frontend with the public edge origin:
VITE_PORTAL_API_BASE_URL=https://portal.example.com npm run dev
Docker
The frontend Docker image serves the built Vite app with nginx over HTTP. It
does not own API path routing; the public edge nginx routes relay-owned paths to
portal:4017 and /ui/* presentation paths to portal-api:8081.
TLS for public domains should live in the outer reverse proxy. The app uses
same-origin relative API paths, so it does not need runtime config file
generation.
docker compose up -d portal-frontend
NPM Scripts
| Script | Purpose |
|---|---|
npm run dev |
Start the Vite development server. |
npm run build |
Type-check and build production assets. |
npm run build:api |
Build the TypeScript API service. |
npm run lint |
Run ESLint. |
npm run typecheck |
Run TypeScript checking. |
npm test |
Run Vitest. |
npm run preview |
Preview the production bundle. |
Relay Integration
Relay server exposes:
/- relay API identity response/api/state- public leases/api/install.shand/api/install.ps1- CLI installers/api/admin/auth/*- admin token auth endpoints/api/policy/*- relay policy endpoints/sdk/*- SDK/control endpoints/discovery- relay discovery when enabled
The TypeScript API service composes frontend-owned presentation
state on top of relay data under the /ui/ prefix:
/ui/state- relay leases pluslanding_page_enabled/ui/policy/*- relay policy, withlanding_page_enabledcomposed into/ui/policyand/ui/policy/state/ui/service/status- hostname and service readiness derived from relay/api/state/ui/thumbnail/{hostname}- generated screenshots, disabled whenHEADLESS_SHELL_URLis empty
Notes
- Relay path constants are owned by Go (
types/paths.go) and mirrored insrc/lib/apiPaths.tsfor browser calls; presentation paths live under/ui/inapi/server.ts, the edge nginx config, andsrc/lib/apiPaths.ts. - Frontend API wire types live in
src/types/api.ts. - Radix Select values cannot be empty strings. Use stable values such as
"all"and"default".