import type { DashboardDocument } from "@dimensionlab/dashboard-model"; import { loadDashboardRuntime, type DashboardRuntimeOptions, type DashboardRuntimeState, } from "$lib/server/dashboard"; import { resolveDashboardDatasources, resolveDashboardTile, type DashboardTileReference, type DashboardTileResolution, type DatasourceResolutionOptions, } from "$lib/server/datasources"; export interface LoadDashboardResponseOptions extends Pick< DashboardRuntimeOptions, "refreshSeedDocument" | "seedIfEmpty" | "seedDocument" >, 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( 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, seedDocument: options.seedDocument, }); if (dashboard.state !== "ready") { return dashboard; } if ( !options.hydrateLiveDatasources || options.disableLiveDatasources || process.env.DISABLE_LIVE_DATASOURCES === "1" ) { return { ...dashboard, liveDatasourceHydration: { enabled: liveHydrationEnabled, }, }; } return { ...dashboard, document: await resolveDashboardDatasources(dashboard.document, options), liveDatasourceHydration: { enabled: false, }, }; } 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}`, }; } 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), ); } export async function handleDashboardRoute(): Promise { return Response.json(await loadDashboardResponse()); } export async function handleDashboardTileRoute( pathname: string, options: LoadDashboardResponseOptions = {}, ): Promise { const tile = parseDashboardTilePath(pathname); if (!tile) { return Response.json( { ok: false, message: "Invalid dashboard tile route" }, { status: 404 }, ); } const response = await loadDashboardTileResponse(tile, options); 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; 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; }