fix: separate sentinel check counts from samples

This commit is contained in:
Codex
2026-06-28 08:05:40 +00:00
parent 72cbcc21f2
commit 4c7c103905
3 changed files with 126 additions and 40 deletions
+60 -23
View File
@@ -2227,9 +2227,13 @@ if (captureScreenshot && screenshotPath) {
});
}
const dom = await page.evaluate(() => {
const dom = await page.evaluate(async () => {
const visible = (element) => Boolean(element && !element.hidden);
const text = (selector) => String(document.querySelector(selector)?.textContent || "").replace(/\s+/g, " ").trim();
const numberValue = (value) => Number.isFinite(Number(value)) ? Number(value) : 0;
const errorSampleCount = (counts) => numberValue(counts?.red) + numberValue(counts?.critical) + numberValue(counts?.error);
const warningSampleCount = (counts) => numberValue(counts?.warning) + numberValue(counts?.warn) + numberValue(counts?.amber);
const allSampleCount = (counts) => Object.values(counts || {}).reduce((sum, value) => sum + numberValue(value), 0);
const root = document.querySelector("#monitor-web-root");
const shell = document.querySelector("[data-monitor-shell='true']");
const error = document.querySelector("#monitor-web-error");
@@ -2262,6 +2266,35 @@ const dom = await page.evaluate(() => {
chartCounts.ok = typeof chartCounts.error === "number" && typeof chartCounts.warning === "number" && typeof chartCounts.total === "number"
? chartCounts.total === chartCounts.error + chartCounts.warning
: false;
const runsPayload = await fetch("/api/runs?limit=30&sort=updated", { cache: "no-store" }).then((item) => item.json()).catch(() => null);
const overviewPayload = await fetch("/api/overview", { cache: "no-store" }).then((item) => item.json()).catch(() => null);
const runs = Array.isArray(runsPayload?.runs) ? runsPayload.runs : Array.isArray(runsPayload?.items) ? runsPayload.items : [];
const latestRun = runs[0] || null;
const latestCounts = latestRun && latestRun.severityCounts && typeof latestRun.severityCounts === "object" && !Array.isArray(latestRun.severityCounts)
? latestRun.severityCounts
: {};
const latestRunCounts = {
runId: latestRun?.id || latestRun?.runId || null,
typeCount: numberValue(latestRun?.findingTypeCount ?? latestRun?.findingCount ?? latestRun?.finding_count),
error: errorSampleCount(latestCounts),
warning: warningSampleCount(latestCounts),
total: errorSampleCount(latestCounts) + warningSampleCount(latestCounts),
allSamples: allSampleCount(latestCounts),
};
const overviewCounts = overviewPayload?.severityCounts && typeof overviewPayload.severityCounts === "object" && !Array.isArray(overviewPayload.severityCounts)
? overviewPayload.severityCounts
: {};
const overviewSamples = {
error: errorSampleCount(overviewCounts),
warning: warningSampleCount(overviewCounts),
total: errorSampleCount(overviewCounts) + warningSampleCount(overviewCounts),
allSamples: allSampleCount(overviewCounts),
};
chartCounts.matchesLatestRun = chartCounts.ok === true
&& chartCounts.error === latestRunCounts.error
&& chartCounts.warning === latestRunCounts.warning
&& chartCounts.total === latestRunCounts.total;
const statusText = text(".status-strip");
const doc = document.documentElement;
const body = document.body;
const viewport = { width: window.innerWidth, height: window.innerHeight };
@@ -2280,8 +2313,7 @@ const dom = await page.evaluate(() => {
if (overflow.length < 5) {
overflow.push({
tag: element.tagName.toLowerCase(),
className: String(element.className || "").slice(0, 80),
text: String(element.textContent || "").replace(/\s+/g, " ").trim().slice(0, 80),
className: String(element.className || "").slice(0, 40),
x: Math.round(rect.x),
y: Math.round(rect.y),
width: Math.round(rect.width),
@@ -2309,17 +2341,19 @@ const dom = await page.evaluate(() => {
summaryText: text(".status-strip"),
runRows: document.querySelectorAll(".run-list .run-row").length,
findingItems: document.querySelectorAll(".finding-list .finding-card").length,
firstCards: cards,
badCardTitleCount: badCardTitles.length,
badCardBodyCount: badCardBodies.length,
badCardTitles,
badCardBodies,
trendCurve: Boolean(trend),
trendDotCount: document.querySelectorAll(".trend-dot-hit").length,
trendTooltip: tooltipSummary(trendTooltip),
trendPanelText: text("#trend-heading"),
legendTexts,
chartCounts,
latestRunCounts,
overviewSamples,
scopeLabels: {
latestPointLegend: legendTexts.some((item) => item.includes("最新点")),
historicalSamples: legendTexts.some((item) => item.includes("历史样本累计")) || statusText.includes("历史错误样本"),
},
timelineItems: document.querySelectorAll(".timeline-list .timeline-item").length,
timelineVisible: Boolean(timeline),
errorVisible: visible(error),
@@ -2327,20 +2361,6 @@ const dom = await page.evaluate(() => {
scrollModel: {
workspace: Boolean(workspace),
paneCount: panes.length,
panes: panes.map((pane) => {
const style = window.getComputedStyle(pane);
const rect = pane.getBoundingClientRect();
return {
className: String(pane.className || ""),
overflowY: style.overflowY,
scrollHeight: pane.scrollHeight,
clientHeight: pane.clientHeight,
x: Math.round(rect.x),
y: Math.round(rect.y),
width: Math.round(rect.width),
height: Math.round(rect.height),
};
}),
independentScroll: panes.length >= 3 && panes.every((pane) => {
const style = window.getComputedStyle(pane);
return style.overflowY === "auto" || style.overflowY === "scroll";
@@ -2352,7 +2372,7 @@ const dom = await page.evaluate(() => {
documentSize,
horizontalOverflow: documentSize.width > viewport.width + 1,
overflowCount,
overflow,
overflow: overflow.slice(0, 2),
},
};
@@ -2407,6 +2427,9 @@ const ok = !navigationError
&& dom.errorVisible !== true
&& dom.trendCurve === true
&& dom.chartCounts?.ok === true
&& dom.chartCounts?.matchesLatestRun === true
&& dom.scopeLabels?.latestPointLegend === true
&& dom.scopeLabels?.historicalSamples === true
&& (dom.trendDotCount === 0 || (dom.trendTooltip?.visible === true && dom.trendTooltip?.hasValues === true && dom.trendTooltip?.hasTime === true))
&& dom.badCardTitleCount === 0
&& dom.badCardBodyCount === 0
@@ -4703,6 +4726,8 @@ function renderDashboardResult(result: Record<string, unknown>): string {
const dataset = record(dom.dataset);
const layout = record(dom.layout);
const chartCounts = record(dom.chartCounts);
const latestRunCounts = record(dom.latestRunCounts);
const overviewSamples = record(dom.overviewSamples);
const screenshot = record(result.screenshot);
const remote = record(result.remote);
const transport = record(result.transport);
@@ -4725,15 +4750,27 @@ function renderDashboardResult(result: Record<string, unknown>): string {
"",
table(["TITLE", "STATUS_TEXT", "CONTRACT", "BASE_PATH"], [[dom.title, dom.statusText, dataset.contractVersion, dataset.basePath ?? "-"]]),
"",
table(["TREND_ERROR", "TREND_WARNING", "TREND_TOTAL", "TREND_EXACT", "BAD_TITLE", "BAD_BODY"], [[
table(["TREND_ERROR", "TREND_WARNING", "TREND_TOTAL", "TREND_EXACT", "MATCH_LATEST", "BAD_TITLE", "BAD_BODY"], [[
chartCounts.error ?? "-",
chartCounts.warning ?? "-",
chartCounts.total ?? "-",
chartCounts.ok ?? "-",
chartCounts.matchesLatestRun ?? "-",
dom.badCardTitleCount ?? "-",
dom.badCardBodyCount ?? "-",
]]),
"",
table(["LATEST_RUN", "TYPE_COUNT", "LATEST_ERR", "LATEST_WARN", "LATEST_TOTAL", "LATEST_ALL", "HIST_ERR", "HIST_WARN"], [[
latestRunCounts.runId ?? "-",
latestRunCounts.typeCount ?? "-",
latestRunCounts.error ?? "-",
latestRunCounts.warning ?? "-",
latestRunCounts.total ?? "-",
latestRunCounts.allSamples ?? "-",
overviewSamples.error ?? "-",
overviewSamples.warning ?? "-",
]]),
"",
table(["VIEWPORT", "DOC", "H_OVERFLOW", "OVERFLOW_COUNT"], [[
result.viewport,
`${record(layout.documentSize).width ?? "-"}x${record(layout.documentSize).height ?? "-"}`,
+17 -2
View File
@@ -888,6 +888,9 @@ function dashboardRunSummary(config: WebProbeSentinelServiceConfig, db: Database
const id = stringOrNull(row.id);
const severityCounts = id === null ? {} : severityCountsForRun(config, db, id);
const maxSeverity = maxSeverityFromCounts(severityCounts);
const findingTypeCount = numberOr(row.finding_count, 0);
const findingSampleCount = Object.values(severityCounts).reduce((sum, value) => sum + numberOr(value, 0), 0);
const findingAlertSampleCount = alertSeveritySampleCount(severityCounts);
return {
id,
runId: id,
@@ -903,8 +906,11 @@ function dashboardRunSummary(config: WebProbeSentinelServiceConfig, db: Database
stateDir: stringOrNull(row.state_dir),
report_json_sha256: stringOrNull(row.report_json_sha256),
reportJsonSha256: stringOrNull(row.report_json_sha256),
finding_count: numberOr(row.finding_count, 0),
findingCount: numberOr(row.finding_count, 0),
finding_count: findingTypeCount,
findingCount: findingTypeCount,
findingTypeCount,
findingSampleCount,
findingAlertSampleCount,
artifact_count: numberOr(row.artifact_count, 0),
artifactCount: numberOr(row.artifact_count, 0),
maintenance: numberOr(row.maintenance, 0) === 1,
@@ -921,6 +927,15 @@ function dashboardRunSummary(config: WebProbeSentinelServiceConfig, db: Database
};
}
function alertSeveritySampleCount(counts: Record<string, number>): number {
return numberOr(counts.red, 0)
+ numberOr(counts.critical, 0)
+ numberOr(counts.error, 0)
+ numberOr(counts.warning, 0)
+ numberOr(counts.warn, 0)
+ numberOr(counts.amber, 0);
}
function dashboardOverallStatus(health: Record<string, unknown>, latestRun: Record<string, unknown> | null, severityCounts: Record<string, number>): string {
if (health.ok !== true) return "degraded";
const latestStatus = latestRun === null ? null : stringOrNull(latestRun.status);