From c59fa464512e2361a837a84b62b81e0f98f1ab5d Mon Sep 17 00:00:00 2001 From: davion-knight <298846663+davion-knight@users.noreply.github.com> Date: Sun, 12 Jul 2026 11:39:17 -0500 Subject: [PATCH] feat(miner-ui): add an optional ORB/Grafana footer link to the root layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Render a footer link to an operator-configured ORB/Grafana dashboard (#5194), shown only when VITE_MINER_UI_GRAFANA_URL is set and non-empty (nothing rendered otherwise). Vite exposes only VITE_-prefixed vars to the client bundle, so the name is VITE_-prefixed — a bare name would be undefined at build. Opens in a new tab (target=_blank rel=noopener noreferrer); the URL is rendered as a plain href (React escapes it, no dangerouslySetInnerHTML) and no token/credential is ever appended. Extracted as a small GrafanaFooterLink component for direct testing. Closes #5194 --- apps/gittensory-miner-ui/README.md | 6 ++++ .../src/components/grafana-footer-link.tsx | 19 +++++++++++ .../src/grafana-footer-link.test.tsx | 32 +++++++++++++++++++ .../gittensory-miner-ui/src/routes/__root.tsx | 4 +++ 4 files changed, 61 insertions(+) create mode 100644 apps/gittensory-miner-ui/src/components/grafana-footer-link.tsx create mode 100644 apps/gittensory-miner-ui/src/grafana-footer-link.test.tsx diff --git a/apps/gittensory-miner-ui/README.md b/apps/gittensory-miner-ui/README.md index a0ce711092..893c586246 100644 --- a/apps/gittensory-miner-ui/README.md +++ b/apps/gittensory-miner-ui/README.md @@ -9,3 +9,9 @@ The miner package invariant is client-side only with no required phone-home to b local miner CLI can serve later — not a Wrangler deploy target. Phase 6 data views (run history, portfolio cards) land in follow-up issues after this empty shell. + +## Configuration + +| Env var | Required | Description | +| --------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `VITE_MINER_UI_GRAFANA_URL` | No | If set (and non-empty), renders a footer link to your ORB/Grafana dashboard at this URL. Unset ⇒ no link. Must be `VITE_`-prefixed so Vite exposes it to the client bundle. It is a plain navigational link — no token or credential is ever appended. | diff --git a/apps/gittensory-miner-ui/src/components/grafana-footer-link.tsx b/apps/gittensory-miner-ui/src/components/grafana-footer-link.tsx new file mode 100644 index 0000000000..dd4e94f475 --- /dev/null +++ b/apps/gittensory-miner-ui/src/components/grafana-footer-link.tsx @@ -0,0 +1,19 @@ +// Optional footer link to an operator's ORB/Grafana dashboard (#5194). Rendered only when the operator sets +// VITE_MINER_UI_GRAFANA_URL (Vite exposes only VITE_-prefixed vars to the client bundle; a bare name would be +// undefined at build time). The URL is a plain navigational link — no auth/session token or credential is ever +// appended, and it is rendered as a normal href attribute (React escapes it, so a misconfigured value cannot +// inject markup). Renders nothing when the var is unset or empty. +export function GrafanaFooterLink() { + const url = import.meta.env.VITE_MINER_UI_GRAFANA_URL; + if (!url) return null; + return ( + + ORB / Grafana dashboard ↗ + + ); +} diff --git a/apps/gittensory-miner-ui/src/grafana-footer-link.test.tsx b/apps/gittensory-miner-ui/src/grafana-footer-link.test.tsx new file mode 100644 index 0000000000..57fe7cb617 --- /dev/null +++ b/apps/gittensory-miner-ui/src/grafana-footer-link.test.tsx @@ -0,0 +1,32 @@ +import { render, screen } from "@testing-library/react"; +import { afterEach, describe, expect, it, vi } from "vitest"; +import { GrafanaFooterLink } from "./components/grafana-footer-link"; + +afterEach(() => vi.unstubAllEnvs()); + +describe("GrafanaFooterLink (#5194)", () => { + it("renders a new-tab link to the configured dashboard URL", () => { + vi.stubEnv("VITE_MINER_UI_GRAFANA_URL", "https://grafana.example.internal/d/ams"); + render(); + const link = screen.getByRole("link", { name: /Grafana dashboard/i }); + expect(link.getAttribute("href")).toBe("https://grafana.example.internal/d/ams"); + expect(link.getAttribute("target")).toBe("_blank"); + expect(link.getAttribute("rel")).toBe("noopener noreferrer"); + }); + + it("renders nothing when the env var is unset or empty", () => { + vi.stubEnv("VITE_MINER_UI_GRAFANA_URL", ""); + const { container } = render(); + expect(container.firstChild).toBeNull(); + expect(screen.queryByRole("link")).toBeNull(); + }); + + it("uses the URL verbatim as the href — never appends a token or credential", () => { + const url = "https://dash.example.internal/grafana"; + vi.stubEnv("VITE_MINER_UI_GRAFANA_URL", url); + render(); + const href = screen.getByRole("link").getAttribute("href") ?? ""; + expect(href).toBe(url); // exact — no `?token=`, no appended session/credential + expect(href).not.toMatch(/token|api[_-]?key|secret|session|auth=/i); + }); +}); diff --git a/apps/gittensory-miner-ui/src/routes/__root.tsx b/apps/gittensory-miner-ui/src/routes/__root.tsx index 07dc49f3c1..86499a8179 100644 --- a/apps/gittensory-miner-ui/src/routes/__root.tsx +++ b/apps/gittensory-miner-ui/src/routes/__root.tsx @@ -1,4 +1,5 @@ import { Outlet, createRootRoute, Link } from "@tanstack/react-router"; +import { GrafanaFooterLink } from "@/components/grafana-footer-link"; export const Route = createRootRoute({ component: RootLayout, @@ -29,6 +30,9 @@ function RootLayout() {
+ ); }