100 lines
4.5 KiB
TypeScript
100 lines
4.5 KiB
TypeScript
import type { Node, Edge } from '@xyflow/react'
|
|
import type { DeviceNodeData } from '@/components/network/nodes/DeviceNode'
|
|
import type { GroupNodeData } from '@/types/network-diagram'
|
|
|
|
// Maps our device slugs to draw.io Cisco stencil shape styles
|
|
const SLUG_TO_DRAWIO_STYLE: Record<string, string> = {
|
|
'router': 'shape=mxgraph.cisco.routers.router;',
|
|
'switch': 'shape=mxgraph.cisco.switches.layer_3_switch;',
|
|
'access-point': 'shape=mxgraph.cisco.misc.access_point;',
|
|
'load-balancer': 'shape=mxgraph.cisco.misc.generic_building;',
|
|
'firewall': 'shape=mxgraph.cisco.firewalls.firewall;',
|
|
'badge-reader': 'shape=mxgraph.cisco.misc.generic_building;',
|
|
'server': 'shape=mxgraph.cisco.servers.standard_server;',
|
|
'vm': 'shape=mxgraph.cisco.servers.standard_server;',
|
|
'container': 'shape=mxgraph.cisco.servers.standard_server;',
|
|
'nas': 'shape=mxgraph.cisco.storage.tape_storage_library;',
|
|
'san': 'shape=mxgraph.cisco.storage.tape_storage_library;',
|
|
'cloud-storage': 'shape=mxgraph.cisco.misc.cloud;',
|
|
'cloud': 'shape=mxgraph.cisco.misc.cloud;',
|
|
'aws': 'shape=mxgraph.cisco.misc.cloud;',
|
|
'azure': 'shape=mxgraph.cisco.misc.cloud;',
|
|
'gcp': 'shape=mxgraph.cisco.misc.cloud;',
|
|
'isp': 'shape=mxgraph.cisco.misc.cloud;',
|
|
'workstation': 'shape=mxgraph.cisco.computers_and_peripherals.pc;',
|
|
'laptop': 'shape=mxgraph.cisco.computers_and_peripherals.laptop;',
|
|
'tablet': 'shape=mxgraph.cisco.computers_and_peripherals.laptop;',
|
|
'phone': 'shape=mxgraph.cisco.computers_and_peripherals.ip_phone;',
|
|
'printer': 'shape=mxgraph.cisco.computers_and_peripherals.printer;',
|
|
'ups': 'shape=mxgraph.cisco.misc.generic_building;',
|
|
'pdu': 'shape=mxgraph.cisco.misc.generic_building;',
|
|
'rack': 'shape=mxgraph.cisco.misc.generic_building;',
|
|
'patch-panel': 'shape=mxgraph.cisco.misc.generic_building;',
|
|
'camera': 'shape=mxgraph.cisco.misc.generic_building;',
|
|
'nvr': 'shape=mxgraph.cisco.misc.generic_building;',
|
|
'iot': 'shape=mxgraph.cisco.misc.generic_building;',
|
|
}
|
|
|
|
const BASE_NODE_STYLE =
|
|
'sketch=0;html=1;pointerEvents=1;dashed=0;fillColor=#036897;strokeColor=#ffffff;strokeWidth=2;verticalLabelPosition=bottom;verticalAlign=top;align=center;outlineConnect=0;'
|
|
const GROUP_STYLE =
|
|
'swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;collapsible=0;marginBottom=0;swimlaneHead=0;fillColor=none;'
|
|
|
|
function esc(s: string): string {
|
|
return s
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"')
|
|
}
|
|
|
|
export function exportToDrawio(nodes: Node[], edges: Edge[]): string {
|
|
const cells: string[] = [
|
|
'<mxCell id="0"/>',
|
|
'<mxCell id="1" parent="0"/>',
|
|
]
|
|
|
|
for (const node of nodes) {
|
|
const w = typeof node.style?.width === 'number' ? node.style.width : (node.measured?.width ?? 120)
|
|
const h = typeof node.style?.height === 'number' ? node.style.height : (node.measured?.height ?? 120)
|
|
const x = node.position.x
|
|
const y = node.position.y
|
|
const parentId = node.parentId ?? '1'
|
|
|
|
if (node.type === 'group') {
|
|
const gd = node.data as GroupNodeData
|
|
cells.push(
|
|
`<mxCell id="${esc(node.id)}" value="${esc(gd.label ?? '')}" style="${GROUP_STYLE}" vertex="1" parent="${esc(parentId)}">` +
|
|
`<mxGeometry x="${x}" y="${y}" width="${w}" height="${h}" as="geometry"/>` +
|
|
`</mxCell>`,
|
|
)
|
|
} else {
|
|
const dd = node.data as DeviceNodeData
|
|
const slug = dd.deviceType ?? 'server'
|
|
const shapeStyle = SLUG_TO_DRAWIO_STYLE[slug] ?? 'rounded=1;whiteSpace=wrap;html=1;'
|
|
cells.push(
|
|
`<mxCell id="${esc(node.id)}" value="${esc(dd.label ?? '')}" style="${shapeStyle}${BASE_NODE_STYLE}" vertex="1" parent="${esc(parentId)}">` +
|
|
`<mxGeometry x="${x}" y="${y}" width="${w}" height="${h}" as="geometry"/>` +
|
|
`</mxCell>`,
|
|
)
|
|
}
|
|
}
|
|
|
|
for (const edge of edges) {
|
|
const label = typeof edge.label === 'string' ? edge.label : ''
|
|
cells.push(
|
|
`<mxCell id="${esc(edge.id)}" value="${esc(label)}" style="edgeStyle=orthogonalEdgeStyle;html=1;" edge="1" source="${esc(edge.source)}" target="${esc(edge.target)}" parent="1">` +
|
|
`<mxGeometry relative="1" as="geometry"/>` +
|
|
`</mxCell>`,
|
|
)
|
|
}
|
|
|
|
const xml =
|
|
`<?xml version="1.0" encoding="UTF-8"?>\n` +
|
|
`<mxGraphModel><root>\n` +
|
|
cells.join('\n') +
|
|
`\n</root></mxGraphModel>`
|
|
|
|
return xml
|
|
}
|