mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-24 08:57:25 +00:00
fix(mesh): treat local-node aliases as always reachable in diagnostics (#994)
MeshService.getRouteDiagnostic called PilotTunnelManager.hasActiveTunnel unconditionally for the alias's target node. Local nodes never establish pilot tunnels because they do not need them (mesh same-node traffic uses the localhost fast path), so the call always returned false and the diagnostic flipped to state: 'tunnel down' even on a working local route. The route detail sheet and node card consequently rendered every local mesh alias as a destructive 'tunnel down' pill. Added a private isMeshReachable helper that returns true for any local-type node and otherwise delegates to hasActiveTunnel. Used it in both getRouteDiagnostic and getStatus, replacing the inline ternary in getStatus that already had the right shape but lived as duplicated logic. The helper falls back to false when the node row is missing (orphaned alias from a partial cascade), matching the pre-existing conservative behavior. Verified live: GET /api/mesh/aliases/echo.audit-mesh-prod.Local.sencho/diagnostic previously returned state='tunnel down' on a healthy local route.
This commit is contained in:
@@ -669,6 +669,22 @@ export class MeshService extends EventEmitter {
|
||||
|
||||
// --- Diagnostics ---
|
||||
|
||||
/**
|
||||
* Whether mesh traffic to this node can flow. Local nodes are always
|
||||
* reachable because mesh uses the same-node fast path on localhost.
|
||||
* Remote nodes are reachable only when a pilot tunnel is registered. The
|
||||
* literal `hasActiveTunnel(localNodeId)` would always be false (local
|
||||
* nodes do not establish tunnels to themselves), so a direct call would
|
||||
* render every local alias as `tunnel down` in the UI even on a working
|
||||
* route.
|
||||
*/
|
||||
private isMeshReachable(nodeId: number): boolean {
|
||||
const node = DatabaseService.getInstance().getNode(nodeId);
|
||||
if (!node) return false;
|
||||
if (node.type !== 'remote') return true;
|
||||
return PilotTunnelManager.getInstance().hasActiveTunnel(nodeId);
|
||||
}
|
||||
|
||||
public async getRouteDiagnostic(alias: string): Promise<MeshRouteDiagnostic> {
|
||||
const target = this.lookupAliasGlobal(alias);
|
||||
const lastError = this.routeErrorMap.get(alias) || null;
|
||||
@@ -678,8 +694,7 @@ export class MeshService extends EventEmitter {
|
||||
return { alias, target: null, pilot: { connected: false, lastSeen: null }, lastError, lastProbeMs, state: 'not authorized' };
|
||||
}
|
||||
|
||||
const ptm = PilotTunnelManager.getInstance();
|
||||
const pilotConnected = ptm.hasActiveTunnel(target.nodeId);
|
||||
const pilotConnected = this.isMeshReachable(target.nodeId);
|
||||
const node = DatabaseService.getInstance().getNode(target.nodeId);
|
||||
const lastSeen = node?.pilot_last_seen ?? null;
|
||||
const optedIn = DatabaseService.getInstance().isMeshStackEnabled(target.nodeId, target.stackName);
|
||||
@@ -739,7 +754,6 @@ export class MeshService extends EventEmitter {
|
||||
|
||||
public async getStatus(): Promise<MeshNodeStatus[]> {
|
||||
const db = DatabaseService.getInstance();
|
||||
const ptm = PilotTunnelManager.getInstance();
|
||||
const nodes = db.getNodes();
|
||||
const out: MeshNodeStatus[] = [];
|
||||
for (const node of nodes) {
|
||||
@@ -749,7 +763,7 @@ export class MeshService extends EventEmitter {
|
||||
nodeName: node.name,
|
||||
enabled: db.getNodeMeshEnabled(node.id),
|
||||
sidecarRunning: await this.isSidecarRunning(node.id),
|
||||
pilotConnected: node.type === 'remote' ? ptm.hasActiveTunnel(node.id) : true,
|
||||
pilotConnected: this.isMeshReachable(node.id),
|
||||
optedInStacks,
|
||||
activeStreamCount: Array.from(this.activeStreams.values()).length,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user