diff options
Diffstat (limited to 'static/interface-renderer.js')
| -rw-r--r-- | static/interface-renderer.js | 232 |
1 files changed, 72 insertions, 160 deletions
diff --git a/static/interface-renderer.js b/static/interface-renderer.js index 93364c8..8c01696 100644 --- a/static/interface-renderer.js +++ b/static/interface-renderer.js @@ -1,111 +1,92 @@ /* jshint esversion: 2024, module: true */ +import { Utils } from './utils.js'; +import { InterfaceState } from './enums.js'; + /** - * Interface Renderer for displaying network interfaces + * Static 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 + * @static * @param {Array} interfaces - Array of interface objects + * @param {Object} currentInterface - Current interface object + * @returns {string} HTML string */ - renderInterfaceTabs(interfaces) { + static renderInterfaceTabs(interfaces, currentInterface) { if (!interfaces.length) { - this.elements.outputs.ifaceTabs.innerHTML = '<div class="no-interfaces">No network interfaces found</div>'; - this.elements.outputs.ifaceDetails.innerHTML = ''; - return; + return '<div class="no-interfaces">No network interfaces found</div>'; } - 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); - } - }); - }); + const tabsHTML = interfaces.map(iface => { + const state = Utils.getInterfaceState(iface); + const stateClass = Utils.getStateClass(state); + const stateText = Utils.getStateText(iface); + const isActive = iface === currentInterface; + + return ` + <button class="interface-tab ${isActive ? 'active' : ''}" + data-interface="${Utils.sanitizeHTML(iface.Name)}"> + ${Utils.sanitizeHTML(iface.Name)} + <span class="interface-state ${stateClass}">${Utils.sanitizeHTML(stateText)}</span> + </button> + `; + }).join(''); + + return `<div class="interface-tabs-container">${tabsHTML}</div>`; } /** - * Show detailed interface information with abbreviations - * @method showInterfaceDetails + * Show detailed interface information + * @static * @param {Object} iface - Interface object + * @returns {string} HTML string */ - 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; + static showInterfaceDetails(iface) { + const details = [ + InterfaceRenderer.renderDetailRow('Link File', iface.LinkFile), + InterfaceRenderer.renderDetailRow('Network File', iface.NetworkFile), + InterfaceRenderer.renderDetailRow('State', iface.State, Utils.getStateClass(Utils.getInterfaceState(iface))), + InterfaceRenderer.renderDetailRow('Online State', iface.OnlineState), + InterfaceRenderer.renderDetailRow('Type', iface.Type), + InterfaceRenderer.renderDetailRow('Path', iface.Path), + InterfaceRenderer.renderDetailRow('Driver', iface.Driver), + InterfaceRenderer.renderDetailRow('Vendor', iface.Vendor), + InterfaceRenderer.renderDetailRow('Model', iface.Model), + InterfaceRenderer.renderDetailRow('Hardware Address', Utils.arrayToMac(iface.HardwareAddress)), + InterfaceRenderer.renderDetailRow('MTU', iface.MTU ? `${iface.MTU} (min: ${iface.MTUMin ?? '?'}, max: ${iface.MTUMax ?? '?'})` : ''), + InterfaceRenderer.renderDetailRow('QDisc', iface.QDisc), + InterfaceRenderer.renderDetailRow('IPv6 Address Generation Mode', iface.IPv6AddressGenerationMode), + InterfaceRenderer.renderDetailRow('Number of Queues (Tx/Rx)', iface.Queues ? `${iface.Queues.Tx ?? '?'}/${iface.Queues.Rx ?? '?'}` : ''), + InterfaceRenderer.renderDetailRow('Auto negotiation', iface.AutoNegotiation ? 'yes' : 'no'), + InterfaceRenderer.renderDetailRow('Speed', iface.Speed), + InterfaceRenderer.renderDetailRow('Duplex', iface.Duplex), + InterfaceRenderer.renderDetailRow('Port', iface.Port), + InterfaceRenderer.renderDetailRow('Address', InterfaceRenderer.renderAddressList(iface.Addresses)), + InterfaceRenderer.renderDetailRow('DNS', InterfaceRenderer.renderDNSServerList(iface.DNS)), + InterfaceRenderer.renderDetailRow('NTP', iface.NTP), + InterfaceRenderer.renderDetailRow('Activation Policy', iface.ActivationPolicy), + InterfaceRenderer.renderDetailRow('Required For Online', iface.RequiredForOnline ? 'yes' : 'no'), + InterfaceRenderer.renderDetailRow('Connected To', iface.ConnectedTo), + InterfaceRenderer.renderDetailRow('Offered DHCP leases', InterfaceRenderer.renderDHCPLeases(iface.DHCPLeases)) + ].filter(Boolean).join(''); + + return `<div class="interface-detail-grid">${details}</div>`; } /** * Render a detail row with abbreviations - * @method renderDetailRow + * @static * @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 = '') { + static renderDetailRow(label, value, valueClass = '') { if (!value) return ''; - // Add abbreviations for common networking terms const abbreviations = { 'MTU': 'Maximum Transmission Unit', 'QDisc': 'Queueing Discipline', @@ -126,125 +107,56 @@ class InterfaceRenderer { return ` <div class="detail-row"> <span class="detail-label">${abbrLabel}:</span> - <span class="detail-value ${valueClass}">${value}</span> + <span class="detail-value ${valueClass}">${Utils.sanitizeHTML(value)}</span> </div> `; } /** * Render address list - * @method renderAddressList + * @static * @param {Array} addresses - Array of addresses * @returns {string} Formatted addresses */ - renderAddressList(addresses) { + static renderAddressList(addresses) { if (!addresses?.length) return ''; return addresses.map(addr => { - const ip = this.ipFromArray(addr); - return ip ? `<div class="address-item">${ip}</div>` : ''; + const ip = Utils.ipFromArray(addr); + return ip ? `<div class="address-item">${Utils.sanitizeHTML(ip)}</div>` : ''; }).join(''); } /** * Render DNS server list - * @method renderDNSServerList + * @static * @param {Array} dnsServers - Array of DNS servers * @returns {string} Formatted DNS servers */ - renderDNSServerList(dnsServers) { + static 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>` : ''; + const server = Utils.ipFromArray(dns.Address ?? dns); + return server ? `<div class="dns-item">${Utils.sanitizeHTML(server)}</div>` : ''; }).join(''); } /** * Render DHCP leases - * @method renderDHCPLeases + * @static * @param {Array} leases - Array of DHCP leases * @returns {string} Formatted leases */ - renderDHCPLeases(leases) { + static 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>`; + return `<div class="lease-item">${Utils.sanitizeHTML(ip)} (to ${Utils.sanitizeHTML(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 }; |
