|
|
||
|---|---|---|
| .forgejo | ||
| docker-entrypoint.d | ||
| nginx | ||
| public | ||
| src | ||
| .dockerignore | ||
| .gitignore | ||
| .nvmrc | ||
| .prettierignore | ||
| .prettierrc.json | ||
| CLAUDE.md | ||
| commitlint.config.js | ||
| Dockerfile | ||
| eslint.config.js | ||
| index.html | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| vite.config.ts | ||
hello-web
Test frontend for hello-svc. Frontend service generated from nholtware/service-template-react: a Vite +
React 19 + React Router (data mode) SPA, served as static files by
unprivileged nginx.
Develop
npm install
npm run dev # http://localhost:5173, /api proxied to API_PROXY_TARGET (default http://localhost:3000)
npm test
npm run lint
npm run format
Runtime config
/config.js sets window.__APP_CONFIG__. In the container,
docker-entrypoint.d/10-config.sh writes it at start from env vars, so one
image runs in every environment. Read values through getConfig() in
src/lib/config.ts.
| Env var | Default | Meaning |
|---|---|---|
API_BASE_URL |
/api |
Backend base URL; /api is the same-host route |
APP_NAME |
app |
Display name |
UNLEASH_URL |
(empty) | Unleash base URL for the browser; empty = flags off |
UNLEASH_FRONTEND_TOKEN |
(empty) | Unleash frontend token (public by design) |
Everything in config.js is public. Never put secrets there.
Feature flags
Flags come from Unleash (UI: http://unleash.192.168.1.83.nip.io, environment development)
through its Frontend API, using the official @unleash/proxy-client-react SDK.
src/lib/flags.tsx wraps it: FlagsProvider is mounted once in main.tsx;
components call the wrappers, never the SDK directly:
import { useFlag, useVariant } from "../lib/flags";
const showBanner = useFlag("hello-banner");
- In the cluster the deployment sets
UNLEASH_URL=http://unleash.192.168.1.83.nip.ioandUNLEASH_FRONTEND_TOKENfrom Secretunleash-client-token(keyfrontendToken). - The SDK fetches on load and refreshes every 15 s; components re-render on change. A reload always picks up the current state.
- With
UNLEASH_URLunset (localnpm run dev, tests) every flag readsfalse, nothing is fetched. - Tests:
vi.mock("@unleash/proxy-client-react")and controluseFlag(seesrc/app.test.tsx). hello-banneron the home page is the platform demo flag; delete it when you don't need it.
Container
GIT_SHAbuild arg is baked in asimport.meta.env.VITE_GIT_SHA.- nginx listens on 8080:
/healthzreturns 200, unknown paths fall back toindex.html,/assets/*are cached as immutable, andindex.htmlandconfig.jsare not cached. - Access log: one JSON line per request in the platform logging contract
(
level,time,service=APP_NAME,version=GIT_SHA,req.id(echoesX-Request-Id),req.method,req.url,res.statusCode,responseTimein ms,msg: "request completed"), the same shape as the backend template. In Kibana (http://kibana.192.168.1.83.nip.io, data viewkube-*):service:"hello-web" and res.statusCode >= 400./healthzisn't logged. nginx's own startup/error lines stay plain text (fieldlog).
Deploy
Push to main: Forgejo CI runs lint/test/format, builds and pushes
192.168.1.230:3000/nholtware/hello-web:<sha>, and bumps the tag in the
gitops repo. ArgoCD then syncs it. Manifests come from gitops
templates/frontend/. The repo needs Actions secrets REGISTRY_USER,
REGISTRY_TOKEN, and GITOPS_TOKEN.