feat: clarify WebVNC portal failure states

This commit is contained in:
Peter Steinberger 2026-05-07 14:13:26 +01:00
parent 4adbfc6d4a
commit 5ed32f1bd0
No known key found for this signature in database
3 changed files with 13 additions and 11 deletions

View File

@ -1373,9 +1373,9 @@ export class FleetDurableObject implements DurableObject {
events: this.recentWebVNCEvents(lease.id),
message: bridgeConnected
? viewerConnected
? "bridge connected; another viewer is active"
? "WebVNC viewer already active; close stale WebVNC tabs or run reset"
: "bridge connected"
: `no bridge connected; run: ${command}`,
: `WebVNC daemon not running; run: ${command}`,
});
}
@ -1830,7 +1830,7 @@ export class FleetDurableObject implements DurableObject {
return json(
{
error: "webvnc_bridge_missing",
message: `start the bridge with: ${command}`,
message: `WebVNC daemon not running; start the bridge with: ${command}`,
command,
},
{ status: 409 },
@ -1842,7 +1842,7 @@ export class FleetDurableObject implements DurableObject {
return json(
{
error: "webvnc_viewer_active",
message: `another viewer is active; close stale WebVNC tabs or wait before reconnecting with: ${command}`,
message: `WebVNC viewer already active; close stale WebVNC tabs or run reset before reconnecting with: ${command}`,
command,
},
{ status: 409 },

View File

@ -614,11 +614,11 @@ export function portalVNC(lease: LeaseRecord): Response {
try {
const state = await bridgeState();
if (state && !state.bridgeConnected) {
scheduleRetry(state.message || "no bridge connected; run the bridge command below");
scheduleRetry(state.message || "WebVNC daemon not running; run the bridge command below");
return;
}
if (state?.viewerConnected) {
scheduleRetry("another viewer is connected; close stale WebVNC tabs");
scheduleRetry("WebVNC viewer already active; close stale WebVNC tabs or run reset");
return;
}
setStatus(retryAttempt ? "bridge connected; opening viewer" : "connecting");
@ -641,7 +641,7 @@ export function portalVNC(lease: LeaseRecord): Response {
}
});
rfb.addEventListener("disconnect", () => {
scheduleRetry(connected ? "bridge disconnected" : "waiting for bridge");
scheduleRetry(connected ? "VNC bridge disconnected" : "waiting for VNC bridge");
});
rfb.addEventListener("credentialsrequired", (event) => {
const types = event.detail?.types || ["password"];
@ -657,7 +657,7 @@ export function portalVNC(lease: LeaseRecord): Response {
rfb.addEventListener("securityfailure", () => {
stopped = true;
window.clearTimeout(retryTimer);
setStatus("VNC authentication failed", "bad");
setStatus("VNC authentication failed; reopen WebVNC or copy the password from crabbox webvnc status", "bad");
});
} catch (error) {
scheduleRetry(error instanceof Error ? error.message : String(error));

View File

@ -1668,8 +1668,10 @@ describe("fleet lease identity and idle", () => {
expect(pageBody).toContain("position:sticky");
expect(pageBody).toContain('data-provider="hetzner"');
expect(pageBody).toContain('data-target="linux"');
expect(pageBody).toContain("no bridge connected; run the bridge command below");
expect(pageBody).toContain("another viewer is connected; close stale WebVNC tabs");
expect(pageBody).toContain("WebVNC daemon not running; run the bridge command below");
expect(pageBody).toContain(
"WebVNC viewer already active; close stale WebVNC tabs or run reset",
);
expect(pageBody).toContain('fragment.get("username")');
expect(pageBody).toContain('types.includes("username")');
expect(pageBody).not.toContain("cdn.jsdelivr.net");
@ -1686,7 +1688,7 @@ describe("fleet lease identity and idle", () => {
command: "crabbox webvnc --provider hetzner --target linux --id blue-lobster --open",
events: [],
message:
"no bridge connected; run: crabbox webvnc --provider hetzner --target linux --id blue-lobster --open",
"WebVNC daemon not running; run: crabbox webvnc --provider hetzner --target linux --id blue-lobster --open",
});
const apiStatus = await fleet.fetch(