feat(web-probe): add multi-sentinel registry
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
// SPEC: PJ2026-01060508 Web哨兵 draft-2026-06-26-p9-desktop-view-density.
|
||||
// SPEC: PJ2026-01060508 Web哨兵 draft-2026-06-26-p9-multi-web-probe-sentinel.
|
||||
// Responsibility: Browser-side API client, formatting, auto refresh, and base dashboard rendering.
|
||||
// Desktop view redesign (issue #1025): 三栏 master-detail、低噪声、渐进披露、排查直觉化。
|
||||
// 保持纯 vanilla JS + 原生 CSS,无框架、无构建步骤,所有渲染通过 innerHTML 拼接。
|
||||
@@ -15,6 +16,7 @@
|
||||
*/
|
||||
|
||||
const root = document.getElementById("sentinel-dashboard");
|
||||
const basePath = normalizeBasePath(root?.dataset?.basePath || "");
|
||||
const refs = {
|
||||
statusPill: document.getElementById("status-pill"),
|
||||
loadingBanner: document.getElementById("loading-banner"),
|
||||
@@ -178,21 +180,31 @@ loadDashboard({ silent: false }).catch((error) => renderError(error));
|
||||
function createDashboardApi() {
|
||||
return {
|
||||
/** @returns {Promise<SentinelOverview>} */
|
||||
overview: () => getJson("/api/overview"),
|
||||
overview: () => getJson(apiPath("/api/overview")),
|
||||
/** @returns {Promise<SentinelRunsResponse>} */
|
||||
runs: (filters) => getJson(`/api/runs?${runsQuery(filters)}`),
|
||||
runs: (filters) => getJson(apiPath(`/api/runs?${runsQuery(filters)}`)),
|
||||
/** @returns {Promise<SentinelFindingsResponse>} */
|
||||
findings: (filters) => getJson(`/api/findings?${findingsQuery(filters)}`),
|
||||
findings: (filters) => getJson(apiPath(`/api/findings?${findingsQuery(filters)}`)),
|
||||
/** @returns {Promise<SentinelRunDetail>} */
|
||||
runDetail: (runId) => getJson(`/api/runs/${encodeURIComponent(runId)}`),
|
||||
runDetail: (runId) => getJson(apiPath(`/api/runs/${encodeURIComponent(runId)}`)),
|
||||
runViews: (runId, view = null) => {
|
||||
const query = new URLSearchParams({ maxBytes: "24000" });
|
||||
if (view) query.set("view", view);
|
||||
return getJson(`/api/runs/${encodeURIComponent(runId)}/views?${query.toString()}`);
|
||||
return getJson(apiPath(`/api/runs/${encodeURIComponent(runId)}/views?${query.toString()}`));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function apiPath(path) {
|
||||
return `${basePath}${path}`;
|
||||
}
|
||||
|
||||
function normalizeBasePath(value) {
|
||||
const text = String(value || "").replace(/\/+$/u, "");
|
||||
if (!text || text === "/") return "";
|
||||
return text.startsWith("/") ? text : `/${text}`;
|
||||
}
|
||||
|
||||
async function getJson(path) {
|
||||
const response = await fetch(path, { headers: { accept: "application/json" } });
|
||||
const body = await response.json().catch(() => ({}));
|
||||
|
||||
Reference in New Issue
Block a user