thermograph/frontend/server/README.md
emi a4ecb51401
Some checks failed
secrets-guard / encrypted (push) Successful in 24s
shell-lint / shellcheck (push) Successful in 26s
Deploy frontend to LAN dev server / build (push) Successful in 2m11s
Build + push frontend image (Forgejo registry) / build-push (push) Successful in 2m20s
Build + push backend image (Forgejo registry) / build-push (push) Successful in 2m28s
Deploy backend to LAN dev server / build (push) Successful in 2m45s
PR build (required check) / changes (pull_request) Successful in 16s
secrets-guard / encrypted (pull_request) Successful in 16s
shell-lint / shellcheck (pull_request) Successful in 15s
PR build (required check) / build-frontend (pull_request) Has been skipped
PR build (required check) / validate-observability (pull_request) Has been skipped
Deploy frontend to LAN dev server / deploy (push) Successful in 39s
PR build (required check) / build-backend (pull_request) Successful in 1m21s
PR build (required check) / gate (pull_request) Successful in 3s
Deploy backend to LAN dev server / deploy (push) Failing after 2m31s
web/worker: add a process-level liveness heartbeat (#80)
2026-07-25 04:13:47 +00:00

2.8 KiB

thermograph-frontend (Go)

The SSR frontend service, ported from the Python implementation one directory up (app.py / content.py / api_client.py / format.py): server-rendered content pages, the interactive tool's SPA shells, and every static asset. It is I/O-bound glue over the backend's /content/* JSON API — no climate maths, no database, no auth.

Layout

go.mod                        module thermograph/frontend (Go 1.26)
main.go                       config + mux + static + graceful shutdown
internal/config/              every env var the service reads (same names,
                              defaults and required/optional split as the
                              Python — see config.go's field docs)
internal/contentapi/          backend /content/* client: TTL cache, bounded
                              LRU, per-key single-flight, origin forwarding;
                              typed payloads in types.go
internal/render/              html/template engine over an embed.FS +
                              response ETag helpers (W/"sha1[:20]",
                              If-None-Match handling)
internal/render/templates/    the page templates (embedded; *.tmpl)
internal/contentdata/         glossary.yaml / pages.yaml loader (fail-loud
                              validation, file order preserved)

Dependencies: stdlib plus gopkg.in/yaml.v3 — the committed SSR copy in frontend/content/*.yaml is shared with the rest of the repo and uses block/ folded scalars, so a YAML parser is genuinely required.

Run locally

cd frontend/server
go build -o thermograph-frontend .
cd ..            # static/ and content/ resolve relative to the working dir
THERMOGRAPH_API_BASE_INTERNAL=http://127.0.0.1:8137 \
THERMOGRAPH_BASE=/thermograph \
  ./server/thermograph-frontend

THERMOGRAPH_API_BASE_INTERNAL is required — the boot fails loudly without it, same as the Python raised at import. Other env vars (all optional): THERMOGRAPH_BASE (default /thermograph; the image sets /), THERMOGRAPH_API_VERSION (default v2 — bump only per the API-version pinning contract in frontend/CLAUDE.md), THERMOGRAPH_API_BASE_PUBLIC, THERMOGRAPH_SSR_CACHE_TTL (seconds, default 600), THERMOGRAPH_GOOGLE_VERIFY / THERMOGRAPH_BING_VERIFY, and PORT (default 8080).

The process expects static/ and content/ in its working directory (frontend/ locally, /app in the image). Templates are embedded in the binary; static assets and the YAML copy are read from disk.

Test / verify

cd frontend/server
go build ./... && go vet ./... && go test ./...

The deployed binary is /usr/local/bin/thermograph-frontend inside the emi/thermograph/frontend image; the image name, frontend-* CI workflows and deploy path are unchanged from the Python service.