|
All checks were successful
secrets-guard / encrypted (pull_request) Successful in 20s
PR build (required check) / changes (pull_request) Successful in 22s
shell-lint / shellcheck (pull_request) Successful in 19s
PR build (required check) / validate-observability (pull_request) Has been skipped
PR build (required check) / build-frontend (pull_request) Successful in 2m27s
PR build (required check) / build-backend (pull_request) Successful in 2m51s
PR build (required check) / gate (pull_request) Successful in 3s
|
||
|---|---|---|
| .. | ||
| content | ||
| scripts | ||
| server | ||
| static | ||
| templates | ||
| tests | ||
| tools | ||
| .dockerignore | ||
| .gitignore | ||
| api_client.py | ||
| app.py | ||
| CLAUDE.md | ||
| content.py | ||
| content_loader.py | ||
| DESIGN.md | ||
| docker-compose.test.yml | ||
| Dockerfile | ||
| format.py | ||
| Makefile | ||
| paths.py | ||
| README.md | ||
| requirements-dev.txt | ||
| requirements.txt | ||
Thermograph frontend
The server-rendered content pages, the interactive-tool SPA shells, and every
static asset for Thermograph — grades recent local
weather against ~45 years of climate history. This domain holds no climate
data and does no compute; it renders pages and serves the JS/CSS/image assets
that call the backend's API from the browser. It builds its own image
(emi/thermograph/frontend) and deploys independently of the backend.
See CLAUDE.md for the agent-facing detail on the deploy contract
and the cross-service contracts, and DESIGN.md for the visual
system.
The service is Go
server/ is the live implementation. The Python files at this level (app.py,
content.py, api_client.py, format.py, content_loader.py, paths.py,
templates/*.html.j2) are the superseded original, kept as the reference
the port was made from — nothing deploys them. See server/README.md for the
port's own notes.
server/ the service (module thermograph/frontend, Go 1.26)
main.go config + mux + static + graceful shutdown
internal/config/ every env var it reads
internal/contentapi/ backend /content/* client: TTL cache, bounded LRU,
per-key single-flight, origin forwarding
internal/content/ SSR page handlers, template funcmap, SEO
internal/contentdata/ glossary.yaml / pages.yaml loader (fail-loud)
internal/format/ unit-aware °C/°F formatting + band names
internal/handlers/ SPA shells + static serving
internal/render/ html/template over embed.FS + ETag helpers
internal/render/templates/ the page templates (embedded, *.tmpl)
static/ every static asset: the interactive tool's JS
(app.js, calendar.js, day.js, score.js, compare.js,
account.js, cache.js, shared.js, units.js,
mappicker.js, …), the SPA-shell HTML pages,
style.css (all design tokens — see DESIGN.md),
icons/favicons, manifest.webmanifest, sw.js
content/ structured SSR copy: glossary.yaml, pages.yaml
tests/fixtures/ golden payloads — consumed by the GO tests as well
as the Python tiers
tests/{unit,integration}/ the Python tiers (local only; CI runs neither)
tools/shoot.py the screenshot sweep for visual verification
Dockerfile golang builder (gofmt + vet + test) → alpine runtime
Dependencies are stdlib plus gopkg.in/yaml.v3 — the committed SSR copy in
content/*.yaml uses block/folded scalars, so a real YAML parser is required.
Talking to the backend
All data comes from the backend's content API over HTTP — this process holds nothing. Two env vars control the topology:
THERMOGRAPH_API_BASE_INTERNAL(required) — where this process reaches the backend server-side, e.g.http://backend:8137in compose, orhttp://127.0.0.1:8137for a local backend. Boot fails loudly if unset: a missing backend URL should break the boot, not silently 500 on the first request.THERMOGRAPH_API_BASE_PUBLIC(optional) — the backend's browser-facing origin, used only when frontend and backend are served cross-origin. Left empty (the default), asset/API references stay relative and same-origin, which is today's real deployed topology.
THERMOGRAPH_BASE (default /thermograph) must be set identically on both
frontend and backend in every real deployment — it's how both processes agree on
the shared URL prefix (or the deployed clean-root /, which the Dockerfile
sets).
Same-origin cookie-auth caveat: the interactive tool's auth
(static/account.js) is an HttpOnly session cookie. It uses
credentials: "include" (not the fetch default) specifically so the cookie
still rides along if this script is ever loaded cross-origin — but the backend
must actually be configured to accept credentialed cross-origin requests (CORS +
SameSite) for that case to work. In the default same-origin deployment this is
a non-issue. Don't assume a fully decoupled, independently-hosted frontend "just
works" for logged-in features without checking that configuration first.
Build & run
Build in server/, run from here — static/ and content/ resolve relative to
the working directory, while templates are embedded in the binary:
cd server && go build -o thermograph-frontend .
cd ..
THERMOGRAPH_API_BASE_INTERNAL=http://127.0.0.1:8137 \
THERMOGRAPH_BASE=/thermograph \
./server/thermograph-frontend
Or build the image directly:
docker build -t thermograph-frontend .
docker run -p 8080:8080 -e THERMOGRAPH_API_BASE_INTERNAL=http://backend:8137 thermograph-frontend
The image healthchecks GET /healthz (liveness only — it does no I/O, so it
stays cheap; it does not prove the backend is reachable).
Test
cd server && go build ./... && go vet ./... && go test ./...
Dockerfile's builder stage runs gofmt -l + go vet + go test ./... before
compiling, so a failing Go test fails the image build — and that is the
whole frontend check in CI. There is no separate frontend test step in any
workflow.
The Python tiers are local-only:
make test-unit # hermetic: SSR rendering fed committed fixtures
make test-integration # pulls + runs a real backend image
make backend-up # just the backend container, for dev
make capture-fixtures # refresh tests/fixtures/*.json from a live backend
make backend-up derives the backend image tag from this checkout (the last
commit touching backend/ — the same domain-keyed rule build-push and deploy
use); pin one with THERMOGRAPH_BACKEND_TEST_TAG=sha-<12hex>.
More detail
CLAUDE.md— agent-facing instructions: build/verify, layout, API-version pinning, and the/cell/ETag,pctOrd()and Fahrenheit-set contracts.server/README.md— the Go port's own notes.DESIGN.md— the visual system (tokens, components, breakpoints) andtools/shoot.pyvisual verification.docs/onboarding/— the developer onboarding path for the whole monorepo.