Files
sencho/backend/src/routes/dependencyMap.ts
T
Anso af4083175c feat(fleet): add read-only dependency map tab (#1324)
* feat(fleet): add read-only dependency map tab

Add a fleet-wide Dependencies tab to Fleet view that maps how stacks,
services, networks, volumes, and ports relate, with flags for missing
dependencies, port conflicts, orphaned resources, and cross-stack shared
resources. Read-only; filterable by stack, node, and flag; collapsed by
default with a list-view fallback at scale.

The graph is derived at request time from Docker and compose metadata, so
no new table or persisted state is introduced. A per-node graph endpoint
feeds a hub aggregation endpoint that fans out across the fleet and
degrades gracefully, surfacing unreachable or unparseable nodes inline
while the rest of the map still renders.

* fix(fleet): harden dependency map flag detection and remote merge

Address review findings on the dependency map:
- Port-conflict detection now does pairwise host-scope overlap, so an
  unrelated bind on the same port and protocol but a different specific host
  IP is no longer flagged, and the flag lands on the exact scoped port node.
- A running service's depends_on target is only considered satisfied when it
  is actually running, so a crashed (exited) dependency is surfaced while a
  deliberately stopped stack stays quiet.
- Declared external networks and volumes are reported missing when they do
  not exist on the host instead of being assumed present.
- The hub deep-validates each remote node-graph payload before merging, so a
  reachable-but-malformed remote degrades to a single node error rather than
  failing the whole fleet map, and the validation failure is logged.
- Searching or filtering on a network, volume, or port now also reveals the
  services that claim it and their stacks.
2026-06-06 11:18:36 -04:00

24 lines
1.0 KiB
TypeScript

import { Router, type Request, type Response } from 'express';
import { authMiddleware } from '../middleware/auth';
import { DatabaseService } from '../services/DatabaseService';
import { buildLocalGraph } from '../services/DependencyGraphService';
export const dependencyMapRouter = Router();
/**
* Per-node dependency graph. Auth-only (never tier-gated) so the hub's
* fleet-wide fan-out can reach this route on every node, including Community
* remotes. Served against the local Docker of whichever node handles it.
*/
dependencyMapRouter.get('/node-graph', authMiddleware, async (req: Request, res: Response): Promise<void> => {
try {
const nodeId = req.nodeId;
const name = DatabaseService.getInstance().getNodes().find((n) => n.id === nodeId)?.name ?? 'This node';
const graph = await buildLocalGraph(nodeId, name);
res.json(graph);
} catch (error) {
console.error('[DependencyMap] node-graph error:', error);
res.status(500).json({ error: 'Failed to build dependency graph' });
}
});