feat(mesh): refine the Routing tab and add per-route removal (#1311)

* feat(mesh): refine the Routing tab and add per-route removal

Tighten the Fleet > Routing tab so its node cards and diagnostics read
honestly for proxy-connected fleets, and remove a couple of dead ends.

- Node cards drop the "pilot connected/offline" line, which was meaningless
  for nodes that connect over the HTTP API proxy. The compact card drops the
  matching agent cell and now reads stacks / aliases / bridge.
- Enabling mesh on a proxy node shows a transient "Connecting" state and
  settles to meshed on its own, instead of flashing "Degraded" with a manual
  refresh while the bridge finishes dialing.
- The alias detail sheet gains a "Remove from mesh" action that opts the
  alias's owning stack out (the confirmation says how many aliases that
  drops), and a "Topology" tab. The standalone topology sheet and its
  opt-in-sheet shortcut are removed in favor of the tab.
- The "Add stack" affordance stays reachable on a meshed node, so a node
  that already has aliases is no longer a dead end.
- Diagnostics and the alias detail sheet show a transport-aware line
  ("local", "API proxy bridge", or "Pilot tunnel" with its state) instead of
  a fixed "Pilot tunnel" label.

Adds unit coverage for the new state derivation, transport descriptor, the
enable auto-converge, and the admin-gated removal, and updates the mesh docs.

* fix(mesh): harden Routing tab toggle and alias sheet against async races

Independent review surfaced two narrow async races in the new code.

- RoutingNodeCard: cancel the converge re-poll batch at the start of any
  toggle (so a slow disable can't let a prior enable's re-polls fire), and
  guard the toast, refresh, timer scheduling, and setToggling behind a mounted
  ref so nothing runs after the card unmounts while the enable POST is still
  in flight.
- MeshRouteDetailSheet: re-check the cancelled flag after reading the
  diagnostic body, not just before it. Reading the body is itself async, so a
  superseded alias's diagnostic could otherwise call setDiag and expose Remove
  for the wrong stack.

Adds tests for unmount-before-enable-resolves and the deferred-diagnostic
alias switch.
This commit is contained in:
Anso
2026-06-04 21:45:53 -04:00
committed by GitHub
parent 9d3055049f
commit 81858a0bb0
16 changed files with 731 additions and 256 deletions
@@ -5,7 +5,7 @@ import { SystemSheet } from '@/components/ui/system-sheet';
import { Button } from '@/components/ui/button';
import { ConfirmModal } from '@/components/ui/modal';
import type { MeshStackEntry } from '@/types/mesh';
import { Loader2, Workflow } from 'lucide-react';
import { Loader2 } from 'lucide-react';
interface Props {
open: boolean;
@@ -13,12 +13,11 @@ interface Props {
nodeId: number;
nodeName: string;
onChanged: () => void;
onViewTopology?: (stack: string) => void;
/** Opt-in/out is admin-only on the backend; non-admins see the list read-only. */
canManage: boolean;
}
export function MeshOptInSheet({ open, onOpenChange, nodeId, nodeName, onChanged, onViewTopology, canManage }: Props) {
export function MeshOptInSheet({ open, onOpenChange, nodeId, nodeName, onChanged, canManage }: Props) {
const [stacks, setStacks] = useState<MeshStackEntry[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
@@ -125,16 +124,6 @@ export function MeshOptInSheet({ open, onOpenChange, nodeId, nodeName, onChanged
<Loader2 className="w-4 h-4 animate-spin text-stat-subtitle" />
) : (
<div className="flex items-center gap-2">
{stack.optedIn && onViewTopology && (
<Button
size="sm"
variant="ghost"
onClick={() => onViewTopology(stack.name)}
aria-label={`View topology for ${stack.name}`}
>
<Workflow className="w-3 h-3 mr-1" /> Topology
</Button>
)}
{canManage && (
<Button
size="sm"