diff --git a/apps/web/src/server/routes/dashboard.test.ts b/apps/web/src/server/routes/dashboard.test.ts index 9b11079..095ee48 100644 --- a/apps/web/src/server/routes/dashboard.test.ts +++ b/apps/web/src/server/routes/dashboard.test.ts @@ -1,12 +1,6 @@ import { describe, expect, test, vi } from "vitest"; import { dimensionLabDashboardFixture } from "$lib/dashboard-seed/dimensionlab"; -import { - createDashboardTileCache, - dashboardTileCacheKey, - handleDashboardTileRoute, - loadDashboardResponse, - loadDashboardTileResponse, -} from "./dashboard"; +import { loadDashboardResponse, loadDashboardTileResponse } from "./dashboard"; describe("dashboard API route", () => { test("returns the ready dashboard shell without hydrating live datasources", async () => { @@ -151,243 +145,6 @@ describe("dashboard API route", () => { }); }); - test("caches ready tile responses until the tile ttl expires", async () => { - const cache = createDashboardTileCache(); - let now = 1_000; - const fetch = vi.fn(async () => - jsonResponse({ - status: "UP", - ping: 42, - }), - ); - const tile = { kind: "service", groupId: "essentials", id: "vaultwarden" } as const; - - const first = await loadDashboardTileResponse(tile, { - fetch, - now: () => now, - refreshSeedDocument: true, - seedIfEmpty: true, - tileCache: cache, - }); - const second = await loadDashboardTileResponse(tile, { - fetch, - now: () => now + 29_999, - refreshSeedDocument: true, - seedIfEmpty: true, - tileCache: cache, - }); - now += 30_001; - const third = await loadDashboardTileResponse(tile, { - fetch, - now: () => now, - refreshSeedDocument: true, - seedIfEmpty: true, - tileCache: cache, - }); - - expect(first).toEqual(second); - expect(third.state).toBe("ready"); - expect(fetch).toHaveBeenCalledTimes(2); - }); - - test("keeps telemetry tiles cached for fifteen seconds", async () => { - const cache = createDashboardTileCache(); - let now = 1_000; - const fetch = telemetryFetch(); - const tile = { kind: "telemetry", id: "infra-ram" } as const; - - await loadDashboardTileResponse(tile, { - fetch, - now: () => now, - prometheusBaseUrl: "https://prometheus.example", - refreshSeedDocument: true, - seedIfEmpty: true, - tileCache: cache, - }); - await loadDashboardTileResponse(tile, { - fetch, - now: () => now + 14_999, - prometheusBaseUrl: "https://prometheus.example", - refreshSeedDocument: true, - seedIfEmpty: true, - tileCache: cache, - }); - now += 15_001; - await loadDashboardTileResponse(tile, { - fetch, - now: () => now, - prometheusBaseUrl: "https://prometheus.example", - refreshSeedDocument: true, - seedIfEmpty: true, - tileCache: cache, - }); - - expect(fetch).toHaveBeenCalledTimes(4); - }); - - test("keeps weather module tiles cached for ten minutes", async () => { - const cache = createDashboardTileCache(); - let now = 1_000; - const fetch = vi.fn(async () => - jsonResponse({ - current: { - apparent_temperature: 19, - temperature_2m: 20, - weather_code: 0, - wind_speed_10m: 11, - }, - }), - ); - const tile = { kind: "module", id: "weather-amsterdam" } as const; - - await loadDashboardTileResponse(tile, { - fetch, - now: () => now, - refreshSeedDocument: true, - seedIfEmpty: true, - tileCache: cache, - }); - await loadDashboardTileResponse(tile, { - fetch, - now: () => now + 599_999, - refreshSeedDocument: true, - seedIfEmpty: true, - tileCache: cache, - }); - now += 600_001; - await loadDashboardTileResponse(tile, { - fetch, - now: () => now, - refreshSeedDocument: true, - seedIfEmpty: true, - tileCache: cache, - }); - - expect(fetch).toHaveBeenCalledTimes(2); - }); - - test("coalesces concurrent tile requests for the same cache key", async () => { - const cache = createDashboardTileCache(); - let resolveFetch: ((response: Response) => void) | undefined; - const fetch = vi.fn(() => - new Promise((resolve) => { - resolveFetch = resolve; - }) - ); - const tile = { kind: "service", groupId: "essentials", id: "vaultwarden" } as const; - - const first = loadDashboardTileResponse(tile, { - fetch, - now: () => 1_000, - refreshSeedDocument: true, - seedIfEmpty: true, - tileCache: cache, - }); - const second = loadDashboardTileResponse(tile, { - fetch, - now: () => 1_000, - refreshSeedDocument: true, - seedIfEmpty: true, - tileCache: cache, - }); - - await Promise.resolve(); - expect(fetch).toHaveBeenCalledTimes(1); - - resolveFetch?.(jsonResponse({ status: "UP", ping: 42 })); - expect(await first).toEqual(await second); - }); - - test("uses structured tile cache keys when identifiers contain delimiters", () => { - expect( - dashboardTileCacheKey({ kind: "service", groupId: "a:b", id: "c" }), - ).not.toBe( - dashboardTileCacheKey({ kind: "service", groupId: "a", id: "b:c" }), - ); - expect( - dashboardTileCacheKey({ kind: "status", stripId: "a:b", id: "c" }), - ).not.toBe( - dashboardTileCacheKey({ kind: "status", stripId: "a", id: "b:c" }), - ); - }); - - test("uses thirty-second status aggregate and five-minute static status ttl buckets", async () => { - const cache = createDashboardTileCache(); - const fetch = vi.fn(async () => jsonResponse({ status: "UP", ping: 42 })); - const serviceCheckCount = dimensionLabDashboardFixture.serviceGroups - .flatMap((group) => group.services).length; - - await loadDashboardTileResponse( - { kind: "status", stripId: "footer-status", id: "system-status" }, - { - fetch, - now: () => 1_000, - refreshSeedDocument: true, - seedIfEmpty: true, - tileCache: cache, - }, - ); - await loadDashboardTileResponse( - { kind: "status", stripId: "footer-status", id: "system-status" }, - { - fetch, - now: () => 31_001, - refreshSeedDocument: true, - seedIfEmpty: true, - tileCache: cache, - }, - ); - - await loadDashboardTileResponse( - { kind: "status", stripId: "footer-status", id: "auto-refresh" }, - { - fetch, - now: () => 1_000, - refreshSeedDocument: true, - seedIfEmpty: true, - tileCache: cache, - }, - ); - await loadDashboardTileResponse( - { kind: "status", stripId: "footer-status", id: "auto-refresh" }, - { - fetch, - now: () => 300_999, - refreshSeedDocument: true, - seedIfEmpty: true, - tileCache: cache, - }, - ); - await loadDashboardTileResponse( - { kind: "status", stripId: "footer-status", id: "auto-refresh" }, - { - fetch, - now: () => 301_001, - refreshSeedDocument: true, - seedIfEmpty: true, - tileCache: cache, - }, - ); - - expect(fetch).toHaveBeenCalledTimes(serviceCheckCount * 2); - }); - - test("serves tile route responses with short private cache headers", async () => { - const response = await handleDashboardTileRoute( - "/api/dashboard/tile/service/essentials/vaultwarden", - { - fetch: vi.fn(async () => jsonResponse({ status: "UP", ping: 42 })), - refreshSeedDocument: true, - seedIfEmpty: true, - tileCache: createDashboardTileCache(), - }, - ); - - expect(response.headers.get("cache-control")).toBe( - "private, max-age=5, stale-while-revalidate=30", - ); - }); - 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"; @@ -422,43 +179,3 @@ function jsonResponse(payload: unknown): Response { headers: { "content-type": "application/json" }, }); } - -function telemetryFetch() { - return 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}`); - }); -} diff --git a/apps/web/src/server/routes/dashboard.ts b/apps/web/src/server/routes/dashboard.ts index ce9c4e6..fd0196e 100644 --- a/apps/web/src/server/routes/dashboard.ts +++ b/apps/web/src/server/routes/dashboard.ts @@ -1,4 +1,3 @@ -import type { DashboardDocument } from "@dimensionlab/dashboard-model"; import { loadDashboardRuntime, type DashboardRuntimeOptions, @@ -20,63 +19,6 @@ export interface LoadDashboardResponseOptions DatasourceResolutionOptions { disableLiveDatasources?: boolean; hydrateLiveDatasources?: boolean; - now?: () => number; - tileCache?: DashboardTileCache; -} - -interface DashboardTileCacheEntry { - expiresAt: number; - response: DashboardTileResolution; -} - -export interface DashboardTileCache { - resolve( - key: string, - ttlMs: number, - now: number, - load: () => Promise, - ): Promise; -} - -const dashboardTileResponseHeaders = { - "Cache-Control": "private, max-age=5, stale-while-revalidate=30", -}; - -const defaultDashboardTileCache = createDashboardTileCache(); - -export function createDashboardTileCache(): DashboardTileCache { - const entries = new Map(); - const inFlight = new Map>(); - - return { - async resolve(key, ttlMs, now, load) { - const cached = entries.get(key); - if (cached && cached.expiresAt > now) { - return cached.response; - } - - const active = inFlight.get(key); - if (active) return active; - - const request = load() - .then((response) => { - if (response.state === "ready") { - entries.set(key, { - expiresAt: now + ttlMs, - response, - }); - } - - return response; - }) - .finally(() => { - inFlight.delete(key); - }); - - inFlight.set(key, request); - return request; - }, - }; } export async function loadDashboardResponse( @@ -146,25 +88,14 @@ export async function loadDashboardTileResponse( }; } - const cache = options.tileCache || defaultDashboardTileCache; - const now = options.now?.() ?? Date.now(); - - return cache.resolve( - dashboardTileCacheKey(tile), - dashboardTileTtlMs(dashboard.document, tile), - now, - () => resolveDashboardTile(dashboard.document, tile, options), - ); + return resolveDashboardTile(dashboard.document, tile, options); } export async function handleDashboardRoute(): Promise { return Response.json(await loadDashboardResponse()); } -export async function handleDashboardTileRoute( - pathname: string, - options: LoadDashboardResponseOptions = {}, -): Promise { +export async function handleDashboardTileRoute(pathname: string): Promise { const tile = parseDashboardTilePath(pathname); if (!tile) { return Response.json( @@ -173,42 +104,12 @@ export async function handleDashboardTileRoute( ); } - const response = await loadDashboardTileResponse(tile, options); + const response = await loadDashboardTileResponse(tile); return Response.json(response, { status: response.state === "not_found" ? 404 : 200, - headers: dashboardTileResponseHeaders, }); } -export function dashboardTileCacheKey(tile: DashboardTileReference): string { - return JSON.stringify(tile); -} - -function dashboardTileTtlMs( - document: DashboardDocument, - tile: DashboardTileReference, -): number { - if (tile.kind === "telemetry") return 15_000; - if (tile.kind === "service") return 30_000; - - if (tile.kind === "module") { - const module = document.modules?.find((item) => item.id === tile.id); - if ( - module?.datasource?.type === "external" && - module.datasource.adapter === "weather" - ) { - return 10 * 60_000; - } - return 30_000; - } - - if (["system-status", "uptime", "load-avg"].includes(tile.id)) { - return 30_000; - } - - return 5 * 60_000; -} - function parseDashboardTilePath(pathname: string): DashboardTileReference | null { const parts = pathname.split("/").filter(Boolean); const [, dashboard, tileRoot, kind, firstId, secondId] = parts;