summaryrefslogtreecommitdiffstats
path: root/static/interface-renderer.js
diff options
context:
space:
mode:
authorPetri Hienonen <petri.hienonen@gmail.com>2025-09-28 13:41:46 +0300
committerPetri Hienonen <petri.hienonen@gmail.com>2025-09-28 13:41:46 +0300
commitb0c76dcc159ead3d67314da3a71d60bad9385991 (patch)
tree08a9d0be61b3e223883b8d164967c0d0ef973960 /static/interface-renderer.js
parenta1862888a7818ae9663b02dda48d25ef5f2ab6a6 (diff)
downloadnetwork-b0c76dcc159ead3d67314da3a71d60bad9385991.tar.zst
Split the system
Diffstat (limited to 'static/interface-renderer.js')
-rw-r--r--static/interface-renderer.js250
1 files changed, 250 insertions, 0 deletions
diff --git a/static/interface-renderer.js b/static/interface-renderer.js
new file mode 100644
index 0000000..93364c8
--- /dev/null
+++ b/static/interface-renderer.js
@@ -0,0 +1,250 @@
+/* jshint esversion: 2024, module: true */
+
+/**
+ * Interface Renderer for displaying network interfaces
+ * @class InterfaceRenderer
+ */
+class InterfaceRenderer {
+ /**
+ * @param {Object} elements - DOM elements
+ * @param {Object} state - Application state
+ */
+ constructor(elements, state) {
+ this.elements = elements;
+ this.state = state;
+ }
+
+ /**
+ * Render interface tabs
+ * @method renderInterfaceTabs
+ * @param {Array} interfaces - Array of interface objects
+ */
+ renderInterfaceTabs(interfaces) {
+ if (!interfaces.length) {
+ this.elements.outputs.ifaceTabs.innerHTML = '<div class="no-interfaces">No network interfaces found</div>';
+ this.elements.outputs.ifaceDetails.innerHTML = '';
+ return;
+ }
+
+ const tabsHTML = interfaces.map(iface => `
+ <button class="interface-tab ${iface === this.state.currentInterface ? 'active' : ''}"
+ data-interface="${iface.Name}">
+ ${iface.Name}
+ <span class="interface-state ${this.getStateClass(iface)}">${this.getStateText(iface)}</span>
+ </button>
+ `).join('');
+
+ this.elements.outputs.ifaceTabs.innerHTML = `<div class="interface-tabs-container">${tabsHTML}</div>`;
+
+ // Add event listeners to tabs
+ this.elements.outputs.ifaceTabs.querySelectorAll('.interface-tab').forEach(tab => {
+ tab.addEventListener('click', (event) => {
+ const ifaceName = event.currentTarget.dataset.interface;
+ const iface = interfaces.find(i => i.Name === ifaceName);
+ if (iface) {
+ this.showInterfaceDetails(iface);
+ }
+ });
+ });
+ }
+
+ /**
+ * Show detailed interface information with abbreviations
+ * @method showInterfaceDetails
+ * @param {Object} iface - Interface object
+ */
+ showInterfaceDetails(iface) {
+ this.state.currentInterface = iface;
+
+ // Update active tab
+ this.elements.outputs.ifaceTabs.querySelectorAll('.interface-tab').forEach(tab => {
+ tab.classList.toggle('active', tab.dataset.interface === iface.Name);
+ });
+
+ const detailsHTML = `
+ <div class="interface-detail-grid">
+ ${this.renderDetailRow('Link File', iface.LinkFile)}
+ ${this.renderDetailRow('Network File', iface.NetworkFile)}
+ ${this.renderDetailRow('State', iface.State, this.getStateClass(iface))}
+ ${this.renderDetailRow('Online State', iface.OnlineState)}
+ ${this.renderDetailRow('Type', iface.Type)}
+ ${this.renderDetailRow('Path', iface.Path)}
+ ${this.renderDetailRow('Driver', iface.Driver)}
+ ${this.renderDetailRow('Vendor', iface.Vendor)}
+ ${this.renderDetailRow('Model', iface.Model)}
+ ${this.renderDetailRow('Hardware Address', this.arrayToMac(iface.HardwareAddress))}
+ ${this.renderDetailRow('MTU', iface.MTU ? `${iface.MTU} (min: ${iface.MTUMin ?? '?'}, max: ${iface.MTUMax ?? '?'})` : '')}
+ ${this.renderDetailRow('QDisc', iface.QDisc)}
+ ${this.renderDetailRow('IPv6 Address Generation Mode', iface.IPv6AddressGenerationMode)}
+ ${this.renderDetailRow('Number of Queues (Tx/Rx)', iface.Queues ? `${iface.Queues.Tx ?? '?'}/${iface.Queues.Rx ?? '?'}` : '')}
+ ${this.renderDetailRow('Auto negotiation', iface.AutoNegotiation ? 'yes' : 'no')}
+ ${this.renderDetailRow('Speed', iface.Speed)}
+ ${this.renderDetailRow('Duplex', iface.Duplex)}
+ ${this.renderDetailRow('Port', iface.Port)}
+ ${this.renderDetailRow('Address', this.renderAddressList(iface.Addresses))}
+ ${this.renderDetailRow('DNS', this.renderDNSServerList(iface.DNS))}
+ ${this.renderDetailRow('NTP', iface.NTP)}
+ ${this.renderDetailRow('Activation Policy', iface.ActivationPolicy)}
+ ${this.renderDetailRow('Required For Online', iface.RequiredForOnline ? 'yes' : 'no')}
+ ${this.renderDetailRow('Connected To', iface.ConnectedTo)}
+ ${this.renderDetailRow('Offered DHCP leases', this.renderDHCPLeases(iface.DHCPLeases))}
+ </div>
+ `;
+
+ this.elements.outputs.ifaceDetails.innerHTML = detailsHTML;
+ }
+
+ /**
+ * Render a detail row with abbreviations
+ * @method renderDetailRow
+ * @param {string} label - Row label
+ * @param {string} value - Row value
+ * @param {string} [valueClass] - CSS class for value
+ * @returns {string} HTML string
+ */
+ renderDetailRow(label, value, valueClass = '') {
+ if (!value) return '';
+
+ // Add abbreviations for common networking terms
+ const abbreviations = {
+ 'MTU': 'Maximum Transmission Unit',
+ 'QDisc': 'Queueing Discipline',
+ 'Tx': 'Transmit',
+ 'Rx': 'Receive',
+ 'DNS': 'Domain Name System',
+ 'NTP': 'Network Time Protocol',
+ 'DHCP': 'Dynamic Host Configuration Protocol',
+ 'MAC': 'Media Access Control',
+ 'IP': 'Internet Protocol',
+ 'IPv6': 'Internet Protocol version 6'
+ };
+
+ const abbrLabel = Object.keys(abbreviations).includes(label)
+ ? `<abbr title="${abbreviations[label]}">${label}</abbr>`
+ : label;
+
+ return `
+ <div class="detail-row">
+ <span class="detail-label">${abbrLabel}:</span>
+ <span class="detail-value ${valueClass}">${value}</span>
+ </div>
+ `;
+ }
+
+ /**
+ * Render address list
+ * @method renderAddressList
+ * @param {Array} addresses - Array of addresses
+ * @returns {string} Formatted addresses
+ */
+ renderAddressList(addresses) {
+ if (!addresses?.length) return '';
+
+ return addresses.map(addr => {
+ const ip = this.ipFromArray(addr);
+ return ip ? `<div class="address-item">${ip}</div>` : '';
+ }).join('');
+ }
+
+ /**
+ * Render DNS server list
+ * @method renderDNSServerList
+ * @param {Array} dnsServers - Array of DNS servers
+ * @returns {string} Formatted DNS servers
+ */
+ renderDNSServerList(dnsServers) {
+ if (!dnsServers?.length) return '';
+
+ return dnsServers.map(dns => {
+ const server = this.ipFromArray(dns.Address ?? dns);
+ return server ? `<div class="dns-item">${server}</div>` : '';
+ }).join('');
+ }
+
+ /**
+ * Render DHCP leases
+ * @method renderDHCPLeases
+ * @param {Array} leases - Array of DHCP leases
+ * @returns {string} Formatted leases
+ */
+ renderDHCPLeases(leases) {
+ if (!leases?.length) return '';
+
+ return leases.map(lease => {
+ const ip = lease.IP ?? lease;
+ const to = lease.To ?? lease.MAC ?? '';
+ return `<div class="lease-item">${ip} (to ${to})</div>`;
+ }).join('');
+ }
+
+ /**
+ * Get CSS class for interface state
+ * @method getStateClass
+ * @param {Object} iface - Interface object
+ * @returns {string} CSS class
+ */
+ getStateClass(iface) {
+ const state = iface.OperationalState ?? iface.AdministrativeState ?? iface.State ?? '';
+ return state.toLowerCase().includes('up') ||
+ state.toLowerCase().includes('routable') ||
+ state.toLowerCase().includes('configured') ? 'state-up' : 'state-down';
+ }
+
+ /**
+ * Get display text for interface state
+ * @method getStateText
+ * @param {Object} iface - Interface object
+ * @returns {string} State text
+ */
+ getStateText(iface) {
+ return iface.OperationalState ?? iface.AdministrativeState ?? iface.State ?? 'unknown';
+ }
+
+ /**
+ * Convert byte array to MAC address
+ * @method arrayToMac
+ * @param {Array} bytes - Byte array
+ * @returns {string} MAC address
+ */
+ arrayToMac(bytes) {
+ if (!Array.isArray(bytes)) return '';
+
+ return bytes.map(byte => byte.toString(16).padStart(2, '0')).join(':');
+ }
+
+ /**
+ * Convert byte array to IP address
+ * @method ipFromArray
+ * @param {Array|Object} obj - IP data
+ * @returns {string} IP address
+ */
+ ipFromArray(obj) {
+ let bytes = null;
+
+ if (Array.isArray(obj)) {
+ bytes = obj;
+ } else if (obj?.Address && Array.isArray(obj.Address)) {
+ bytes = obj.Address;
+ } else {
+ return '';
+ }
+
+ // IPv4
+ if (bytes.length === 4) {
+ return bytes.join('.');
+ }
+
+ // IPv6
+ if (bytes.length === 16) {
+ const parts = [];
+ for (let i = 0; i < 16; i += 2) {
+ parts.push(((bytes[i] << 8) | bytes[i + 1]).toString(16));
+ }
+ return parts.join(':').replace(/(^|:)0+/g, '$1').replace(/:{3,}/, '::');
+ }
+
+ return '';
+ }
+}
+
+export { InterfaceRenderer };