Documentation
¶
Overview ¶
Package frontend embeds the Astro static build and exposes it as an http.Handler with SPA fallback. The dist directory is populated by `make frontend` (see Makefile) which copies web/dist into here.
A .keep file ships with the repository so the //go:embed directive never fails on a fresh clone, but the served content is only useful after the frontend has been built.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrIndexMissing = errors.New("frontend: index.html missing — run `make frontend`")
ErrIndexMissing is returned by Handler when the dist directory does not contain an index.html — typically because `make frontend` has not run yet on this checkout.
Functions ¶
func Handler ¶
Handler returns an http.Handler that serves the embedded frontend with SPA fallback: any URL that does not correspond to an existing file is answered with the contents of index.html so the client-side router (Astro + Preact) can take over (e.g. /r/<scan-id>).
The fallback writes the index bytes directly rather than rewriting the request and re-entering http.FileServer, which would trigger Go's built-in `/index.html → ./` redirect and loop.
When the embedded dist does not contain index.html, Handler returns ErrIndexMissing so callers can choose to disable the frontend route gracefully rather than panic.
headInject, when non-empty, is spliced once into every shell HTML just before </head>. Used to opt the public deployment into analytics without touching self-hosted builds. The snippet is trusted operator config — not escaped. If </head> is absent (or the snippet is empty) the bytes are served verbatim.
staticOverlayDir, when non-empty, is a directory whose tree mirrors URL paths. Anything under .well-known/ is served from the overlay when the file exists; at the root only a closed whitelist (robots.txt, humans.txt, ads.txt, sitemap.xml) is honoured so a misconfigured overlay cannot accidentally hijack index.html or the report shell. Paths absent from the overlay fall back to the embedded fs.
Types ¶
This section is empty.