diff --git a/apps/web/src/App.test.tsx b/apps/web/src/App.test.tsx index c319cc6..fbb133d 100644 --- a/apps/web/src/App.test.tsx +++ b/apps/web/src/App.test.tsx @@ -1,8 +1,6 @@ import { renderToString } from "react-dom/server"; import { describe, expect, test } from "vitest"; -import { genericDashboardFixture } from "@dimensionlab/dashboard-model/fixtures"; -import { dimensionLabDashboardFixture } from "$lib/dashboard-seed/dimensionlab"; -import { AppStateView, dashboardHydrationTiles } from "./App"; +import { AppStateView } from "./App"; describe("React app dashboard state view", () => { test("renders loading dashboard state", () => { @@ -43,43 +41,4 @@ describe("React app dashboard state view", () => { expect(html).toContain("Dark"); expect(html).toContain("Light"); }); - - test("renders the dashboard shell while individual items hydrate", () => { - const html = renderToString( - , - ); - - expect(html).toContain("Operations Console"); - expect(html).toContain("Service Uptime"); - expect(html).toContain("Identity"); - expect(html).toContain("Environment"); - expect(html).not.toContain("Loading Dashboard"); - expect(html).toContain( - 'data-severity="loading" data-model-id="service-uptime"', - ); - expect(html).toContain('data-severity="loading" data-model-id="identity"'); - expect(html).toContain('data-severity="loading" data-model-id="ambient"'); - expect(html).toContain('data-severity="loading" data-model-id="runtime:status"'); - }); - - test("hydrates every status cell from the Dimension Lab shell", () => { - expect(dashboardHydrationTiles(dimensionLabDashboardFixture)).toContainEqual({ - kind: "status", - stripId: "footer-status", - id: "auto-refresh", - }); - }); }); diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index 206b74f..0099173 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -1,11 +1,4 @@ import { useEffect, useState } from "react"; -import type { - DashboardDocument, - DashboardModule, - ServiceEntry, - StatusItem, - TelemetryCard, -} from "@dimensionlab/dashboard-model"; import type { DashboardRuntimeState } from "$lib/server/dashboard"; import { dashboardDocumentToUiDashboard } from "$lib/ui-adapter/model-renderer"; import { @@ -16,35 +9,21 @@ import { resolveInitialUiTheme, type UiTheme, type UiSeverity, - type UiDashboardPreview, } from "@dimensionlab/ui"; -type DashboardTileReference = - | { kind: "telemetry"; id: string } - | { kind: "service"; groupId: string; id: string } - | { kind: "module"; id: string } - | { kind: "status"; stripId: string; id: string }; - -type DashboardTileResponse = - | { - state: "ready"; - tile: DashboardTileReference; - item: DashboardModule | ServiceEntry | StatusItem | TelemetryCard; - } - | { - state: "not_found"; - tile: DashboardTileReference; - message: string; - }; +const loadingDashboardState: DashboardRuntimeState = { + state: "loading", + title: "Loading Dashboard", + subtitle: "Fetching active model", + message: "Waiting for the active dashboard document.", +}; export function AppStateView({ dashboard, onThemeChange, theme, - hydratingItemIds, }: { dashboard: DashboardRuntimeState; - hydratingItemIds?: ReadonlySet; onThemeChange?: (theme: UiTheme) => void; theme?: UiTheme; }) { @@ -54,14 +33,9 @@ export function AppStateView({ ) : null; if (dashboard.state === "ready") { - const uiDashboard = markHydratingItems( - dashboardDocumentToUiDashboard(dashboard.document), - hydratingItemIds, - ); - return ( ); @@ -115,10 +89,7 @@ export function resolveDocumentMetadata(dashboard: DashboardRuntimeState): { export default function App() { const [dashboard, setDashboard] = - useState(null); - const [hydratingItemIds, setHydratingItemIds] = useState>( - () => new Set(), - ); + useState(loadingDashboardState); const [theme, setTheme] = useState(() => { if (typeof window === "undefined") return "dark"; @@ -126,8 +97,6 @@ export default function App() { }); useEffect(() => { - if (!dashboard) return; - const metadata = resolveDocumentMetadata(dashboard); document.title = metadata.title; @@ -150,7 +119,6 @@ export default function App() { useEffect(() => { let cancelled = false; let refreshTimer: number | undefined; - let hydrationRun = 0; async function loadDashboard() { const response = await fetch("/api/dashboard"); @@ -159,18 +127,6 @@ export default function App() { if (cancelled) return; setDashboard(nextDashboard); - const currentRun = ++hydrationRun; - if ( - nextDashboard.state === "ready" && - nextDashboard.liveDatasourceHydration?.enabled !== false - ) { - const tiles = dashboardHydrationTiles(nextDashboard.document); - setHydratingItemIds(new Set(tiles.map(dashboardTileKey))); - hydrateDashboardTiles(tiles, currentRun); - } else { - setHydratingItemIds(new Set()); - } - if (refreshTimer) { window.clearInterval(refreshTimer); refreshTimer = undefined; @@ -189,54 +145,6 @@ export default function App() { } } - function hydrateDashboardTiles( - tiles: DashboardTileReference[], - run: number, - ) { - tiles.forEach((tile) => { - void hydrateDashboardTile(tile, run); - }); - } - - async function hydrateDashboardTile( - tile: DashboardTileReference, - run: number, - ) { - const key = dashboardTileKey(tile); - - try { - const response = await fetch(dashboardTileUrl(tile)); - const tileResponse = (await response.json()) as DashboardTileResponse; - - if ( - cancelled || - run !== hydrationRun || - !response.ok || - tileResponse.state !== "ready" - ) { - return; - } - - const readyTileResponse = tileResponse; - setDashboard((current) => - current?.state === "ready" - ? { - ...current, - document: applyDashboardTile(current.document, readyTileResponse), - } - : current, - ); - } finally { - if (!cancelled && run === hydrationRun) { - setHydratingItemIds((current) => { - const next = new Set(current); - next.delete(key); - return next; - }); - } - } - } - void loadDashboard(); return () => { @@ -245,14 +153,13 @@ export default function App() { }; }, []); - return dashboard ? ( + return ( - ) : null; + ); } function stateSeverity(state: DashboardRuntimeState["state"]): UiSeverity { @@ -274,154 +181,3 @@ function getThemeStorage(): Storage | undefined { return undefined; } } - -function markHydratingItems( - dashboard: UiDashboardPreview, - hydratingItemIds?: ReadonlySet, -): UiDashboardPreview { - if (!hydratingItemIds?.size) return dashboard; - - return { - ...dashboard, - telemetry: dashboard.telemetry.map((card) => - hydratingItemIds.has(`telemetry:${card.id}`) - ? { - ...card, - severity: "loading", - detail: "loading live telemetry", - } - : card, - ), - serviceGroups: dashboard.serviceGroups.map((group) => ({ - ...group, - services: group.services.map((service) => - hydratingItemIds.has(`service:${group.id}:${service.id}`) - ? { - ...service, - severity: "loading", - detail: "loading", - } - : service, - ), - })), - modules: dashboard.modules.map((module) => - hydratingItemIds.has(`module:${module.id}`) - ? { - ...module, - severity: "loading", - detail: "loading live data", - } - : module, - ), - statusItems: dashboard.statusItems.map((item) => - hydratingItemIds.has(`status:${item.id}`) - ? { - ...item, - severity: "loading", - value: "loading", - } - : item, - ), - }; -} - -export function dashboardHydrationTiles( - document: DashboardDocument, -): DashboardTileReference[] { - const telemetry = document.telemetry - .filter((card) => card.datasource?.type === "external") - .map((card): DashboardTileReference => ({ kind: "telemetry", id: card.id })); - const services = document.serviceGroups.flatMap((group) => - group.services - .filter((service) => service.datasource?.type === "external") - .map((service): DashboardTileReference => ({ - kind: "service", - groupId: group.id, - id: service.id, - })), - ); - const modules = (document.modules || []) - .filter((module) => - module.datasource?.type === "external" || - module.id === "runtime-health-summary" - ) - .map((module): DashboardTileReference => ({ kind: "module", id: module.id })); - const status = document.statusStrips.flatMap((strip) => - strip.items - .map((item): DashboardTileReference => ({ - kind: "status", - stripId: strip.id, - id: item.id, - })), - ); - - return [...telemetry, ...services, ...modules, ...status]; -} - -function dashboardTileKey(tile: DashboardTileReference): string { - if (tile.kind === "status") return `${tile.kind}:${tile.stripId}:${tile.id}`; - if (tile.kind === "service") return `${tile.kind}:${tile.groupId}:${tile.id}`; - return `${tile.kind}:${tile.id}`; -} - -function dashboardTileUrl(tile: DashboardTileReference): string { - const parts = tile.kind === "status" - ? ["api", "dashboard", "tile", tile.kind, tile.stripId, tile.id] - : tile.kind === "service" - ? ["api", "dashboard", "tile", tile.kind, tile.groupId, tile.id] - : ["api", "dashboard", "tile", tile.kind, tile.id]; - return `/${parts.map(encodeURIComponent).join("/")}`; -} - -function applyDashboardTile( - document: DashboardDocument, - response: Extract, -): DashboardDocument { - if (response.tile.kind === "telemetry") { - return { - ...document, - telemetry: document.telemetry.map((card) => - card.id === response.tile.id ? response.item as TelemetryCard : card, - ), - }; - } - - if (response.tile.kind === "service") { - const tile = response.tile; - return { - ...document, - serviceGroups: document.serviceGroups.map((group) => ({ - ...group, - services: group.id === tile.groupId - ? group.services.map((service) => - service.id === tile.id ? response.item as ServiceEntry : service, - ) - : group.services, - })), - }; - } - - if (response.tile.kind === "module") { - return { - ...document, - modules: (document.modules || []).map((module) => - module.id === response.tile.id ? response.item as DashboardModule : module, - ), - }; - } - - const tile = response.tile; - return { - ...document, - statusStrips: document.statusStrips.map((strip) => - strip.id === tile.stripId - ? { - ...strip, - items: strip.items.map((item) => - item.id === tile.id ? response.item as StatusItem : item, - ), - } - : strip, - ), - }; -} diff --git a/apps/web/src/lib/server/dashboard.ts b/apps/web/src/lib/server/dashboard.ts index 33afa3a..47ebfa2 100644 --- a/apps/web/src/lib/server/dashboard.ts +++ b/apps/web/src/lib/server/dashboard.ts @@ -18,9 +18,6 @@ export interface DashboardRuntimeReady { document: DashboardDocument; schemaVersion: string; currentRevisionId: string; - liveDatasourceHydration?: { - enabled: boolean; - }; } export interface DashboardRuntimeEmpty { diff --git a/apps/web/src/lib/server/datasources/index.ts b/apps/web/src/lib/server/datasources/index.ts index ee24bcd..d69db86 100644 --- a/apps/web/src/lib/server/datasources/index.ts +++ b/apps/web/src/lib/server/datasources/index.ts @@ -10,35 +10,6 @@ import type { TelemetryCard, } from "@dimensionlab/dashboard-model"; -export type DashboardTileReference = - | { kind: "telemetry"; id: string } - | { kind: "service"; groupId: string; id: string } - | { kind: "module"; id: string } - | { kind: "status"; stripId: string; id: string }; - -export type DashboardTileItem = - | DashboardModule - | ServiceEntry - | StatusItem - | TelemetryCard; - -export type DashboardTileResolution = - | { - state: "ready"; - tile: DashboardTileReference; - item: DashboardTileItem; - } - | { - state: "not_found"; - tile: DashboardTileReference; - message: string; - } - | { - state: "disabled"; - tile: DashboardTileReference; - message: string; - }; - export interface DatasourceResolutionOptions { fetch?: DatasourceFetch; prometheusBaseUrl?: string; @@ -75,69 +46,6 @@ export async function resolveDashboardDatasources( }; } -export async function resolveDashboardTile( - document: DashboardDocument, - tile: DashboardTileReference, - options: DatasourceResolutionOptions = {}, -): Promise { - const context = datasourceContext(options); - - if (tile.kind === "telemetry") { - const card = document.telemetry.find((item) => item.id === tile.id); - if (!card) return missingTile(tile); - - return { - state: "ready", - tile, - item: await resolveTelemetryCard(card, context), - }; - } - - if (tile.kind === "service") { - const service = document.serviceGroups - .find((group) => group.id === tile.groupId) - ?.services.find((item) => item.id === tile.id); - if (!service) return missingTile(tile); - - return { - state: "ready", - tile, - item: await resolveService(service, context), - }; - } - - if (tile.kind === "module") { - const module = document.modules?.find((item) => item.id === tile.id); - if (!module) return missingTile(tile); - - const item = module.id === "runtime-health-summary" - ? runtimeHealthSummary( - module, - await Promise.all( - document.serviceGroups.map((group) => resolveServiceGroup(group, context)), - ), - ) - : await resolveModule(module, context); - - return { state: "ready", tile, item }; - } - - const strip = document.statusStrips.find((item) => item.id === tile.stripId); - const statusItem = strip?.items.find((item) => item.id === tile.id); - if (!strip || !statusItem) return missingTile(tile); - - return { - state: "ready", - tile, - item: await resolveStatusTile( - statusItem, - document.metadata.refreshIntervalSeconds, - document.serviceGroups, - context, - ), - }; -} - interface DatasourceContext { fetch: DatasourceFetch; prometheusBaseUrl: string; @@ -482,68 +390,6 @@ function resolveStatusItem( return structuredClone(item); } -async function resolveStatusTile( - item: StatusItem, - refreshIntervalSeconds: number | undefined, - serviceGroups: ServiceGroup[], - context: DatasourceContext, -): Promise { - if (item.id === "system-status") { - const resolvedGroups = await Promise.all( - serviceGroups.map((group) => resolveServiceGroup(group, context)), - ); - const health = serviceHealthSummary(resolvedGroups); - return { - ...structuredClone(item), - value: health.value, - severity: health.severity, - }; - } - - if (item.id === "last-sync") { - return { - ...structuredClone(item), - value: "just now", - severity: "ok", - }; - } - - if (item.id === "uptime") { - const uptime = await prometheusScalar( - 'time() - node_boot_time_seconds{job="node",host="linux-infra"}', - context, - ).catch(() => null); - return uptime === null - ? structuredClone(item) - : { - ...structuredClone(item), - value: formatDuration(uptime), - severity: "ok", - }; - } - - if (item.id === "load-avg") { - const loadAverage = await prometheusLoadAverage(context).catch(() => null); - return loadAverage - ? { - ...structuredClone(item), - value: loadAverage, - severity: "neutral", - } - : structuredClone(item); - } - - if (item.id === "auto-refresh" && refreshIntervalSeconds) { - return { - ...structuredClone(item), - value: `${refreshIntervalSeconds}s`, - severity: "neutral", - }; - } - - return structuredClone(item); -} - function serviceHealthSummary(serviceGroups: ServiceGroup[]): { severity: Severity; value: string; @@ -827,17 +673,3 @@ function formatDuration(totalSeconds: number): string { const minutes = Math.floor((seconds % 3_600) / 60); return `${days}d ${hours}h ${minutes}m`; } - -function missingTile(tile: DashboardTileReference): DashboardTileResolution { - return { - state: "not_found", - tile, - message: `Dashboard tile not found: ${tileKey(tile)}`, - }; -} - -function tileKey(tile: DashboardTileReference): string { - if (tile.kind === "status") return `${tile.kind}:${tile.stripId}:${tile.id}`; - if (tile.kind === "service") return `${tile.kind}:${tile.groupId}:${tile.id}`; - return `${tile.kind}:${tile.id}`; -} diff --git a/apps/web/src/server/index.ts b/apps/web/src/server/index.ts index ba2135f..860d042 100644 --- a/apps/web/src/server/index.ts +++ b/apps/web/src/server/index.ts @@ -1,6 +1,6 @@ import { extname, normalize } from "node:path"; import { handleAgentDashboardRoute } from "./routes/agent-dashboard"; -import { handleDashboardRoute, handleDashboardTileRoute } from "./routes/dashboard"; +import { handleDashboardRoute } from "./routes/dashboard"; const host = process.env.HOST || "0.0.0.0"; const port = Number(process.env.PORT || 3000); @@ -23,11 +23,6 @@ export async function handleRequest(request: Request): Promise { return handleDashboardRoute(); } - if (url.pathname.startsWith("/api/dashboard/tile/")) { - if (request.method !== "GET") return methodNotAllowed(["GET"]); - return handleDashboardTileRoute(url.pathname); - } - if (url.pathname === "/api/agent/dashboard") { if (request.method !== "POST") return methodNotAllowed(["POST"]); return handleAgentDashboardRoute(request); diff --git a/apps/web/src/server/routes/dashboard.test.ts b/apps/web/src/server/routes/dashboard.test.ts index 095ee48..21ad58d 100644 --- a/apps/web/src/server/routes/dashboard.test.ts +++ b/apps/web/src/server/routes/dashboard.test.ts @@ -1,14 +1,11 @@ -import { describe, expect, test, vi } from "vitest"; +import { describe, expect, test } from "vitest"; import { dimensionLabDashboardFixture } from "$lib/dashboard-seed/dimensionlab"; -import { loadDashboardResponse, loadDashboardTileResponse } from "./dashboard"; +import { loadDashboardResponse } from "./dashboard"; describe("dashboard API route", () => { - test("returns the ready dashboard shell without hydrating live datasources", async () => { - const fetch = vi.spyOn(globalThis, "fetch").mockRejectedValue( - new Error("live datasource fetch should not run for the shell response"), - ); - + test("returns ready dashboard runtime state from the existing model loader", async () => { const response = await loadDashboardResponse({ + disableLiveDatasources: true, refreshSeedDocument: true, seedIfEmpty: true, }); @@ -18,164 +15,5 @@ describe("dashboard API route", () => { expect(response.document.metadata.title).toBe( dimensionLabDashboardFixture.metadata.title, ); - expect(fetch).not.toHaveBeenCalled(); - - fetch.mockRestore(); - }); - - test("reports when client-side live hydration is disabled", async () => { - const previous = process.env.DISABLE_LIVE_DATASOURCES; - process.env.DISABLE_LIVE_DATASOURCES = "1"; - - try { - const response = await loadDashboardResponse({ - refreshSeedDocument: true, - seedIfEmpty: true, - }); - - expect(response.state).toBe("ready"); - if (response.state !== "ready") throw new Error("expected ready dashboard"); - expect(response.liveDatasourceHydration).toEqual({ enabled: false }); - } finally { - if (previous === undefined) { - delete process.env.DISABLE_LIVE_DATASOURCES; - } else { - process.env.DISABLE_LIVE_DATASOURCES = previous; - } - } - }); - - test("hydrates a telemetry tile independently from the dashboard shell", async () => { - const fetch = vi.fn(async (input: RequestInfo | URL) => { - const url = String(input); - - if (url.startsWith("https://prometheus.example/api/v1/query_range")) { - return jsonResponse({ - status: "success", - data: { - result: [ - { - metric: { host: "linux-infra" }, - values: [ - [1771430000, "10"], - [1771430060, "20"], - [1771430120, "42"], - ], - }, - ], - }, - }); - } - - if (url.startsWith("https://prometheus.example/api/v1/query")) { - return jsonResponse({ - status: "success", - data: { - result: [ - { - metric: { host: "linux-infra" }, - value: [1771430400, "42"], - }, - ], - }, - }); - } - - throw new Error(`Unhandled test request: ${url}`); - }); - - const response = await loadDashboardTileResponse( - { kind: "telemetry", id: "infra-ram" }, - { - fetch, - prometheusBaseUrl: "https://prometheus.example", - refreshSeedDocument: true, - seedIfEmpty: true, - }, - ); - - expect(response.state).toBe("ready"); - if (response.state !== "ready") throw new Error("expected ready tile"); - expect(response.tile).toEqual({ kind: "telemetry", id: "infra-ram" }); - expect(response.item).toMatchObject({ - id: "infra-ram", - value: { kind: "percent", value: 42 }, - severity: "ok", - detail: "linux-infra", - sparkline: [10, 20, 42], - }); - expect(fetch).toHaveBeenCalledWith( - expect.stringContaining("/api/v1/query?"), - expect.objectContaining({ cache: "no-store" }), - ); - expect(fetch).toHaveBeenCalledWith( - expect.stringContaining("/api/v1/query_range?"), - expect.objectContaining({ cache: "no-store" }), - ); - }); - - test("hydrates a service tile with its service group identity", async () => { - const fetch = vi.fn(async () => - jsonResponse({ - status: "UP", - ping: 42, - }), - ); - - const response = await loadDashboardTileResponse( - { kind: "service", groupId: "essentials", id: "vaultwarden" }, - { - fetch, - refreshSeedDocument: true, - seedIfEmpty: true, - }, - ); - - expect(response.state).toBe("ready"); - if (response.state !== "ready") throw new Error("expected ready tile"); - expect(response.tile).toEqual({ - kind: "service", - groupId: "essentials", - id: "vaultwarden", - }); - expect(response.item).toMatchObject({ - id: "vaultwarden", - severity: "ok", - detail: "42 ms", - }); - }); - - test("does not hydrate tile routes when live datasources are disabled", async () => { - const previous = process.env.DISABLE_LIVE_DATASOURCES; - process.env.DISABLE_LIVE_DATASOURCES = "1"; - const fetch = vi.spyOn(globalThis, "fetch").mockRejectedValue( - new Error("live datasource fetch should not run when disabled"), - ); - - try { - const response = await loadDashboardTileResponse( - { kind: "telemetry", id: "infra-ram" }, - { - refreshSeedDocument: true, - seedIfEmpty: true, - }, - ); - - expect(response.state).toBe("disabled"); - expect(fetch).not.toHaveBeenCalled(); - } finally { - fetch.mockRestore(); - if (previous === undefined) { - delete process.env.DISABLE_LIVE_DATASOURCES; - } else { - process.env.DISABLE_LIVE_DATASOURCES = previous; - } - } }); }); - -function jsonResponse(payload: unknown): Response { - return new Response(JSON.stringify(payload), { - headers: { "content-type": "application/json" }, - }); -} diff --git a/apps/web/src/server/routes/dashboard.ts b/apps/web/src/server/routes/dashboard.ts index fd0196e..21c30e4 100644 --- a/apps/web/src/server/routes/dashboard.ts +++ b/apps/web/src/server/routes/dashboard.ts @@ -3,30 +3,19 @@ import { type DashboardRuntimeOptions, type DashboardRuntimeState, } from "$lib/server/dashboard"; -import { - resolveDashboardDatasources, - resolveDashboardTile, - type DashboardTileReference, - type DashboardTileResolution, - type DatasourceResolutionOptions, -} from "$lib/server/datasources"; +import { resolveDashboardDatasources } from "$lib/server/datasources"; export interface LoadDashboardResponseOptions extends Pick< DashboardRuntimeOptions, "refreshSeedDocument" | "seedIfEmpty" | "seedDocument" - >, - DatasourceResolutionOptions { + > { disableLiveDatasources?: boolean; - hydrateLiveDatasources?: boolean; } export async function loadDashboardResponse( options: LoadDashboardResponseOptions = {}, ): Promise { - const liveHydrationEnabled = - !options.disableLiveDatasources && - process.env.DISABLE_LIVE_DATASOURCES !== "1"; const dashboard = loadDashboardRuntime(undefined, { refreshSeedDocument: options.refreshSeedDocument ?? true, seedIfEmpty: options.seedIfEmpty ?? true, @@ -37,106 +26,16 @@ export async function loadDashboardResponse( return dashboard; } - if ( - !options.hydrateLiveDatasources || - options.disableLiveDatasources || - process.env.DISABLE_LIVE_DATASOURCES === "1" - ) { - return { - ...dashboard, - liveDatasourceHydration: { - enabled: liveHydrationEnabled, - }, - }; + if (options.disableLiveDatasources || process.env.DISABLE_LIVE_DATASOURCES === "1") { + return dashboard; } return { ...dashboard, - document: await resolveDashboardDatasources(dashboard.document, options), - liveDatasourceHydration: { - enabled: false, - }, + document: await resolveDashboardDatasources(dashboard.document), }; } -export async function loadDashboardTileResponse( - tile: DashboardTileReference, - options: LoadDashboardResponseOptions = {}, -): Promise { - if ( - options.disableLiveDatasources || - process.env.DISABLE_LIVE_DATASOURCES === "1" - ) { - return { - state: "disabled", - tile, - message: "Live datasource hydration is disabled.", - }; - } - - const dashboard = loadDashboardRuntime(undefined, { - refreshSeedDocument: options.refreshSeedDocument ?? true, - seedIfEmpty: options.seedIfEmpty ?? true, - seedDocument: options.seedDocument, - }); - - if (dashboard.state !== "ready") { - return { - state: "not_found", - tile, - message: `Dashboard is not ready: ${dashboard.state}`, - }; - } - - return resolveDashboardTile(dashboard.document, tile, options); -} - export async function handleDashboardRoute(): Promise { return Response.json(await loadDashboardResponse()); } - -export async function handleDashboardTileRoute(pathname: string): Promise { - const tile = parseDashboardTilePath(pathname); - if (!tile) { - return Response.json( - { ok: false, message: "Invalid dashboard tile route" }, - { status: 404 }, - ); - } - - const response = await loadDashboardTileResponse(tile); - return Response.json(response, { - status: response.state === "not_found" ? 404 : 200, - }); -} - -function parseDashboardTilePath(pathname: string): DashboardTileReference | null { - const parts = pathname.split("/").filter(Boolean); - const [, dashboard, tileRoot, kind, firstId, secondId] = parts; - if (dashboard !== "dashboard" || tileRoot !== "tile" || !kind || !firstId) { - return null; - } - - const id = decodeURIComponent(firstId); - if (kind === "telemetry" || kind === "module") { - return { kind, id }; - } - - if (kind === "service" && secondId) { - return { - kind, - groupId: id, - id: decodeURIComponent(secondId), - }; - } - - if (kind === "status" && secondId) { - return { - kind, - stripId: id, - id: decodeURIComponent(secondId), - }; - } - - return null; -}