fix: restore web sentinel recovery diagnostics (#980)

Co-authored-by: Codex <codex@noreply.local>
This commit is contained in:
Lyon
2026-06-26 14:04:37 +08:00
committed by GitHub
parent fc6d3bdaf9
commit bdec05729d
8 changed files with 409 additions and 161 deletions
@@ -1,4 +1,4 @@
// SPEC: PJ2026-01060508 Web哨兵 draft-2026-06-26-p7-web-probe-sentinel-dashboard.
// SPEC: PJ2026-01060508 Web哨兵 draft-2026-06-26-p8-web-probe-sentinel-recovery.
// Responsibility: Static dashboard shell and asset serving for the web-probe sentinel frontend.
import { readFileSync } from "node:fs";
import { rootPath } from "./config";
@@ -11,7 +11,7 @@ interface DashboardShellConfig {
}
const DASHBOARD_ASSET_ROOT = "scripts/assets/web-probe-sentinel-dashboard";
const DASHBOARD_CONTRACT_VERSION = "draft-2026-06-26-p7-web-probe-sentinel-dashboard";
const DASHBOARD_CONTRACT_VERSION = "draft-2026-06-26-p8-web-probe-sentinel-recovery";
export function renderWebProbeSentinelDashboardHtml(config: DashboardShellConfig): string {
const publicOrigin = stringOrNull(config.publicExposure.publicBaseUrl) ?? "";
@@ -20,7 +20,7 @@ export function renderWebProbeSentinelDashboardHtml(config: DashboardShellConfig
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>HWLAB Web Probe Sentinel</title>
<title>HWLAB Web哨兵</title>
<link rel="stylesheet" href="/dashboard/assets/dashboard.css">
</head>
<body>
@@ -33,58 +33,58 @@ export function renderWebProbeSentinelDashboardHtml(config: DashboardShellConfig
data-config-ready="${config.plan.ok ? "true" : "false"}"
data-contract-version="${DASHBOARD_CONTRACT_VERSION}"
>
<section class="sentinel-topbar" aria-label="Web probe sentinel overview">
<section class="sentinel-topbar" aria-label="Web哨兵概览">
<div class="sentinel-title">
<div class="sentinel-mark" aria-hidden="true"></div>
<div>
<h1>HWLAB Web Probe Sentinel</h1>
<h1>HWLAB Web哨兵</h1>
<p id="sentinel-subtitle">${escapeHtml(config.node)} / ${escapeHtml(config.lane)}</p>
</div>
</div>
<div class="sentinel-toolbar" aria-label="Dashboard controls">
<span id="status-pill" class="status-pill status-idle">idle</span>
<div class="sentinel-toolbar" aria-label="监控面板控制">
<span id="status-pill" class="status-pill status-idle">空闲</span>
<label class="refresh-toggle">
<input id="auto-refresh-enabled" type="checkbox">
<span>Auto refresh</span>
<span>自动刷新</span>
</label>
<select id="auto-refresh-interval" aria-label="Auto refresh interval">
<select id="auto-refresh-interval" aria-label="自动刷新间隔">
<option value="5">5s</option>
<option value="10">10s</option>
<option value="30">30s</option>
</select>
<button id="manual-refresh" class="icon-button" type="button" title="Refresh" aria-label="Refresh">
<span aria-hidden="true">Refresh</span>
<button id="manual-refresh" class="icon-button" type="button" title="刷新" aria-label="刷新">
<span aria-hidden="true">刷新</span>
</button>
</div>
</section>
<section id="loading-banner" class="banner banner-muted">Loading</section>
<section id="loading-banner" class="banner banner-muted">加载中</section>
<section id="error-banner" class="banner banner-danger" hidden></section>
<section class="metric-grid" aria-label="Sentinel status metrics">
<section class="metric-grid" aria-label="哨兵状态指标">
<article class="metric-card">
<span class="metric-label">Overall</span>
<span class="metric-label">总体状态</span>
<strong id="metric-overall">-</strong>
<small id="metric-origin">-</small>
</article>
<article class="metric-card">
<span class="metric-label">Latest run</span>
<span class="metric-label">最近运行</span>
<strong id="metric-latest-run">-</strong>
<small id="metric-latest-age">-</small>
</article>
<article class="metric-card">
<span class="metric-label">Findings</span>
<span class="metric-label">发现项</span>
<strong id="metric-findings">0</strong>
<small id="metric-findings-note">-</small>
</article>
<article class="metric-card">
<span class="metric-label">Scheduler</span>
<span class="metric-label">调度器</span>
<strong id="metric-scheduler">-</strong>
<small id="metric-budget">-</small>
</article>
</section>
<section class="overview-checks" aria-label="Sentinel health checks">
<section class="overview-checks" aria-label="哨兵健康检查">
<span id="check-config" class="check-chip">config -</span>
<span id="check-pvc" class="check-chip">pvc -</span>
<span id="check-analyzer" class="check-chip">analyzer -</span>
@@ -94,7 +94,7 @@ export function renderWebProbeSentinelDashboardHtml(config: DashboardShellConfig
<section class="panel timeline-panel" aria-labelledby="timeline-heading">
<div class="panel-header">
<h2 id="timeline-heading">Run Timeline</h2>
<h2 id="timeline-heading">运行时间线</h2>
<span id="timeline-count" class="panel-subtitle">-</span>
</div>
<div id="run-timeline" class="run-timeline"></div>
@@ -103,34 +103,34 @@ export function renderWebProbeSentinelDashboardHtml(config: DashboardShellConfig
<section class="dashboard-grid">
<section class="panel panel-wide" aria-labelledby="runs-heading">
<div class="panel-header">
<h2 id="runs-heading">Runs</h2>
<h2 id="runs-heading">运行历史</h2>
<span id="runs-count" class="panel-subtitle">-</span>
</div>
<form id="runs-filter" class="runs-filter">
<label>
<span>Status</span>
<span>状态</span>
<select id="filter-status" name="status">
<option value="">Any</option>
<option value="planned">planned</option>
<option value="running">running</option>
<option value="analyzed">analyzed</option>
<option value="blocked">blocked</option>
<option value="interrupted">interrupted</option>
<option value="">全部</option>
<option value="planned">已计划</option>
<option value="running">运行中</option>
<option value="analyzed">已分析</option>
<option value="blocked">阻塞</option>
<option value="interrupted">已中断</option>
</select>
</label>
<label>
<span>Severity</span>
<span>严重级别</span>
<select id="filter-severity" name="severity">
<option value="">Any</option>
<option value="red">red</option>
<option value="warning">warning</option>
<option value="info">info</option>
<option value="">全部</option>
<option value="red">红色</option>
<option value="warning">警告</option>
<option value="info">信息</option>
</select>
</label>
<label>
<span>Time</span>
<span>时间</span>
<select id="filter-window" name="window">
<option value="">All</option>
<option value="">全部</option>
<option value="1h">1h</option>
<option value="6h">6h</option>
<option value="24h">24h</option>
@@ -138,29 +138,29 @@ export function renderWebProbeSentinelDashboardHtml(config: DashboardShellConfig
</select>
</label>
<label>
<span>Sort</span>
<span>排序</span>
<select id="filter-sort" name="sort">
<option value="updated">updated</option>
<option value="created">created</option>
<option value="findings">findings</option>
<option value="severity">severity</option>
<option value="updated">按更新时间</option>
<option value="created">按创建时间</option>
<option value="findings">按发现数量</option>
<option value="severity">按严重级别</option>
</select>
</label>
<label class="filter-search">
<span>Search</span>
<input id="filter-search" name="search" type="search" placeholder="run, observer, report">
<span>搜索</span>
<input id="filter-search" name="search" type="search" placeholder="runobserverreport">
</label>
<button id="clear-filters" class="icon-button" type="button">Clear</button>
<button id="clear-filters" class="icon-button" type="button">清除</button>
</form>
<div class="table-frame">
<table class="runs-table">
<thead>
<tr>
<th>Run</th>
<th>Status</th>
<th>Scenario</th>
<th>Findings</th>
<th>Updated</th>
<th>运行</th>
<th>状态</th>
<th>场景</th>
<th>发现项</th>
<th>更新时间</th>
</tr>
</thead>
<tbody id="runs-body"></tbody>
@@ -170,38 +170,38 @@ export function renderWebProbeSentinelDashboardHtml(config: DashboardShellConfig
<section class="panel" aria-labelledby="findings-heading">
<div class="panel-header">
<h2 id="findings-heading">Findings</h2>
<h2 id="findings-heading">发现分析</h2>
<span id="findings-count" class="panel-subtitle">-</span>
</div>
<form id="findings-filter" class="findings-filter">
<label>
<span>Severity</span>
<span>严重级别</span>
<select id="finding-filter-severity" name="fseverity">
<option value="">Any</option>
<option value="red">red</option>
<option value="warning">warning</option>
<option value="info">info</option>
<option value="">全部</option>
<option value="red">红色</option>
<option value="warning">警告</option>
<option value="info">信息</option>
</select>
</label>
<label>
<span>Window</span>
<span>窗口</span>
<select id="finding-filter-window" name="fwindow">
<option value="24h">24h</option>
<option value="1h">1h</option>
<option value="6h">6h</option>
<option value="7d">7d</option>
<option value="">All</option>
<option value="">全部</option>
</select>
</label>
<label>
<span>Code</span>
<span>代码</span>
<input id="finding-filter-code" name="fcode" type="search" placeholder="finding code">
</label>
<label>
<span>Scenario</span>
<span>场景</span>
<input id="finding-filter-scenario" name="fscenario" type="search" placeholder="scenario">
</label>
<button id="finding-clear-filters" class="icon-button" type="button">Clear</button>
<button id="finding-clear-filters" class="icon-button" type="button">清除</button>
</form>
<div id="finding-aggregation" class="finding-aggregation"></div>
<div id="findings-list" class="finding-list"></div>
@@ -210,8 +210,8 @@ export function renderWebProbeSentinelDashboardHtml(config: DashboardShellConfig
<section class="panel detail-panel" aria-labelledby="detail-heading" role="dialog" aria-modal="false">
<div class="panel-header">
<h2 id="detail-heading">Run Detail</h2>
<span id="detail-subtitle" class="panel-subtitle">No run selected</span>
<h2 id="detail-heading">运行详情</h2>
<span id="detail-subtitle" class="panel-subtitle">未选择运行</span>
</div>
<div id="detail-content" class="detail-content"></div>
</section>