| 1 | # Relay Server Frontend |
| 2 | |
| 3 | React + TypeScript frontend for relay server discovery and onboarding. |
| 4 | |
| 5 | ## Tech Stack |
| 6 | |
| 7 | - React 19 |
| 8 | - TypeScript |
| 9 | - Vite 7 |
| 10 | - Tailwind CSS 4 |
| 11 | - shadcn/ui (Radix-based) |
| 12 | - Lucide React |
| 13 | - @ssgoi/react |
| 14 | - React Compiler (`babel-plugin-react-compiler`, enabled in `vite.config.ts`) |
| 15 | |
| 16 | ## Core Behavior |
| 17 | |
| 18 | The Go relay is API-only. This frontend is a standalone Vite app that talks to |
| 19 | the relay over the JSON API and does not receive server-side injected lease |
| 20 | data. |
| 21 | |
| 22 | - Public presentation state is loaded from `/ui/state`. |
| 23 | - Operator presentation policy state is loaded from `/ui/policy/state`. |
| 24 | - All JSON API responses use the `{ ok, data?, error? }` envelope parsed by `src/lib/apiClient.ts`. |
| 25 | - `VITE_PORTAL_API_BASE_URL` points the frontend at the public edge origin or deployment base path, not at `/api`; `/api`, `/ui`, `/sdk`, and `/discovery` are sibling paths. Admin auth uses a bearer token returned by `/api/admin/auth/login`. |
| 26 | |
| 27 | ## Project Structure |
| 28 | |
| 29 | ```text |
| 30 | frontend/ |
| 31 | src/ |
| 32 | components/ |
| 33 | hooks/ |
| 34 | useServerList.ts |
| 35 | useAdmin.ts |
| 36 | useList.ts |
| 37 | useAuth.ts |
| 38 | lib/ |
| 39 | apiClient.ts |
| 40 | apiPaths.ts |
| 41 | metadata.ts |
| 42 | pages/ |
| 43 | Admin.tsx |
| 44 | ServerDetail.tsx |
| 45 | ServerList.tsx |
| 46 | types/ |
| 47 | api.ts |
| 48 | App.tsx |
| 49 | main.tsx |
| 50 | index.css |
| 51 | api/ |
| 52 | server.ts |
| 53 | index.html |
| 54 | package.json |
| 55 | tsconfig.json |
| 56 | tsconfig.api.json |
| 57 | vite.config.ts |
| 58 | ``` |
| 59 | |
| 60 | ## Install and Build |
| 61 | |
| 62 | ```bash |
| 63 | cd frontend |
| 64 | npm install |
| 65 | npm run build |
| 66 | ``` |
| 67 | |
| 68 | Build output goes to `frontend/dist/`. |
| 69 | |
| 70 | ## Development |
| 71 | |
| 72 | ```bash |
| 73 | cd frontend |
| 74 | npm run dev |
| 75 | ``` |
| 76 | |
| 77 | Default dev URL: `http://localhost:5173`. |
| 78 | |
| 79 | To run against another origin, build or run the frontend with the public edge origin: |
| 80 | |
| 81 | ```bash |
| 82 | VITE_PORTAL_API_BASE_URL=https://portal.example.com npm run dev |
| 83 | ``` |
| 84 | |
| 85 | ## Docker |
| 86 | |
| 87 | The frontend Docker image serves the built Vite app with nginx over HTTP. It |
| 88 | does not own API path routing; the public edge nginx routes relay-owned paths to |
| 89 | `portal:4017` and `/ui/*` presentation paths to `portal-api:8081`. |
| 90 | TLS for public domains should live in the outer reverse proxy. The app uses |
| 91 | same-origin relative API paths, so it does not need runtime config file |
| 92 | generation. |
| 93 | |
| 94 | ```bash |
| 95 | docker compose up -d portal-frontend |
| 96 | ``` |
| 97 | |
| 98 | ## NPM Scripts |
| 99 | |
| 100 | | Script | Purpose | |
| 101 | | --- | --- | |
| 102 | | `npm run dev` | Start the Vite development server. | |
| 103 | | `npm run build` | Type-check and build production assets. | |
| 104 | | `npm run build:api` | Build the TypeScript API service. | |
| 105 | | `npm run lint` | Run ESLint. | |
| 106 | | `npm run typecheck` | Run TypeScript checking. | |
| 107 | | `npm test` | Run Vitest. | |
| 108 | | `npm run preview` | Preview the production bundle. | |
| 109 | |
| 110 | ## Relay Integration |
| 111 | |
| 112 | Relay server exposes: |
| 113 | |
| 114 | - `/` - relay API identity response |
| 115 | - `/api/state` - public leases |
| 116 | - `/api/install.sh` and `/api/install.ps1` - CLI installers |
| 117 | - `/api/admin/auth/*` - admin token auth endpoints |
| 118 | - `/api/policy/*` - relay policy endpoints |
| 119 | - `/sdk/*` - SDK/control endpoints |
| 120 | - `/discovery` - relay discovery when enabled |
| 121 | |
| 122 | The TypeScript API service composes frontend-owned presentation |
| 123 | state on top of relay data under the `/ui/` prefix: |
| 124 | |
| 125 | - `/ui/state` - relay leases plus `landing_page_enabled` |
| 126 | - `/ui/policy/*` - relay policy, with `landing_page_enabled` composed into `/ui/policy` and `/ui/policy/state` |
| 127 | - `/ui/service/status` - hostname and service readiness derived from relay `/api/state` |
| 128 | - `/ui/thumbnail/{hostname}` - generated screenshots, disabled when `HEADLESS_SHELL_URL` is empty |
| 129 | |
| 130 | ## Notes |
| 131 | |
| 132 | - Relay path constants are owned by Go (`types/paths.go`) and mirrored in `src/lib/apiPaths.ts` for browser calls; presentation paths live under `/ui/` in `api/server.ts`, the edge nginx config, and `src/lib/apiPaths.ts`. |
| 133 | - Frontend API wire types live in `src/types/api.ts`. |
| 134 | - Radix Select values cannot be empty strings. Use stable values such as `"all"` and `"default"`. |