fix: remote endpoint reach-gating + drag-to-connect resync race
- Mesh endpoints now gate the http port on `subdomain` (the registry's "exposed" signal), matching the local reach-gated derivation — so a remote reach:off service (e.g. primer's castle-gateway) no longer shows a phantom :9000. - The focus/resync effect no longer rebuilds the node set while a connection is being dragged; a background refetch mid-drag was silently cancelling the connection, so drag-to-expose/-require intermittently "did nothing".
This commit is contained in:
@@ -436,6 +436,9 @@ export function SystemMapPage() {
|
|||||||
const kindRef = useRef<Record<string, string>>({})
|
const kindRef = useRef<Record<string, string>>({})
|
||||||
const reachRef = useRef<Record<string, "internal" | "public">>({})
|
const reachRef = useRef<Record<string, "internal" | "public">>({})
|
||||||
const focusRef = useRef<string | null>(null)
|
const focusRef = useRef<string | null>(null)
|
||||||
|
// True while a connection is being dragged — the resync must not replace nodes
|
||||||
|
// mid-drag (a background refetch would otherwise silently abort the connection).
|
||||||
|
const connectingRef = useRef(false)
|
||||||
// Manual node positions (persisted), and the react-flow instance for fitView.
|
// Manual node positions (persisted), and the react-flow instance for fitView.
|
||||||
const posRef = useRef<PosMap | null>(null)
|
const posRef = useRef<PosMap | null>(null)
|
||||||
if (posRef.current == null) posRef.current = loadPositions()
|
if (posRef.current == null) posRef.current = loadPositions()
|
||||||
@@ -923,6 +926,10 @@ export function SystemMapPage() {
|
|||||||
kindRef.current = built.kindOf
|
kindRef.current = built.kindOf
|
||||||
reachRef.current = built.reachOf
|
reachRef.current = built.reachOf
|
||||||
focusRef.current = focus
|
focusRef.current = focus
|
||||||
|
// Don't rebuild the node/edge set while a connection is being dragged — replacing
|
||||||
|
// the source node mid-drag silently cancels the connection (drag-to-expose "does
|
||||||
|
// nothing"). The mutation that follows the drop re-syncs us anyway.
|
||||||
|
if (connectingRef.current) return
|
||||||
setNodes(materialize(built))
|
setNodes(materialize(built))
|
||||||
// External consumption is drawn only while the consumer is selected — a dashed
|
// External consumption is drawn only while the consumer is selected — a dashed
|
||||||
// line to each of its reference nodes (kept off-canvas otherwise to avoid a
|
// line to each of its reference nodes (kept off-canvas otherwise to avoid a
|
||||||
@@ -1041,6 +1048,15 @@ export function SystemMapPage() {
|
|||||||
[applyReach, addDep],
|
[applyReach, addDep],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const onConnectStart = useCallback(() => {
|
||||||
|
connectingRef.current = true
|
||||||
|
}, [])
|
||||||
|
// Re-sync on connect end (a refetch may have been skipped mid-drag).
|
||||||
|
const onConnectEnd = useCallback(() => {
|
||||||
|
connectingRef.current = false
|
||||||
|
setNodes((ns) => [...ns])
|
||||||
|
}, [setNodes])
|
||||||
|
|
||||||
// Click a dashed amber suggestion to accept it → declares the requires.
|
// Click a dashed amber suggestion to accept it → declares the requires.
|
||||||
const onEdgeClick = useCallback(
|
const onEdgeClick = useCallback(
|
||||||
(_e: React.MouseEvent, edge: Edge) => {
|
(_e: React.MouseEvent, edge: Edge) => {
|
||||||
@@ -1211,6 +1227,8 @@ export function SystemMapPage() {
|
|||||||
onNodesChange={onNodesChange}
|
onNodesChange={onNodesChange}
|
||||||
onEdgesChange={onEdgesChange}
|
onEdgesChange={onEdgesChange}
|
||||||
onConnect={onConnect}
|
onConnect={onConnect}
|
||||||
|
onConnectStart={onConnectStart}
|
||||||
|
onConnectEnd={onConnectEnd}
|
||||||
onEdgeClick={onEdgeClick}
|
onEdgeClick={onEdgeClick}
|
||||||
onNodeDragStop={onNodeDragStop}
|
onNodeDragStop={onNodeDragStop}
|
||||||
onEdgesDelete={onEdgesDelete}
|
onEdgesDelete={onEdgesDelete}
|
||||||
|
|||||||
@@ -97,10 +97,13 @@ _TCP_PROTOCOL = {5432: "pg", 7687: "bolt", 1883: "mqtt", 6379: "redis"}
|
|||||||
|
|
||||||
|
|
||||||
def _endpoints_of_registry(d: object) -> list[dict]:
|
def _endpoints_of_registry(d: object) -> list[dict]:
|
||||||
"""Derive display endpoints from a registry deployment (mirrors relations)."""
|
"""Derive display endpoints from a registry deployment. Mirrors the local
|
||||||
|
relations derivation, which gates the http endpoint on being exposed — here the
|
||||||
|
registry's `subdomain` is that signal (a reach:off service has none). Without
|
||||||
|
this, remote reach:off services show a phantom port (e.g. castle-gateway :9000)."""
|
||||||
eps: list[dict] = []
|
eps: list[dict] = []
|
||||||
port = getattr(d, "port", None)
|
port = getattr(d, "port", None)
|
||||||
if port is not None:
|
if port is not None and getattr(d, "subdomain", None):
|
||||||
eps.append({"protocol": "http", "port": port})
|
eps.append({"protocol": "http", "port": port})
|
||||||
tcp = getattr(d, "tcp_port", None)
|
tcp = getattr(d, "tcp_port", None)
|
||||||
if tcp is not None:
|
if tcp is not None:
|
||||||
|
|||||||
Reference in New Issue
Block a user