feat: show provider gateway versions in webui
This commit is contained in:
+29
-2
@@ -1,4 +1,4 @@
|
||||
import { mkdirSync } from "node:fs";
|
||||
import { mkdirSync, readFileSync } from "node:fs";
|
||||
import { connect } from "node:net";
|
||||
import { join } from "node:path";
|
||||
import { chromium } from "playwright";
|
||||
@@ -70,6 +70,20 @@ function addCheck(checks: E2ECheck[], name: string, passed: boolean, detail: unk
|
||||
checks.push({ name, status: passed ? "passed" : "failed", detail });
|
||||
}
|
||||
|
||||
function safeTestId(value: string): string {
|
||||
return value.replace(/[^a-zA-Z0-9_-]/g, "_");
|
||||
}
|
||||
|
||||
function providerGatewayPackageVersion(): string {
|
||||
try {
|
||||
const raw = readFileSync(rootPath("src", "components", "provider-gateway", "package.json"), "utf8");
|
||||
const parsed = JSON.parse(raw) as { version?: unknown };
|
||||
return typeof parsed.version === "string" ? parsed.version : "";
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function runPsql(config: UniDeskConfig, sql: string): { ok: boolean; stdout: string; stderr: string; exitCode: number | null } {
|
||||
const result = runCommand([
|
||||
"docker",
|
||||
@@ -209,13 +223,16 @@ async function serviceChecks(config: UniDeskConfig, urls: PublicUrls, checks: E2
|
||||
const dockerStatus = dockerCoreJson("/api/nodes/docker-status");
|
||||
const providerIngress = await fetchProbe(urls.providerIngressHealthUrl);
|
||||
const overviewBody = (coreOverview as { body?: { ok?: boolean; dbReady?: boolean; onlineNodeCount?: number } }).body;
|
||||
const nodeList = (coreNodes as { body?: { nodes?: Array<{ providerId?: string; status?: string }> } }).body?.nodes ?? [];
|
||||
const nodeList = (coreNodes as { body?: { nodes?: Array<{ providerId?: string; status?: string; labels?: Record<string, unknown> }> } }).body?.nodes ?? [];
|
||||
const mainNode = nodeList.find((node) => node.providerId === config.providerGateway.id);
|
||||
const expectedGatewayVersion = providerGatewayPackageVersion();
|
||||
const systemStatuses = (systemStatus as { body?: { systemStatuses?: Array<{ providerId?: string; current?: { cpu?: { percent?: number }; memory?: { percent?: number; mode?: string; cacheBytes?: number }; disk?: { percent?: number } }; history?: unknown[] }> } }).body?.systemStatuses ?? [];
|
||||
const mainSystem = systemStatuses.find((item) => item.providerId === config.providerGateway.id);
|
||||
const dockerStatuses = (dockerStatus as { body?: { dockerStatuses?: Array<{ providerId?: string; dockerStatus?: { counts?: { containers?: number }; containers?: unknown[] } }> } }).body?.dockerStatuses ?? [];
|
||||
const mainDocker = dockerStatuses.find((item) => item.providerId === config.providerGateway.id);
|
||||
addCheck(checks, "core:internal-overview", (coreOverview as { ok?: boolean }).ok === true && overviewBody?.ok === true && overviewBody.dbReady === true && (overviewBody.onlineNodeCount ?? 0) >= 1, coreOverview);
|
||||
addCheck(checks, "provider:self-node-online", nodeList.some((node) => node.providerId === config.providerGateway.id && node.status === "online"), coreNodes);
|
||||
addCheck(checks, "provider:gateway-version-label", mainNode?.labels?.providerGatewayVersion === expectedGatewayVersion && mainNode?.labels?.providerGatewayUpgradePolicy === "always-enabled", { providerId: config.providerGateway.id, expectedGatewayVersion, labels: mainNode?.labels ?? null });
|
||||
addCheck(checks, "provider:system-status", (systemStatus as { ok?: boolean }).ok === true && mainSystem?.current !== undefined && Number.isFinite(mainSystem.current.cpu?.percent) && Number.isFinite(mainSystem.current.memory?.percent) && mainSystem.current.memory?.mode === "actual_without_cache" && Number.isFinite(mainSystem.current.memory?.cacheBytes) && Number.isFinite(mainSystem.current.disk?.percent) && (mainSystem.history?.length ?? 0) > 0, systemStatusCheckDetail(systemStatus, config.providerGateway.id));
|
||||
addCheck(checks, "provider:docker-status", (dockerStatus as { ok?: boolean }).ok === true && mainDocker?.dockerStatus !== undefined && ((mainDocker.dockerStatus.counts?.containers ?? 0) > 0 || (mainDocker.dockerStatus.containers?.length ?? 0) > 0), dockerStatusCheckDetail(dockerStatus, config.providerGateway.id));
|
||||
const upgradeDispatch = dockerCoreJson("/api/dispatch", {
|
||||
@@ -353,6 +370,15 @@ async function frontendCheck(config: UniDeskConfig, urls: PublicUrls, checks: E2
|
||||
return text.includes("docker desktop 视图") && text.includes("containers") && text.includes("unidesk_pgdata_10gb");
|
||||
}, undefined, { timeout: 10000 });
|
||||
const dockerText = await page.locator('[data-testid="docker-status-page"]').innerText({ timeout: 5000 });
|
||||
await page.getByRole("button", { name: /网关版本/ }).click();
|
||||
await page.waitForSelector('[data-testid="gateway-version-page"]', { timeout: 10000 });
|
||||
await page.waitForSelector(`[data-testid="gateway-version-${safeTestId(config.providerGateway.id)}"]`, { timeout: 10000 });
|
||||
await page.waitForFunction(() => {
|
||||
const text = document.body.innerText.toLowerCase();
|
||||
return text.includes("provider gateway 版本") && text.includes("自动更新记录") && text.includes("provider.upgrade");
|
||||
}, undefined, { timeout: 10000 });
|
||||
const gatewayText = await page.locator('[data-testid="gateway-version-page"]').innerText({ timeout: 5000 });
|
||||
const gatewayTextLower = gatewayText.toLowerCase();
|
||||
addCheck(checks, "frontend:login-provider-visible", bodyText.includes(config.providerGateway.id) && bodyText.includes(config.providerGateway.name) && bodyText.includes("核心在线"), { screenshotPath });
|
||||
addCheck(checks, "frontend:public-provider-info-visible", publicFrontendReached && bodyText.includes(config.providerGateway.id) && bodyText.includes(config.providerGateway.name) && rawText.includes('"status": "online"') && rawText.includes(`"providerId": "${config.providerGateway.id}"`), { frontendUrl: urls.frontendUrl, landedUrl, providerId: config.providerGateway.id, rawTextPreview: rawText.slice(0, 400) });
|
||||
addCheck(checks, "frontend:mobile-nav-fixed-height", mobileRailMax - mobileRailMin <= 1 && mobileRailMax <= 44, { mobileRailHeights });
|
||||
@@ -364,6 +390,7 @@ async function frontendCheck(config: UniDeskConfig, urls: PublicUrls, checks: E2
|
||||
addCheck(checks, "frontend:system-monitor-visible", monitorText.includes("任务管理器视图") && monitorText.includes("CPU") && monitorText.includes("Memory") && monitorText.includes("Disk") && monitorText.includes("不含缓存"), { monitorTextPreview: monitorText.slice(0, 800) });
|
||||
addCheck(checks, "frontend:upgrade-plan-dispatch", upgradeControlText.includes("预检升级 已下发"), { providerId: config.providerGateway.id, upgradeControlPreview: upgradeControlText.slice(0, 500) });
|
||||
addCheck(checks, "frontend:docker-status-visible", dockerText.toLowerCase().includes("docker desktop 视图") && dockerText.toLowerCase().includes("containers") && dockerText.includes("unidesk_pgdata_10gb") && (dockerText.includes("unidesk-frontend") || dockerText.includes("unidesk-backend-core")), { dockerTextPreview: dockerText.slice(0, 800) });
|
||||
addCheck(checks, "frontend:gateway-version-records-visible", gatewayTextLower.includes("provider gateway 版本") && gatewayText.includes("自动更新记录") && gatewayText.includes(config.providerGateway.id) && gatewayText.includes(`v${providerGatewayPackageVersion()}`) && gatewayText.includes("provider.upgrade"), { gatewayTextPreview: gatewayText.slice(0, 900) });
|
||||
addCheck(checks, "frontend:no-console-errors", consoleErrors.length === 0, { consoleErrors });
|
||||
return { screenshotPath, bodyText, consoleErrors };
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user