Improve microservice health availability
This commit is contained in:
@@ -619,6 +619,10 @@ function OverviewPage({ data, onRaw, onNavigate }: AnyRecord) {
|
||||
const pendingCount = overview.pendingTaskCount ?? pendingTasks.length;
|
||||
const recentTasks = data.tasks.slice(0, 5);
|
||||
const pgdata = overview.pgdata || {};
|
||||
const microserviceAvailability = overview.microserviceAvailability || {};
|
||||
const microserviceTotal = asNumber(microserviceAvailability.totalCount);
|
||||
const microserviceHealthy = asNumber(microserviceAvailability.healthyCount);
|
||||
const microserviceUnhealthy = asNumber(microserviceAvailability.unhealthyCount);
|
||||
return h("div", { className: "page-grid overview-grid", "data-testid": "overview-page" },
|
||||
h(Panel, { title: "核心指标", eyebrow: "Control" },
|
||||
h("div", { className: "metric-grid" },
|
||||
@@ -626,6 +630,7 @@ function OverviewPage({ data, onRaw, onNavigate }: AnyRecord) {
|
||||
h(MetricCard, { label: "PGDATA", value: fmtBytes(pgdata.databaseBytes), hint: `${pgdata.volumeName || "unidesk_pgdata_10gb"} / ${pgdata.databasePretty || "--"}`, tone: "ok", testId: "pgdata-usage-card" }),
|
||||
h(MetricCard, { label: "在线节点", value: overview.onlineNodeCount ?? 0, hint: `${overview.nodeCount ?? 0} registered`, tone: "ok" }),
|
||||
h(MetricCard, { label: "WebSocket", value: overview.activeSocketCount ?? 0, hint: "Provider ingress sockets" }),
|
||||
h(MetricCard, { label: "用户服务可用", value: microserviceTotal > 0 ? `${microserviceHealthy}/${microserviceTotal}` : "--", hint: microserviceTotal > 0 ? `healthyCount ${microserviceHealthy} · unhealthyCount ${microserviceUnhealthy}` : "strict /health probes", tone: microserviceTotal > 0 && microserviceUnhealthy === 0 ? "ok" : "warn", testId: "microservice-availability-card" }),
|
||||
h(MetricCard, { label: "待处理任务", value: pendingCount, hint: pendingCount > 0 ? "点击查看具体任务" : `timeout ${fmtDuration(Math.floor((overview.taskPendingTimeoutMs ?? 0) / 1000))}`, tone: pendingCount > 0 ? "warn" : "ok", onClick: () => onNavigate("tasks", "pending"), testId: "pending-task-card" }),
|
||||
),
|
||||
),
|
||||
@@ -1608,6 +1613,8 @@ function MicroserviceCatalogPage({ microservices, onRaw, onNavigate }: AnyRecord
|
||||
const runtime = microserviceRuntime(service);
|
||||
const repository = microserviceRepository(service);
|
||||
const backend = microserviceBackend(service);
|
||||
const availability = runtime.availability || {};
|
||||
const availabilityStatus = availability.status || (runtime.providerStatus === "online" ? "unknown" : "unhealthy");
|
||||
return h("tr", { key: service.id, "data-testid": `microservice-row-${safeId(service.id)}` },
|
||||
h("td", null, h("strong", null, service.name), h("code", null, service.id)),
|
||||
h("td", null, h("strong", null, runtime.providerName || service.providerId), h("code", null, service.providerId)),
|
||||
@@ -1618,7 +1625,11 @@ function MicroserviceCatalogPage({ microservices, onRaw, onNavigate }: AnyRecord
|
||||
h("code", null, `${backend.nodeBindHost || "--"}:${backend.nodePort || "--"} -> ${backend.proxyMode || "--"}`),
|
||||
),
|
||||
h("td", null, h("span", null, service.development?.sshPassthrough ? "SSH 透传" : "未配置"), h("code", null, service.development?.worktreePath || "--")),
|
||||
h("td", null, h(StatusBadge, { status: runtime.providerStatus === "online" ? "online" : "warn" }, runtime.providerStatus || "unknown"), h(DataSummary, { data: runtime.container, empty: "容器快照未上报" })),
|
||||
h("td", null,
|
||||
h(StatusBadge, { status: availabilityStatus === "healthy" ? "online" : availabilityStatus === "unknown" ? "warn" : "failed" }, availabilityStatus),
|
||||
h("span", null, availability.reason || runtime.providerStatus || "unknown"),
|
||||
h(DataSummary, { data: runtime.container, empty: "容器快照未上报" }),
|
||||
),
|
||||
h("td", null,
|
||||
h("div", { className: "microservice-actions" },
|
||||
service.id === "findjob" ? h("button", { type: "button", className: "ghost-btn", onClick: () => onNavigate("apps", "findjob"), "data-testid": "open-findjob-button" }, "打开") : null,
|
||||
|
||||
@@ -22,6 +22,16 @@ async function requestJson(path: string, options: AnyRecord = {}): Promise<any>
|
||||
return requestUniDeskJson(path, { failureFields: ["ok", "success"], ...options });
|
||||
}
|
||||
|
||||
async function requestHealthJson(path: string): Promise<any> {
|
||||
const response = await fetch(path, { credentials: "same-origin" });
|
||||
const text = await response.text();
|
||||
try {
|
||||
return text ? JSON.parse(text) : { ok: response.ok, status: response.status };
|
||||
} catch {
|
||||
return { ok: response.ok, status: response.status, text };
|
||||
}
|
||||
}
|
||||
|
||||
function StatusBadge({ status, children }: AnyRecord) {
|
||||
const normalized = String(status || "unknown").toLowerCase();
|
||||
return h("span", { className: `status-badge ${normalized}` }, children || status || "unknown");
|
||||
@@ -119,7 +129,7 @@ export function ClaudeQqPage({ microservices, onRaw, apiBaseUrl = "/api" }: AnyR
|
||||
setState((prev: any) => ({ ...prev, loading: true, error: "" }));
|
||||
try {
|
||||
const [health, status, events, subscriptions, sent] = await Promise.all([
|
||||
requestJson(`${apiBaseUrl}/microservices/claudeqq/health`),
|
||||
requestHealthJson(`${apiBaseUrl}/microservices/claudeqq/health`),
|
||||
requestJson(claudeqqApi(apiBaseUrl, "/api/server/status")),
|
||||
requestJson(claudeqqApi(apiBaseUrl, "/api/events/recent?limit=60")),
|
||||
requestJson(claudeqqApi(apiBaseUrl, "/api/events/subscriptions")),
|
||||
|
||||
Reference in New Issue
Block a user