feat: proxmox import diagnostics, node style fix, and cluster edges
Scan History: - Proxmox is now a first-class scan kind (badge, filter chip, Server icon, completion toast) instead of being mislabeled as an IP scan. - A done run carrying a non-fatal advisory renders amber (info) with a warning toast, distinct from red failures. Import diagnostics: - test-connection probes /access/permissions and warns when the API token has no ACL (VMs/LXC would be invisible) — points at the PVEAuditor grant. - import surfaces an advisory when hosts import but no guests are visible (privilege-separated token whose rights are the intersection with the user), rather than a silent "done". Node style: - Proxmox container mode is now opt-in (container_mode === true), matching the rest of the codebase (App.tsx nesting logic). Imported proxmox nodes leave the flag unset and render like a manually-created node instead of an empty group container. Cluster edges: - Hosts from one import are chained with 'cluster' edges via left/right handles, distinct from the vertical host->guest 'virtual' edges. Wired for both the direct "Add to Canvas" path and the pending -> approve path (host<->host proxmox_cluster links, resolved to cluster edges on approve; cluster hosts get left/right handles). Tests added on both sides. ha-relevant: maybe
This commit is contained in:
@@ -47,6 +47,7 @@ import type { NodeData, EdgeData, CustomStyleDef, FloorMapConfig, NodeType } fro
|
||||
import type { ZigbeeNode, ZigbeeEdge } from '@/components/zigbee/types'
|
||||
import type { ZwaveNode, ZwaveEdge } from '@/components/zwave/types'
|
||||
import type { ProxmoxNode, ProxmoxEdge } from '@/components/proxmox/types'
|
||||
import { buildProxmoxClusterEdges } from '@/components/proxmox/clusterEdges'
|
||||
|
||||
const STANDALONE = import.meta.env.VITE_STANDALONE === 'true'
|
||||
|
||||
@@ -650,10 +651,16 @@ export default function App() {
|
||||
const cols = Math.min(COLS, pmNodes.length)
|
||||
const rows = Math.ceil(pmNodes.length / COLS)
|
||||
const origin = getCenteredPosition(cols * SPACING_X, rows * SPACING_Y)
|
||||
// Multiple hosts from one import = a cluster → chain them via left/right
|
||||
// 'cluster' edges. Those endpoints need one left + one right handle each
|
||||
// (both default to 0), so grant them to the host nodes up front.
|
||||
const clusterEdges = buildProxmoxClusterEdges(pmNodes)
|
||||
const cluster = clusterEdges.length > 0
|
||||
pmNodes.forEach((pn, i) => {
|
||||
const col = i % COLS
|
||||
const row = Math.floor(i / COLS)
|
||||
const position = { x: origin.x + col * SPACING_X, y: origin.y + row * SPACING_Y }
|
||||
const isClusterHost = cluster && pn.type === 'proxmox'
|
||||
const newNode: import('@xyflow/react').Node<NodeData> = {
|
||||
id: pn.id,
|
||||
type: pn.type,
|
||||
@@ -665,6 +672,7 @@ export default function App() {
|
||||
services: [],
|
||||
...(pn.ip ? { ip: pn.ip } : {}),
|
||||
...(pn.hostname ? { hostname: pn.hostname } : {}),
|
||||
...(isClusterHost ? { left_handles: 1, right_handles: 1 } : {}),
|
||||
},
|
||||
}
|
||||
addNode(newNode)
|
||||
@@ -679,6 +687,16 @@ export default function App() {
|
||||
type: 'virtual',
|
||||
} as unknown as import('@xyflow/react').Connection)
|
||||
})
|
||||
// Host ↔ host links render as 'cluster' edges (left → right chain).
|
||||
clusterEdges.forEach((ce) => {
|
||||
onConnect({
|
||||
source: ce.source,
|
||||
sourceHandle: ce.sourceHandle,
|
||||
target: ce.target,
|
||||
targetHandle: ce.targetHandle,
|
||||
type: 'cluster',
|
||||
} as unknown as import('@xyflow/react').Connection)
|
||||
})
|
||||
const importedIds = new Set(pmNodes.map((pn) => pn.id))
|
||||
useCanvasStore.setState((state) => ({
|
||||
nodes: state.nodes.map((n) => ({ ...n, selected: importedIds.has(n.id) })),
|
||||
|
||||
Reference in New Issue
Block a user