summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPetri Hienonen <petri.hienonen@gmail.com>2025-09-28 21:23:14 +0300
committerPetri Hienonen <petri.hienonen@gmail.com>2025-09-28 21:23:14 +0300
commitdf83a2861130d4e07b0a720a3452f10cbc4bc84b (patch)
tree901b84a33714d7b7057c294e0c1c91399cb6d3bf
parent01fa523251680e9708c0301ee09f83f472f03e06 (diff)
downloadnetwork-df83a2861130d4e07b0a720a3452f10cbc4bc84b.tar.zst
Complex
-rw-r--r--static/interface-renderer.js150
-rw-r--r--static/utils.js37
2 files changed, 168 insertions, 19 deletions
diff --git a/static/interface-renderer.js b/static/interface-renderer.js
index 5145cf3..d3478be 100644
--- a/static/interface-renderer.js
+++ b/static/interface-renderer.js
@@ -1,6 +1,7 @@
/* jshint esversion: 2024, module: true */
import { Utils } from "./utils.js";
+import { InterfaceState } from "./enums.js";
/**
* Static Interface Renderer for displaying network interfaces
@@ -27,7 +28,7 @@ class InterfaceRenderer {
const isActive = iface === currentInterface;
return `
- <button class="interface-tab ${isActive ? "active" : ""}"
+ <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>
@@ -96,7 +97,10 @@ class InterfaceRenderer {
"DNS",
InterfaceRenderer.renderDNSServerList(iface.DNS),
),
- InterfaceRenderer.renderDetailRow("NTP", iface.NTP),
+ InterfaceRenderer.renderDetailRow(
+ "NTP",
+ InterfaceRenderer.renderNTPServerList(iface.NTP),
+ ),
InterfaceRenderer.renderDetailRow(
"Activation Policy",
iface.ActivationPolicy,
@@ -126,7 +130,7 @@ class InterfaceRenderer {
* @returns {string} HTML string
*/
static renderDetailRow(label, value, valueClass = "") {
- if (!value) return "";
+ if (!value && value !== 0 && value !== false) return "";
const abbreviations = {
MTU: "Maximum Transmission Unit",
@@ -145,15 +149,74 @@ class InterfaceRenderer {
? `<abbr title="${abbreviations[label]}">${label}</abbr>`
: label;
+ // Handle different value types
+ let displayValue;
+ if (typeof value === "string") {
+ displayValue = Utils.sanitizeHTML(value);
+ } else if (typeof value === "object") {
+ // Handle object rendering
+ displayValue = InterfaceRenderer.renderObjectValue(value);
+ } else {
+ displayValue = Utils.sanitizeHTML(String(value));
+ }
+
return `
<div class="detail-row">
<span class="detail-label">${abbrLabel}:</span>
- <span class="detail-value ${valueClass}">${Utils.sanitizeHTML(value)}</span>
+ <span class="detail-value ${valueClass}">${displayValue}</span>
</div>
`;
}
/**
+ * Render object value for display
+ * @static
+ * @param {Object} obj - Object to render
+ * @returns {string} HTML string
+ */
+ static renderObjectValue(obj) {
+ if (obj === null || obj === undefined) return "";
+
+ // Handle array of objects
+ if (Array.isArray(obj)) {
+ return obj
+ .map((item) => InterfaceRenderer.renderObjectValue(item))
+ .join(", ");
+ }
+
+ // Handle plain object
+ if (typeof obj === "object") {
+ // Check if it's an IP address object
+ if (obj.Address && Array.isArray(obj.Address)) {
+ return Utils.ipFromArray(obj);
+ }
+
+ // Check if it's a simple key-value object we can stringify
+ try {
+ const simpleObj = {};
+ for (const [key, value] of Object.entries(obj)) {
+ if (
+ value !== null &&
+ value !== undefined &&
+ typeof value !== "object"
+ ) {
+ simpleObj[key] = value;
+ }
+ }
+ if (Object.keys(simpleObj).length > 0) {
+ return Utils.sanitizeHTML(JSON.stringify(simpleObj));
+ }
+ } catch (e) {
+ // Fall through to default handling
+ }
+
+ return Utils.sanitizeHTML(String(obj));
+ }
+
+ return Utils.sanitizeHTML(String(obj));
+ }
+
+ /**
* Render address list
* @static
* @param {Array} addresses - Array of addresses
@@ -162,14 +225,15 @@ class InterfaceRenderer {
static renderAddressList(addresses) {
if (!addresses?.length) return "";
- return addresses
+ const addressItems = addresses
.map((addr) => {
const ip = Utils.ipFromArray(addr);
- return ip
- ? `<div class="address-item">${Utils.sanitizeHTML(ip)}</div>`
- : "";
+ return ip ? Utils.sanitizeHTML(ip) : "";
})
- .join("");
+ .filter((ip) => ip)
+ .join(", ");
+
+ return addressItems || "";
}
/**
@@ -181,14 +245,47 @@ class InterfaceRenderer {
static renderDNSServerList(dnsServers) {
if (!dnsServers?.length) return "";
- return dnsServers
+ const dnsItems = dnsServers
.map((dns) => {
const server = Utils.ipFromArray(dns.Address ?? dns);
- return server
- ? `<div class="dns-item">${Utils.sanitizeHTML(server)}</div>`
- : "";
+ return server ? Utils.sanitizeHTML(server) : "";
})
- .join("");
+ .filter((server) => server)
+ .join(", ");
+
+ return dnsItems || "";
+ }
+
+ /**
+ * Render NTP server list
+ * @static
+ * @param {Array} ntpServers - Array of NTP servers
+ * @returns {string} Formatted NTP servers
+ */
+ static renderNTPServerList(ntpServers) {
+ if (!ntpServers?.length) return "";
+
+ const ntpItems = ntpServers
+ .map((ntp) => {
+ if (typeof ntp === "string") {
+ return Utils.sanitizeHTML(ntp);
+ } else if (ntp && typeof ntp === "object") {
+ // Handle NTP server object
+ if (ntp.Address && Array.isArray(ntp.Address)) {
+ return Utils.ipFromArray(ntp);
+ } else if (ntp.Server) {
+ return Utils.sanitizeHTML(ntp.Server);
+ } else {
+ // Try to extract any stringifiable value
+ return InterfaceRenderer.renderObjectValue(ntp);
+ }
+ }
+ return "";
+ })
+ .filter((server) => server)
+ .join(", ");
+
+ return ntpItems || "";
}
/**
@@ -200,13 +297,28 @@ class InterfaceRenderer {
static renderDHCPLeases(leases) {
if (!leases?.length) return "";
- return leases
+ const leaseItems = leases
.map((lease) => {
- const ip = lease.IP ?? lease;
- const to = lease.To ?? lease.MAC ?? "";
- return `<div class="lease-item">${Utils.sanitizeHTML(ip)} (to ${Utils.sanitizeHTML(to)})</div>`;
+ if (typeof lease === "string") {
+ return Utils.sanitizeHTML(lease);
+ } else if (lease && typeof lease === "object") {
+ const ip = lease.IP ?? lease.Address ?? lease;
+ const to = lease.To ?? lease.MAC ?? lease.ClientIdentifier ?? "";
+
+ if (ip && to) {
+ return `${Utils.sanitizeHTML(String(ip))} (to ${Utils.sanitizeHTML(String(to))})`;
+ } else if (ip) {
+ return Utils.sanitizeHTML(String(ip));
+ } else {
+ return InterfaceRenderer.renderObjectValue(lease);
+ }
+ }
+ return "";
})
- .join("");
+ .filter((lease) => lease)
+ .join(", ");
+
+ return leaseItems || "";
}
}
diff --git a/static/utils.js b/static/utils.js
index 32b10be..931653f 100644
--- a/static/utils.js
+++ b/static/utils.js
@@ -168,6 +168,43 @@ class Utils {
timeout = setTimeout(later, wait);
};
}
+ /**
+ * Safely convert any value to display string
+ * @static
+ * @param {*} value - Any value
+ * @returns {string} Safe display string
+ */
+ static safeToString(value) {
+ if (value === null || value === undefined) return "";
+ if (typeof value === "string") return value;
+ if (typeof value === "number" || typeof value === "boolean")
+ return String(value);
+ if (Array.isArray(value)) {
+ return value.map((item) => Utils.safeToString(item)).join(", ");
+ }
+ if (typeof value === "object") {
+ // Handle IP address objects
+ if (value.Address && Array.isArray(value.Address)) {
+ return Utils.ipFromArray(value);
+ }
+ // Try to stringify simple objects
+ try {
+ const simpleObj = {};
+ for (const [key, val] of Object.entries(value)) {
+ if (val !== null && val !== undefined && typeof val !== "object") {
+ simpleObj[key] = val;
+ }
+ }
+ if (Object.keys(simpleObj).length > 0) {
+ return JSON.stringify(simpleObj);
+ }
+ } catch (e) {
+ // Fall through
+ }
+ return String(value);
+ }
+ return String(value);
+ }
}
export { Utils };