From 8029e506df9c57c9d3dcde008489b09d6b38b8df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Thu, 30 Jul 2026 15:24:49 +0000 Subject: [PATCH] probe the active headscale server automatically the dot showed amber for the active server until you pressed test, which read as a warning while every other section on the screen was loading fine. probe the active one on mount so the dot always states something checked. Co-Authored-By: Claude Opus 5 --- .../src/apps/Headscale/ServersView.tsx | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/workspaces/officerdev/src/apps/Headscale/ServersView.tsx b/src/workspaces/officerdev/src/apps/Headscale/ServersView.tsx index adca325e..4ea0c3b4 100644 --- a/src/workspaces/officerdev/src/apps/Headscale/ServersView.tsx +++ b/src/workspaces/officerdev/src/apps/Headscale/ServersView.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { Plus, Loader2, Server, Check, Activity, Pencil, Trash2 } from 'lucide-react'; import type { HeadscaleServer, HeadscaleHealth } from './shared'; import { MIN_HEADSCALE_VERSION } from './shared'; @@ -9,9 +9,12 @@ import { ServerForm } from './ServerForm'; // The servers section — register Headscale servers and switch between them. Exactly one is active at a // time (a DB invariant, not a UI convention), and every other section in this workspace reads it. // -// Health is probed on demand only. It costs two upstream round trips (an unauthenticated /version plus an -// authenticated call to prove the key still works), so polling every registered server would be rude to -// servers the owner isn't currently using. +// The ACTIVE server is probed once when this section opens; the others only when asked. A probe costs two +// upstream round trips (an unauthenticated /version plus an authenticated call to prove the key still +// works), which is worth spending on the one server everything else acts on, and rude to spend on servers +// the owner isn't currently using. The point of probing the active one automatically is that its dot then +// always states something — green or red — instead of an amber "haven't checked" that the working screen +// behind it already contradicts. function timeAgo(iso: string): string { const seconds = Math.round((Date.now() - new Date(iso).getTime()) / 1000); @@ -38,7 +41,8 @@ type ServerRowProps = { const ServerRow = ({ server, health, testing, busy, onActivate, onTest, onEdit, onRemove }: ServerRowProps) => { const [confirming, setConfirming] = useState(false); - // Untested servers get a neutral dot, not a green one: we only know the credentials worked at registration. + // Amber is only ever the in-flight state for the active server's own probe — an unprobed server is grey, + // because a green dot should mean "I checked", not "nothing has gone wrong that I noticed". const tone = health ? (health.ok ? 'ok' : 'bad') : server.isActive ? 'warn' : 'idle'; return ( @@ -132,6 +136,16 @@ export const ServersView = () => { } }; + // Probe the active server once per activation. Keyed on the id rather than a boolean so switching servers + // probes the new one, and re-rendering doesn't re-probe the same one. + const activeId = servers.find((s) => s.isActive)?.id ?? null; + const autoProbed = useRef(null); + useEffect(() => { + if (activeId === null || autoProbed.current === activeId) return; + autoProbed.current = activeId; + void test(activeId); + }, [activeId]); + const run = async (fn: () => Promise) => { setActionError(null); try {