feat(*): Add machine notes and MAC address fields

This commit is contained in:
Josh Creek
2026-07-12 20:19:19 +01:00
parent 9cea45945c
commit 336ee5e91b
7 changed files with 98 additions and 7 deletions
+4 -1
View File
@@ -5,10 +5,12 @@
"ipAddress": "192.168.1.10",
"role": "Hypervisor",
"operatingSystem": "Linux",
"notes": "Optional notes",
"ports": [
{
"portName": "eth0",
"speedGbps": 1,
"macAddress": "aa:bb:cc:dd:ee:01",
"connectedTo": {
"device": "Example Switch",
"port": "1"
@@ -20,7 +22,8 @@
{
"name": "Example VM",
"role": "Service",
"ipAddress": "192.168.1.20"
"ipAddress": "192.168.1.20",
"macAddress": "aa:bb:cc:dd:ee:02"
}
]
},
+63
View File
@@ -1931,6 +1931,21 @@
)}
/>
</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 class="edit-section">
@@ -2037,6 +2052,15 @@
on:input={() => mutateDraft(() => undefined, { autosave: true })}
/>
</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>
Icon
<input
@@ -2100,6 +2124,15 @@
bind:value={port.speedGbps}
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>
Connect To Device
@@ -2264,6 +2297,15 @@
bind:value={port.speedGbps}
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>
Connect To Device
@@ -2374,6 +2416,23 @@
)}
/>
</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>
<div class="modal-footer">
<button
@@ -2454,6 +2513,10 @@
Operating System
<input type="text" bind:value={newMachineDraft.operatingSystem} />
</label>
<label>
Notes
<textarea bind:value={newMachineDraft.notes}></textarea>
</label>
</section>
{:else if addModalKind === 'device'}
<section class="edit-section">
+4 -1
View File
@@ -110,7 +110,8 @@ export default function transformNetworkDataToGraph(data: NetworkData): GraphTra
const machineVms = sourceMachineVms.map((vm) => ({
name: vm.name,
ip: vm.ipAddress,
role: vm.role
role: vm.role,
macAddress: vm.macAddress
}));
nodes.push({
@@ -133,6 +134,7 @@ export default function transformNetworkDataToGraph(data: NetworkData): GraphTra
cpu: machine.hardware?.cpu ?? 'Unknown',
ram: machine.hardware?.ram ?? 'Unknown',
gpu: machine.hardware?.gpu ?? undefined,
notes: machine.notes,
ports: machinePortsList,
vmCount: machineVms.length,
vms: machineVms
@@ -166,6 +168,7 @@ export default function transformNetworkDataToGraph(data: NetworkData): GraphTra
iconKey: vm.iconKey,
ip: vm.ipAddress,
role: vm.role,
macAddress: vm.macAddress,
hostName: machine.machineName
}
}
+4
View File
@@ -4,6 +4,7 @@ export type GraphEdgeKind = 'physical' | 'hosting';
export interface PortDetails {
portName: string;
speedGbps?: number;
macAddress?: string;
connectedTo?: {
device: string;
port: string;
@@ -20,12 +21,14 @@ export interface MachineDetails {
cpu: string;
ram: string;
gpu?: string;
notes?: string;
ports: PortDetails[];
vmCount: number;
vms: Array<{
name: string;
ip: string;
role: string;
macAddress?: string;
}>;
}
@@ -45,6 +48,7 @@ export interface VmDetails {
iconKey?: string;
ip: string;
role: string;
macAddress?: string;
hostName: string;
}
+16 -4
View File
@@ -1,10 +1,10 @@
/**
* @typedef {{ path: string; message: string }} ValidationIssue
* @typedef {{ device: string; port: string }} ConnectedTo
* @typedef {{ portName: string; speedGbps?: number; connectedTo?: ConnectedTo }} Port
* @typedef {{ name: string; role: string; ipAddress: string; iconKey?: string }} VM
* @typedef {{ portName: string; speedGbps?: number; macAddress?: string; connectedTo?: ConnectedTo }} Port
* @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 {{ 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 {{ machines: Machine[]; devices: NetworkDevice[] }} NetworkData
* @typedef {{ valid: boolean; errors: ValidationIssue[]; data?: NetworkData }} ValidationResult
@@ -105,6 +105,10 @@ export function normalizePort(issues, path, value, ownerLabel) {
optional: true,
min: 0
});
const macAddress = readString(issues, `${path}.macAddress`, value.macAddress, {
optional: true,
allowEmpty: true
});
let connectedTo;
if (value.connectedTo !== undefined) {
@@ -126,6 +130,7 @@ export function normalizePort(issues, path, value, ownerLabel) {
return {
portName,
...(typeof speedGbps === 'number' ? { speedGbps } : {}),
...(macAddress ? { macAddress } : {}),
...(connectedTo ? { connectedTo } : {})
};
}
@@ -166,6 +171,10 @@ export function normalizeVm(issues, path, value) {
const role = readString(issues, `${path}.role`, value.role);
const ipAddress = readString(issues, `${path}.ipAddress`, value.ipAddress);
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) {
return null;
@@ -175,7 +184,8 @@ export function normalizeVm(issues, path, value) {
name,
role,
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 operatingSystem = readString(issues, `${path}.operatingSystem`, value.operatingSystem);
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;
if (!isRecord(softwareRaw)) {
@@ -287,6 +298,7 @@ function normalizeMachine(issues, path, value) {
role,
operatingSystem,
...(iconKey ? { iconKey } : {}),
...(notes !== undefined ? { notes } : {}),
software: { vms },
hardware: {
cpu: hardware.cpu,
+3
View File
@@ -6,6 +6,7 @@ export interface ConnectedPortRef {
export interface Port {
portName: string;
speedGbps?: number; // default to 1 if not provided
macAddress?: string;
connectedTo?: ConnectedPortRef;
}
@@ -22,6 +23,7 @@ export interface VM {
role: string;
ipAddress: string;
iconKey?: string;
macAddress?: string;
}
export interface Software {
@@ -34,6 +36,7 @@ export interface Machine {
role: string;
operatingSystem: string;
iconKey?: string;
notes?: string;
software: Software;
hardware: Hardware;
ports?: Port[];
+4 -1
View File
@@ -6,12 +6,14 @@
"role": "Hypervisor",
"operatingSystem": "Proxmox",
"iconKey": "homarr:proxmox",
"notes": "Primary router box; do not power off without failover in place.",
"software": {
"vms": [
{
"name": "OpnSense",
"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",
@@ -50,6 +52,7 @@
{
"portName": "eth0",
"speedGbps": 1,
"macAddress": "84:47:09:1c:aa:10",
"connectedTo": {
"device": "Gigabit Switch",
"port": "1"