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 <noreply@anthropic.com>
This commit is contained in:
@@ -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 { Plus, Loader2, Server, Check, Activity, Pencil, Trash2 } from 'lucide-react';
|
||||||
import type { HeadscaleServer, HeadscaleHealth } from './shared';
|
import type { HeadscaleServer, HeadscaleHealth } from './shared';
|
||||||
import { MIN_HEADSCALE_VERSION } 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
|
// 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.
|
// 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
|
// The ACTIVE server is probed once when this section opens; the others only when asked. A probe costs two
|
||||||
// authenticated call to prove the key still works), so polling every registered server would be rude to
|
// upstream round trips (an unauthenticated /version plus an authenticated call to prove the key still
|
||||||
// servers the owner isn't currently using.
|
// 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 {
|
function timeAgo(iso: string): string {
|
||||||
const seconds = Math.round((Date.now() - new Date(iso).getTime()) / 1000);
|
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 ServerRow = ({ server, health, testing, busy, onActivate, onTest, onEdit, onRemove }: ServerRowProps) => {
|
||||||
const [confirming, setConfirming] = useState(false);
|
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';
|
const tone = health ? (health.ok ? 'ok' : 'bad') : server.isActive ? 'warn' : 'idle';
|
||||||
|
|
||||||
return (
|
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<number | null>(null);
|
||||||
|
useEffect(() => {
|
||||||
|
if (activeId === null || autoProbed.current === activeId) return;
|
||||||
|
autoProbed.current = activeId;
|
||||||
|
void test(activeId);
|
||||||
|
}, [activeId]);
|
||||||
|
|
||||||
const run = async (fn: () => Promise<unknown>) => {
|
const run = async (fn: () => Promise<unknown>) => {
|
||||||
setActionError(null);
|
setActionError(null);
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user