Files
signoz/pkg/web
Pandey ef298af388 feat(apiserver): derive HTTP route prefix from global.external_url (#10943)
* feat(apiserver): derive HTTP route prefix from global.external_url

The path component of global.external_url is now used as the base path
for all HTTP routes (API and web frontend), enabling SigNoz to be served
behind a reverse proxy at a sub-path (e.g. https://example.com/signoz/).

The prefix is applied via http.StripPrefix at the outermost handler
level, requiring zero changes to route registration code. Health
endpoints (/api/v1/health, /api/v2/healthz, /api/v2/readyz,
/api/v2/livez) remain accessible without the prefix for container
healthchecks.

Removes web.prefix config in favor of the unified global.external_url
approach, avoiding the desync bugs seen in projects with separate
API/UI prefix configs (ArgoCD, Prometheus).

closes SigNoz/platform-pod#1775

* feat(web): template index.html with dynamic base href from global.external_url

Read index.html at startup, parse as Go template with [[ ]] delimiters,
execute with BasePath derived from global.external_url, and cache the
rendered bytes in memory. This injects <base href="/signoz/" /> (or
whatever the route prefix is) so the browser resolves relative URLs
correctly when SigNoz is served at a sub-path.

Inject global.Config into the routerweb provider via the factory closure
pattern. Static files (JS, CSS, images) are still served from disk
unchanged.

* refactor(web): extract index.html templating into web.NewIndex

Move the template parsing and execution logic from routerweb provider
into pkg/web/template.go. NewIndex logs and returns raw bytes on
template failure; NewIndexE returns the error for callers that need it.

Rename BasePath to BaseHref to match the HTML attribute it populates.
Inject global.Config into routerweb via the factory closure pattern.

* refactor(global): rename RoutePrefix to ExternalPath, add ExternalPathTrailing

Rename RoutePrefix() to ExternalPath() to accurately reflect what it
returns: the path component of the external URL. Add
ExternalPathTrailing() which returns the path with a trailing slash,
used for HTML base href injection.

* refactor(web): make index filename configurable via web.index

Move the hardcoded indexFileName const from routerweb/provider.go to
web.Config.Index with default "index.html". This allows overriding the
SPA entrypoint file via configuration.

* refactor(web): collapse testdata_basepath into testdata

Use a single testdata directory with a templated index.html for all
routerweb tests. Remove the redundant testdata_basepath directory.

* test(web): add no-template and invalid-template index test cases

Add three distinct index fixtures in testdata:
- index.html: correct [[ ]] template with BaseHref
- index_no_template.html: plain HTML, no placeholders
- index_invalid_template.html: malformed template syntax

Tests verify: template substitution works, plain files pass through
unchanged, and invalid templates fall back to serving raw bytes.
Consolidate test helpers into startServer/get.

* refactor(web): rename test fixtures to no_template, valid_template, invalid_template

Drop the index_ prefix from test fixtures. Use web instead of w for
the variable name in test helpers.

* test(web): add SPA fallback paths to no_template and invalid_template tests

Test /, /does-not-exist, and /assets in all three template test cases
to verify SPA fallback behavior (non-existent paths and directories
serve the index) regardless of template type.

* test(web): use exact match instead of contains in template tests

Match the full expected response body in TestServeTemplatedIndex
instead of using assert.Contains.

* style(web): use raw string literals for expected test values

* refactor(web): rename get test helper to httpGet

* refactor(web): use table-driven tests with named path cases

Replace for-loop path iteration with explicit table-driven test cases
for each path. Each path (root, non-existent, directory) is a named
subtest case in all three template tests.

* chore: remove redundant comments from added code

* style: add blank lines between logical blocks

* fix(web): resolve lint errors in provider and template

Fix errcheck on rw.Write in serveIndex, use ErrorContext instead of
Error in NewIndex for sloglint compliance. Move serveIndex below
ServeHTTP to order public methods before private ones.

* style: formatting and test cleanup from review

Restructure Validate nil check, rename expectErr to fail with
early-return, trim trailing newlines in test assertions, remove
t.Parallel from subtests, inline short config literals, restore
struct field comments in web.Config.

* fix: remove unused files

* fix: remove unused files

* perf(web): cache http.FileServer on provider instead of creating per-request

* refactor(web): use html/template for context-aware escaping in index rendering

---------

Co-authored-by: SagarRajput-7 <162284829+SagarRajput-7@users.noreply.github.com>
2026-04-18 06:47:17 +00:00
..