mirror of
https://github.com/jcreek/OpenNetworkDiagram.git
synced 2026-07-14 03:23:44 +00:00
feat(*): Add machine notes and MAC address fields
This commit is contained in:
@@ -5,10 +5,12 @@
|
|||||||
"ipAddress": "192.168.1.10",
|
"ipAddress": "192.168.1.10",
|
||||||
"role": "Hypervisor",
|
"role": "Hypervisor",
|
||||||
"operatingSystem": "Linux",
|
"operatingSystem": "Linux",
|
||||||
|
"notes": "Optional notes",
|
||||||
"ports": [
|
"ports": [
|
||||||
{
|
{
|
||||||
"portName": "eth0",
|
"portName": "eth0",
|
||||||
"speedGbps": 1,
|
"speedGbps": 1,
|
||||||
|
"macAddress": "aa:bb:cc:dd:ee:01",
|
||||||
"connectedTo": {
|
"connectedTo": {
|
||||||
"device": "Example Switch",
|
"device": "Example Switch",
|
||||||
"port": "1"
|
"port": "1"
|
||||||
@@ -20,7 +22,8 @@
|
|||||||
{
|
{
|
||||||
"name": "Example VM",
|
"name": "Example VM",
|
||||||
"role": "Service",
|
"role": "Service",
|
||||||
"ipAddress": "192.168.1.20"
|
"ipAddress": "192.168.1.20",
|
||||||
|
"macAddress": "aa:bb:cc:dd:ee:02"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1931,6 +1931,21 @@
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
<label>
|
||||||
|
Notes
|
||||||
|
<textarea
|
||||||
|
value={selectedMachine.notes ?? ''}
|
||||||
|
on:input={(event) =>
|
||||||
|
mutateDraft(
|
||||||
|
(draft) => {
|
||||||
|
draft.machines[selectedTarget.index].notes = (
|
||||||
|
event.currentTarget as HTMLTextAreaElement
|
||||||
|
).value;
|
||||||
|
},
|
||||||
|
{ autosave: true }
|
||||||
|
)}
|
||||||
|
></textarea>
|
||||||
|
</label>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="edit-section">
|
<section class="edit-section">
|
||||||
@@ -2037,6 +2052,15 @@
|
|||||||
on:input={() => mutateDraft(() => undefined, { autosave: true })}
|
on:input={() => mutateDraft(() => undefined, { autosave: true })}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
<label>
|
||||||
|
MAC Address
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="aa:bb:cc:dd:ee:ff"
|
||||||
|
bind:value={vm.macAddress}
|
||||||
|
on:input={() => mutateDraft(() => undefined, { autosave: true })}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
<label>
|
<label>
|
||||||
Icon
|
Icon
|
||||||
<input
|
<input
|
||||||
@@ -2100,6 +2124,15 @@
|
|||||||
bind:value={port.speedGbps}
|
bind:value={port.speedGbps}
|
||||||
on:input={() => mutateDraft(() => undefined, { autosave: true })}
|
on:input={() => mutateDraft(() => undefined, { autosave: true })}
|
||||||
/>
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
MAC Address
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="aa:bb:cc:dd:ee:ff"
|
||||||
|
bind:value={port.macAddress}
|
||||||
|
on:input={() => mutateDraft(() => undefined, { autosave: true })}
|
||||||
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
Connect To Device
|
Connect To Device
|
||||||
@@ -2264,6 +2297,15 @@
|
|||||||
bind:value={port.speedGbps}
|
bind:value={port.speedGbps}
|
||||||
on:input={() => mutateDraft(() => undefined, { autosave: true })}
|
on:input={() => mutateDraft(() => undefined, { autosave: true })}
|
||||||
/>
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
MAC Address
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="aa:bb:cc:dd:ee:ff"
|
||||||
|
bind:value={port.macAddress}
|
||||||
|
on:input={() => mutateDraft(() => undefined, { autosave: true })}
|
||||||
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
Connect To Device
|
Connect To Device
|
||||||
@@ -2374,6 +2416,23 @@
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
<label>
|
||||||
|
MAC Address
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="aa:bb:cc:dd:ee:ff"
|
||||||
|
value={selectedVm.macAddress ?? ''}
|
||||||
|
on:input={(event) =>
|
||||||
|
mutateDraft(
|
||||||
|
(draft) => {
|
||||||
|
draft.machines[selectedTarget.machineIndex].software.vms[
|
||||||
|
selectedTarget.vmIndex
|
||||||
|
].macAddress = (event.currentTarget as HTMLInputElement).value;
|
||||||
|
},
|
||||||
|
{ autosave: true }
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
</section>
|
</section>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button
|
<button
|
||||||
@@ -2454,6 +2513,10 @@
|
|||||||
Operating System
|
Operating System
|
||||||
<input type="text" bind:value={newMachineDraft.operatingSystem} />
|
<input type="text" bind:value={newMachineDraft.operatingSystem} />
|
||||||
</label>
|
</label>
|
||||||
|
<label>
|
||||||
|
Notes
|
||||||
|
<textarea bind:value={newMachineDraft.notes}></textarea>
|
||||||
|
</label>
|
||||||
</section>
|
</section>
|
||||||
{:else if addModalKind === 'device'}
|
{:else if addModalKind === 'device'}
|
||||||
<section class="edit-section">
|
<section class="edit-section">
|
||||||
|
|||||||
@@ -110,7 +110,8 @@ export default function transformNetworkDataToGraph(data: NetworkData): GraphTra
|
|||||||
const machineVms = sourceMachineVms.map((vm) => ({
|
const machineVms = sourceMachineVms.map((vm) => ({
|
||||||
name: vm.name,
|
name: vm.name,
|
||||||
ip: vm.ipAddress,
|
ip: vm.ipAddress,
|
||||||
role: vm.role
|
role: vm.role,
|
||||||
|
macAddress: vm.macAddress
|
||||||
}));
|
}));
|
||||||
|
|
||||||
nodes.push({
|
nodes.push({
|
||||||
@@ -133,6 +134,7 @@ export default function transformNetworkDataToGraph(data: NetworkData): GraphTra
|
|||||||
cpu: machine.hardware?.cpu ?? 'Unknown',
|
cpu: machine.hardware?.cpu ?? 'Unknown',
|
||||||
ram: machine.hardware?.ram ?? 'Unknown',
|
ram: machine.hardware?.ram ?? 'Unknown',
|
||||||
gpu: machine.hardware?.gpu ?? undefined,
|
gpu: machine.hardware?.gpu ?? undefined,
|
||||||
|
notes: machine.notes,
|
||||||
ports: machinePortsList,
|
ports: machinePortsList,
|
||||||
vmCount: machineVms.length,
|
vmCount: machineVms.length,
|
||||||
vms: machineVms
|
vms: machineVms
|
||||||
@@ -166,6 +168,7 @@ export default function transformNetworkDataToGraph(data: NetworkData): GraphTra
|
|||||||
iconKey: vm.iconKey,
|
iconKey: vm.iconKey,
|
||||||
ip: vm.ipAddress,
|
ip: vm.ipAddress,
|
||||||
role: vm.role,
|
role: vm.role,
|
||||||
|
macAddress: vm.macAddress,
|
||||||
hostName: machine.machineName
|
hostName: machine.machineName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export type GraphEdgeKind = 'physical' | 'hosting';
|
|||||||
export interface PortDetails {
|
export interface PortDetails {
|
||||||
portName: string;
|
portName: string;
|
||||||
speedGbps?: number;
|
speedGbps?: number;
|
||||||
|
macAddress?: string;
|
||||||
connectedTo?: {
|
connectedTo?: {
|
||||||
device: string;
|
device: string;
|
||||||
port: string;
|
port: string;
|
||||||
@@ -20,12 +21,14 @@ export interface MachineDetails {
|
|||||||
cpu: string;
|
cpu: string;
|
||||||
ram: string;
|
ram: string;
|
||||||
gpu?: string;
|
gpu?: string;
|
||||||
|
notes?: string;
|
||||||
ports: PortDetails[];
|
ports: PortDetails[];
|
||||||
vmCount: number;
|
vmCount: number;
|
||||||
vms: Array<{
|
vms: Array<{
|
||||||
name: string;
|
name: string;
|
||||||
ip: string;
|
ip: string;
|
||||||
role: string;
|
role: string;
|
||||||
|
macAddress?: string;
|
||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,6 +48,7 @@ export interface VmDetails {
|
|||||||
iconKey?: string;
|
iconKey?: string;
|
||||||
ip: string;
|
ip: string;
|
||||||
role: string;
|
role: string;
|
||||||
|
macAddress?: string;
|
||||||
hostName: string;
|
hostName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
/**
|
/**
|
||||||
* @typedef {{ path: string; message: string }} ValidationIssue
|
* @typedef {{ path: string; message: string }} ValidationIssue
|
||||||
* @typedef {{ device: string; port: string }} ConnectedTo
|
* @typedef {{ device: string; port: string }} ConnectedTo
|
||||||
* @typedef {{ portName: string; speedGbps?: number; connectedTo?: ConnectedTo }} Port
|
* @typedef {{ portName: string; speedGbps?: number; macAddress?: string; connectedTo?: ConnectedTo }} Port
|
||||||
* @typedef {{ name: string; role: string; ipAddress: string; iconKey?: string }} VM
|
* @typedef {{ name: string; role: string; ipAddress: string; iconKey?: string; macAddress?: string }} VM
|
||||||
* @typedef {{ cpu: string; ram: string; networkPorts: number; networkPortSpeedGbps?: number; gpu?: string }} Hardware
|
* @typedef {{ cpu: string; ram: string; networkPorts: number; networkPortSpeedGbps?: number; gpu?: string }} Hardware
|
||||||
* @typedef {{ machineName: string; ipAddress: string; role: string; operatingSystem: string; iconKey?: string; software: { vms: VM[] }; hardware: Hardware; ports?: Port[] }} Machine
|
* @typedef {{ machineName: string; ipAddress: string; role: string; operatingSystem: string; iconKey?: string; notes?: string; software: { vms: VM[] }; hardware: Hardware; ports?: Port[] }} Machine
|
||||||
* @typedef {{ name: string; ipAddress: string; type: string; iconKey?: string; notes?: string; ports?: Port[] }} NetworkDevice
|
* @typedef {{ name: string; ipAddress: string; type: string; iconKey?: string; notes?: string; ports?: Port[] }} NetworkDevice
|
||||||
* @typedef {{ machines: Machine[]; devices: NetworkDevice[] }} NetworkData
|
* @typedef {{ machines: Machine[]; devices: NetworkDevice[] }} NetworkData
|
||||||
* @typedef {{ valid: boolean; errors: ValidationIssue[]; data?: NetworkData }} ValidationResult
|
* @typedef {{ valid: boolean; errors: ValidationIssue[]; data?: NetworkData }} ValidationResult
|
||||||
@@ -105,6 +105,10 @@ export function normalizePort(issues, path, value, ownerLabel) {
|
|||||||
optional: true,
|
optional: true,
|
||||||
min: 0
|
min: 0
|
||||||
});
|
});
|
||||||
|
const macAddress = readString(issues, `${path}.macAddress`, value.macAddress, {
|
||||||
|
optional: true,
|
||||||
|
allowEmpty: true
|
||||||
|
});
|
||||||
|
|
||||||
let connectedTo;
|
let connectedTo;
|
||||||
if (value.connectedTo !== undefined) {
|
if (value.connectedTo !== undefined) {
|
||||||
@@ -126,6 +130,7 @@ export function normalizePort(issues, path, value, ownerLabel) {
|
|||||||
return {
|
return {
|
||||||
portName,
|
portName,
|
||||||
...(typeof speedGbps === 'number' ? { speedGbps } : {}),
|
...(typeof speedGbps === 'number' ? { speedGbps } : {}),
|
||||||
|
...(macAddress ? { macAddress } : {}),
|
||||||
...(connectedTo ? { connectedTo } : {})
|
...(connectedTo ? { connectedTo } : {})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -166,6 +171,10 @@ export function normalizeVm(issues, path, value) {
|
|||||||
const role = readString(issues, `${path}.role`, value.role);
|
const role = readString(issues, `${path}.role`, value.role);
|
||||||
const ipAddress = readString(issues, `${path}.ipAddress`, value.ipAddress);
|
const ipAddress = readString(issues, `${path}.ipAddress`, value.ipAddress);
|
||||||
const iconKey = readString(issues, `${path}.iconKey`, value.iconKey, { optional: true });
|
const iconKey = readString(issues, `${path}.iconKey`, value.iconKey, { optional: true });
|
||||||
|
const macAddress = readString(issues, `${path}.macAddress`, value.macAddress, {
|
||||||
|
optional: true,
|
||||||
|
allowEmpty: true
|
||||||
|
});
|
||||||
|
|
||||||
if (!name || !role || !ipAddress) {
|
if (!name || !role || !ipAddress) {
|
||||||
return null;
|
return null;
|
||||||
@@ -175,7 +184,8 @@ export function normalizeVm(issues, path, value) {
|
|||||||
name,
|
name,
|
||||||
role,
|
role,
|
||||||
ipAddress,
|
ipAddress,
|
||||||
...(iconKey ? { iconKey } : {})
|
...(iconKey ? { iconKey } : {}),
|
||||||
|
...(macAddress ? { macAddress } : {})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,6 +206,7 @@ function normalizeMachine(issues, path, value) {
|
|||||||
const role = readString(issues, `${path}.role`, value.role);
|
const role = readString(issues, `${path}.role`, value.role);
|
||||||
const operatingSystem = readString(issues, `${path}.operatingSystem`, value.operatingSystem);
|
const operatingSystem = readString(issues, `${path}.operatingSystem`, value.operatingSystem);
|
||||||
const iconKey = readString(issues, `${path}.iconKey`, value.iconKey, { optional: true });
|
const iconKey = readString(issues, `${path}.iconKey`, value.iconKey, { optional: true });
|
||||||
|
const notes = readString(issues, `${path}.notes`, value.notes, { optional: true, allowEmpty: true });
|
||||||
|
|
||||||
const softwareRaw = value.software;
|
const softwareRaw = value.software;
|
||||||
if (!isRecord(softwareRaw)) {
|
if (!isRecord(softwareRaw)) {
|
||||||
@@ -287,6 +298,7 @@ function normalizeMachine(issues, path, value) {
|
|||||||
role,
|
role,
|
||||||
operatingSystem,
|
operatingSystem,
|
||||||
...(iconKey ? { iconKey } : {}),
|
...(iconKey ? { iconKey } : {}),
|
||||||
|
...(notes !== undefined ? { notes } : {}),
|
||||||
software: { vms },
|
software: { vms },
|
||||||
hardware: {
|
hardware: {
|
||||||
cpu: hardware.cpu,
|
cpu: hardware.cpu,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export interface ConnectedPortRef {
|
|||||||
export interface Port {
|
export interface Port {
|
||||||
portName: string;
|
portName: string;
|
||||||
speedGbps?: number; // default to 1 if not provided
|
speedGbps?: number; // default to 1 if not provided
|
||||||
|
macAddress?: string;
|
||||||
connectedTo?: ConnectedPortRef;
|
connectedTo?: ConnectedPortRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ export interface VM {
|
|||||||
role: string;
|
role: string;
|
||||||
ipAddress: string;
|
ipAddress: string;
|
||||||
iconKey?: string;
|
iconKey?: string;
|
||||||
|
macAddress?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Software {
|
export interface Software {
|
||||||
@@ -34,6 +36,7 @@ export interface Machine {
|
|||||||
role: string;
|
role: string;
|
||||||
operatingSystem: string;
|
operatingSystem: string;
|
||||||
iconKey?: string;
|
iconKey?: string;
|
||||||
|
notes?: string;
|
||||||
software: Software;
|
software: Software;
|
||||||
hardware: Hardware;
|
hardware: Hardware;
|
||||||
ports?: Port[];
|
ports?: Port[];
|
||||||
|
|||||||
@@ -6,12 +6,14 @@
|
|||||||
"role": "Hypervisor",
|
"role": "Hypervisor",
|
||||||
"operatingSystem": "Proxmox",
|
"operatingSystem": "Proxmox",
|
||||||
"iconKey": "homarr:proxmox",
|
"iconKey": "homarr:proxmox",
|
||||||
|
"notes": "Primary router box; do not power off without failover in place.",
|
||||||
"software": {
|
"software": {
|
||||||
"vms": [
|
"vms": [
|
||||||
{
|
{
|
||||||
"name": "OpnSense",
|
"name": "OpnSense",
|
||||||
"role": "Router/Firewall/Gateway (DHCP 10.0.0.100-254)",
|
"role": "Router/Firewall/Gateway (DHCP 10.0.0.100-254)",
|
||||||
"ipAddress": "10.0.0.1"
|
"ipAddress": "10.0.0.1",
|
||||||
|
"macAddress": "bc:24:11:5a:2e:01"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "TPLink Omada Controller",
|
"name": "TPLink Omada Controller",
|
||||||
@@ -50,6 +52,7 @@
|
|||||||
{
|
{
|
||||||
"portName": "eth0",
|
"portName": "eth0",
|
||||||
"speedGbps": 1,
|
"speedGbps": 1,
|
||||||
|
"macAddress": "84:47:09:1c:aa:10",
|
||||||
"connectedTo": {
|
"connectedTo": {
|
||||||
"device": "Gigabit Switch",
|
"device": "Gigabit Switch",
|
||||||
"port": "1"
|
"port": "1"
|
||||||
|
|||||||
Reference in New Issue
Block a user