package display import ( "fmt" "html" "net" "os" "sort" "strings" "time" "forge.lthn.ai/core/gui/pkg/p2p" ) type NetworkInterfaceState struct { Name string `json:"name"` Index int `json:"index"` MTU int `json:"mtu"` HardwareAddr string `json:"hardware_addr,omitempty"` Flags []string `json:"flags,omitempty"` Addresses []string `json:"addresses,omitempty"` Up bool `json:"up"` Loopback bool `json:"loopback"` } type NetworkPeerState struct { ID string `json:"id"` Topic string `json:"topic"` Connected bool `json:"connected"` SeenAt time.Time `json:"seen_at"` } type NetworkState struct { Hostname string `json:"hostname"` Interfaces []NetworkInterfaceState `json:"interfaces"` Peers []NetworkPeerState `json:"peers,omitempty"` ObservedAt time.Time `json:"observed_at"` } func (s *Service) networkState() NetworkState { state := NetworkState{ Hostname: hostname(), Interfaces: make([]NetworkInterfaceState, 0), ObservedAt: time.Now().UTC(), } interfaces, err := net.Interfaces() if err != nil { return state } sort.Slice(interfaces, func(i, j int) bool { return strings.ToLower(interfaces[i].Name) < strings.ToLower(interfaces[j].Name) }) for _, iface := range interfaces { addresses := make([]string, 0) if addrs, err := iface.Addrs(); err == nil { for _, addr := range addrs { addresses = append(addresses, addr.String()) } sort.Strings(addresses) } state.Interfaces = append(state.Interfaces, NetworkInterfaceState{ Name: iface.Name, Index: iface.Index, MTU: iface.MTU, HardwareAddr: iface.HardwareAddr.String(), Flags: interfaceFlags(iface.Flags), Addresses: addresses, Up: iface.Flags&net.FlagUp != 0, Loopback: iface.Flags&net.FlagLoopback != 0, }) } state.Peers = s.p2pPeers() return state } type peerLister interface { Peers() []p2p.Peer } func (s *Service) p2pPeers() []NetworkPeerState { if s == nil || s.Core() == nil { return nil } for _, serviceName := range []string{"p2p", "network"} { serviceResult := s.Core().Service(serviceName) if !serviceResult.OK || serviceResult.Value == nil { continue } lister, ok := serviceResult.Value.(peerLister) if !ok { continue } peers := lister.Peers() if len(peers) == 0 { continue } peerStates := make([]NetworkPeerState, 0, len(peers)) for _, peer := range peers { peerStates = append(peerStates, NetworkPeerState{ ID: peer.ID, Topic: peer.Topic, Connected: peer.Connected, SeenAt: peer.SeenAt, }) } sort.Slice(peerStates, func(i, j int) bool { if peerStates[i].SeenAt.Equal(peerStates[j].SeenAt) { return strings.ToLower(peerStates[i].ID) < strings.ToLower(peerStates[j].ID) } return peerStates[i].SeenAt.After(peerStates[j].SeenAt) }) return peerStates } return nil } func (s *Service) renderNetworkPage(state NetworkState) string { var builder strings.Builder builder.WriteString("core://network
core://network
") builder.WriteString(html.EscapeString(state.Hostname)) builder.WriteString(" · ") builder.WriteString(html.EscapeString(state.ObservedAt.Format(time.RFC3339))) builder.WriteString("
Registered peers
") } else { builder.WriteString("") } builder.WriteString("
") return builder.String() } func (s *Service) renderNetworkInterfacePage(state NetworkState, iface NetworkInterfaceState) string { var builder strings.Builder builder.WriteString("core://network/") builder.WriteString(html.EscapeString(iface.Name)) builder.WriteString("
core://network/") builder.WriteString(html.EscapeString(iface.Name)) builder.WriteString("
") builder.WriteString(html.EscapeString(state.Hostname)) builder.WriteString("
") builder.WriteString(html.EscapeString(iface.Name)) builder.WriteString("
Index ") builder.WriteString(fmt.Sprintf("%d", iface.Index)) builder.WriteString(" · MTU ") builder.WriteString(fmt.Sprintf("%d", iface.MTU)) builder.WriteString(" · ") if iface.Up { builder.WriteString("up") } else { builder.WriteString("down") } if iface.Loopback { builder.WriteString(" · loopback") } builder.WriteString("
")
	builder.WriteString(html.EscapeString(strings.Join(iface.Addresses, "\n")))
	builder.WriteString("
Flags: ") if len(iface.Flags) == 0 { builder.WriteString("none") } else { builder.WriteString(html.EscapeString(strings.Join(iface.Flags, ", "))) } builder.WriteString("
") if len(state.Peers) > 0 { builder.WriteString("
Registered peers
") } builder.WriteString("
") return builder.String() } func interfaceFlags(flags net.Flags) []string { values := make([]string, 0, 4) if flags&net.FlagUp != 0 { values = append(values, "up") } if flags&net.FlagBroadcast != 0 { values = append(values, "broadcast") } if flags&net.FlagLoopback != 0 { values = append(values, "loopback") } if flags&net.FlagPointToPoint != 0 { values = append(values, "point-to-point") } if flags&net.FlagMulticast != 0 { values = append(values, "multicast") } if flags&net.FlagRunning != 0 { values = append(values, "running") } return values } func hostname() string { name, err := os.Hostname() if err != nil || strings.TrimSpace(name) == "" { return "localhost" } return name }