feat: add sentinel error detail deep links
This commit is contained in:
@@ -17,6 +17,7 @@ import { webProbeSentinelConfigPlan, type WebProbeSentinelConfigPlan } from "./h
|
||||
import type { HwlabRuntimeLaneSpec } from "./hwlab-node-lanes";
|
||||
import { effectiveWebProbeSentinelPublicExposure, resolveWebProbeSentinel, readConfigRefTarget as readSentinelConfigRefTarget } from "./hwlab-node-web-sentinel-resolver";
|
||||
import { emitWebProbeSentinelSpan, webProbeSentinelOtelSummary } from "./hwlab-node-web-sentinel-otel";
|
||||
import { dashboardErrorDetail, decorateRunFindingsWithErrorDetails } from "./hwlab-node-web-sentinel-error-detail";
|
||||
|
||||
const DASHBOARD_CONTRACT_VERSION = "draft-2026-06-27-p11-monitor-web-observability-dashboard";
|
||||
const DASHBOARD_MAX_TEXT_BYTES = 16_000;
|
||||
@@ -78,6 +79,7 @@ export interface WebProbeSentinelService {
|
||||
overview(): Record<string, unknown>;
|
||||
dashboardRuns(url: URL): Record<string, unknown>;
|
||||
runDetail(runId: string): Record<string, unknown>;
|
||||
errorDetail(errorId: string): Promise<Record<string, unknown>>;
|
||||
findings(url: URL): Record<string, unknown>;
|
||||
runViews(runId: string, view: string | null, url: URL): Record<string, unknown>;
|
||||
maintenance(): MaintenanceState;
|
||||
@@ -207,6 +209,9 @@ export function createWebProbeSentinelService(options: WebProbeSentinelServiceOp
|
||||
runDetail(runId: string) {
|
||||
return dashboardRunDetail(config, db, runId);
|
||||
},
|
||||
errorDetail(errorId: string) {
|
||||
return dashboardErrorDetail(config, db, errorId, { findingsForRow: (row, limit) => visibleFindingsForRun(config, db, row, limit, readAnalysisArtifactSummaryForRun(config, row, limit)), runSummary: (row) => dashboardRunSummary(config, db, row), traceability: (row) => runTraceability(config, row) });
|
||||
},
|
||||
findings(url: URL) {
|
||||
return dashboardFindings(config, db, url);
|
||||
},
|
||||
@@ -321,6 +326,11 @@ async function sentinelFetch(service: WebProbeSentinelService, request: Request)
|
||||
if (request.method === "GET" && pathname === "/api/overview") return jsonResponse(service.overview());
|
||||
if (request.method === "GET" && pathname === "/api/runs") return jsonResponse(service.dashboardRuns(url));
|
||||
if (request.method === "GET" && pathname === "/api/findings") return jsonResponse(service.findings(url));
|
||||
const errorDetailMatch = /^\/api\/errors\/([^/]+)$/u.exec(pathname);
|
||||
if (request.method === "GET" && errorDetailMatch !== null) {
|
||||
const result = await service.errorDetail(decodeURIComponent(errorDetailMatch[1]));
|
||||
return jsonResponse(result, result.ok === false ? 404 : 200);
|
||||
}
|
||||
const runViewsMatch = /^\/api\/runs\/([^/]+)\/views$/u.exec(pathname);
|
||||
if (request.method === "GET" && runViewsMatch !== null) {
|
||||
const view = url.searchParams.get("view");
|
||||
@@ -941,7 +951,7 @@ function dashboardRunDetail(config: WebProbeSentinelServiceConfig, db: Database,
|
||||
if (row === null) return { ok: false, error: "run-not-found", runId, valuesRedacted: true };
|
||||
const stored = readMetadata(db, `run.report.${runId}`) ?? {};
|
||||
const artifactSummary = readAnalysisArtifactSummaryForRun(config, row, dashboardMaxPageSize(config));
|
||||
const findings = visibleFindingsForRun(config, db, row, dashboardMaxPageSize(config), artifactSummary);
|
||||
const findings = decorateRunFindingsWithErrorDetails(config, row, visibleFindingsForRun(config, db, row, dashboardMaxPageSize(config), artifactSummary));
|
||||
const views = record(stored.views);
|
||||
const reportJsonSha256 = stringOrNull(row.report_json_sha256) ?? stringOrNull(artifactSummary.reportJsonSha256);
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user