import type { NetworkNode } from "@/api/client"; import { statusRGBA, rgbaToHex, type StatusColors } from "./graphColors"; export interface GraphNode { id: string; name: string; entry: NetworkNode; type: "self" | "peer" | "interface"; color: string; size: number; cluster?: string; x?: number; y?: number; z?: number; } export interface GraphLink { source: string; target: string; } export interface GraphData { nodes: GraphNode[]; links: GraphLink[]; clusterNames: string[]; } function findParentIface( entry: NetworkNode, interfaces: NetworkNode[], peerIndex: number, ): NetworkNode | undefined { if (entry.interface) { const iface = interfaces.find(i => i.name === entry.interface); if (iface) return iface; } if (interfaces.length > 0) return interfaces[peerIndex % interfaces.length]; return undefined; } export function buildGraphData( rawNodes: NetworkNode[], prevPositions: Map, theme: StatusColors, ): GraphData { const interfaces = rawNodes .filter(e => e.type === "interface") .sort((a, b) => a.name.localeCompare(b.name)); const selfNode = rawNodes.find(e => e.is_self && e.type !== "interface"); const peers = rawNodes.filter(e => !e.is_self && e.type !== "interface"); const nodes: GraphNode[] = []; const links: GraphLink[] = []; // Add interface nodes for (const iface of interfaces) { const prev = prevPositions.get(iface.hash); nodes.push({ id: iface.hash, name: iface.name, entry: iface, type: "interface", color: rgbaToHex(theme.stale), size: 2, ...(prev ? { x: prev.x, y: prev.y, z: prev.z } : {}), }); } // Add self node if (selfNode) { const prev = prevPositions.get(selfNode.hash); nodes.push({ id: selfNode.hash, name: selfNode.name, entry: selfNode, type: "self", color: "#ffffff", size: 2, ...(prev ? { x: prev.x, y: prev.y, z: prev.z } : {}), }); } // Add peer nodes + links to parent interface peers.forEach((peer, i) => { const prev = prevPositions.get(peer.hash); const parentIface = findParentIface(peer, interfaces, i); nodes.push({ id: peer.hash, name: peer.name, entry: peer, type: "peer", color: rgbaToHex(statusRGBA(peer, theme)), size: 2, cluster: parentIface?.name, ...(prev ? { x: prev.x, y: prev.y, z: prev.z } : {}), }); if (parentIface) { links.push({ source: parentIface.hash, target: peer.hash }); } }); const clusterNames = interfaces.map(i => i.name); return { nodes, links, clusterNames }; }