mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-22 01:13:00 +02:00
Vendor a Lucide icon set under static/img/icons/ and partial:ize the inline SVGs across base, login, firmware, sidebar, and yang-tree. Reorganize the sidebar with mirrored Status/Configure section labels. Rename NACM to Users & Groups and Firmware to Software; swap Routes and Routing between Status and Configure. Split Configure > System into General, NTP Client, and DNS Client pages, which opens up for relocating the Set Time RPC from the Maintenance section to a new Date & Time card in the System General config page. Update browser tab title on htmx navigation via an HX-Trigger setPageTitle event; encode the payload as 7-bit ASCII so the middle dot in "Page · Context" survives header transit. Add missing hover-help ⓘ icons to bridge port configuration, and adjust their alignment slightly. While in interface configuration: - fix the annoying Save => collapse so one can edit multiple interface sections without having to re-expand the iface - add missing dhcpv4 settings - add missing custom mac (use advanced view for other settings) - clean up Save button mess (consolidate) Rename Firmware -> Software everywhere in the Maintenance section and fix "Install progress stuck at 10%" issue. Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
133 lines
4.2 KiB
Go
133 lines
4.2 KiB
Go
// SPDX-License-Identifier: MIT
|
|
|
|
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"html/template"
|
|
"log"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"infix/webui/internal/restconf"
|
|
)
|
|
|
|
// ─── LLDP types ──────────────────────────────────────────────────────────────
|
|
|
|
// LLDPNeighbor is a remote system seen via LLDP.
|
|
type LLDPNeighbor struct {
|
|
LocalPort string
|
|
ChassisID string
|
|
SystemName string
|
|
PortID string
|
|
PortDesc string
|
|
SystemDesc string
|
|
Capabilities string // comma-separated
|
|
MgmtAddress string
|
|
}
|
|
|
|
// ─── Page data ───────────────────────────────────────────────────────────────
|
|
|
|
type lldpPageData struct {
|
|
PageData
|
|
Neighbors []LLDPNeighbor
|
|
Error string
|
|
}
|
|
|
|
// ─── Handler ─────────────────────────────────────────────────────────────────
|
|
|
|
// LLDPHandler serves the LLDP neighbors page.
|
|
type LLDPHandler struct {
|
|
Template *template.Template
|
|
RC *restconf.Client
|
|
}
|
|
|
|
// Overview renders the LLDP page (GET /lldp).
|
|
func (h *LLDPHandler) Overview(w http.ResponseWriter, r *http.Request) {
|
|
data := lldpPageData{
|
|
PageData: newPageData(w, r, "lldp", "LLDP"),
|
|
}
|
|
|
|
ctx := context.WithoutCancel(r.Context())
|
|
|
|
var raw struct {
|
|
LLDP struct {
|
|
Port []struct {
|
|
Name string `json:"name"`
|
|
DestMACAddress string `json:"dest-mac-address"`
|
|
RemoteSystemsData []struct {
|
|
ChassisID string `json:"chassis-id"`
|
|
PortID string `json:"port-id"`
|
|
PortDesc string `json:"port-desc"`
|
|
SystemName string `json:"system-name"`
|
|
SystemDescription string `json:"system-description"`
|
|
SystemCapabilitiesEnabled json.RawMessage `json:"system-capabilities-enabled"`
|
|
ManagementAddress []struct {
|
|
Address string `json:"address"`
|
|
} `json:"management-address"`
|
|
} `json:"remote-systems-data"`
|
|
} `json:"port"`
|
|
} `json:"ieee802-dot1ab-lldp:lldp"`
|
|
}
|
|
if err := h.RC.Get(ctx, "/data/ieee802-dot1ab-lldp:lldp", &raw); err != nil {
|
|
log.Printf("restconf lldp: %v", err)
|
|
data.Error = "Failed to fetch LLDP data"
|
|
} else {
|
|
for _, port := range raw.LLDP.Port {
|
|
for _, rs := range port.RemoteSystemsData {
|
|
mgmt := ""
|
|
if len(rs.ManagementAddress) > 0 {
|
|
mgmt = rs.ManagementAddress[0].Address
|
|
}
|
|
data.Neighbors = append(data.Neighbors, LLDPNeighbor{
|
|
LocalPort: port.Name,
|
|
ChassisID: rs.ChassisID,
|
|
SystemName: rs.SystemName,
|
|
PortID: rs.PortID,
|
|
PortDesc: rs.PortDesc,
|
|
SystemDesc: rs.SystemDescription,
|
|
Capabilities: parseLLDPCapabilities(rs.SystemCapabilitiesEnabled),
|
|
MgmtAddress: mgmt,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
tmplName := "lldp.html"
|
|
if r.Header.Get("HX-Request") == "true" {
|
|
tmplName = "content"
|
|
}
|
|
if err := h.Template.ExecuteTemplate(w, tmplName, data); err != nil {
|
|
log.Printf("template error: %v", err)
|
|
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
|
}
|
|
}
|
|
|
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
|
|
// parseLLDPCapabilities turns the YANG system-capabilities-enabled bits
|
|
// value into a readable comma-separated string.
|
|
func parseLLDPCapabilities(raw json.RawMessage) string {
|
|
if len(raw) == 0 {
|
|
return ""
|
|
}
|
|
// Try plain string first (some implementations encode as "bridge router")
|
|
var s string
|
|
if json.Unmarshal(raw, &s) == nil {
|
|
parts := strings.Fields(s)
|
|
return strings.Join(parts, ", ")
|
|
}
|
|
// Try array of strings
|
|
var arr []string
|
|
if json.Unmarshal(raw, &arr) == nil {
|
|
return strings.Join(arr, ", ")
|
|
}
|
|
// Fallback: return raw minus braces
|
|
trimmed := strings.TrimSpace(string(raw))
|
|
if trimmed == "{}" || trimmed == "null" || trimmed == "[]" {
|
|
return ""
|
|
}
|
|
return trimmed
|
|
}
|