Fix interface merging, error ordering, sACN client, and add charset headers
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ package tendrils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -245,6 +246,12 @@ func (e *ErrorTracker) GetErrors() []*Error {
|
||||
for _, err := range e.errors {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
sort.Slice(errors, func(i, j int) bool {
|
||||
if errors[i].NodeName != errors[j].NodeName {
|
||||
return errors[i].NodeName < errors[j].NodeName
|
||||
}
|
||||
return errors[i].Port < errors[j].Port
|
||||
})
|
||||
return errors
|
||||
}
|
||||
|
||||
|
||||
4
http.go
4
http.go
@@ -115,7 +115,7 @@ func ensureCert() error {
|
||||
}
|
||||
|
||||
func (t *Tendrils) handleAPIStatus(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
data, err := t.GetStatusJSON()
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] failed to encode status: %v", err)
|
||||
@@ -178,7 +178,7 @@ func (t *Tendrils) handleAPIStatusStream(w http.ResponseWriter, r *http.Request)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
w.Header().Set("Content-Type", "text/event-stream; charset=utf-8")
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
w.Header().Set("Connection", "keep-alive")
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
|
||||
36
nodes.go
36
nodes.go
@@ -171,13 +171,28 @@ func (n *Nodes) updateNodeIPs(node *Node, ips []net.IP) []string {
|
||||
}
|
||||
}
|
||||
n.ipIndex[ipKey] = node
|
||||
iface, exists := node.Interfaces[ipKey]
|
||||
if !exists {
|
||||
iface = &Interface{IPs: IPSet{}}
|
||||
node.Interfaces[ipKey] = iface
|
||||
|
||||
var targetIface *Interface
|
||||
for _, iface := range node.Interfaces {
|
||||
if iface.MAC != "" {
|
||||
targetIface = iface
|
||||
break
|
||||
}
|
||||
}
|
||||
if targetIface != nil {
|
||||
if !targetIface.IPs.Has(ipKey) {
|
||||
targetIface.IPs.Add(ip)
|
||||
added = append(added, "ip="+ipKey)
|
||||
}
|
||||
} else {
|
||||
iface, exists := node.Interfaces[ipKey]
|
||||
if !exists {
|
||||
iface = &Interface{IPs: IPSet{}}
|
||||
node.Interfaces[ipKey] = iface
|
||||
}
|
||||
iface.IPs.Add(ip)
|
||||
added = append(added, "ip="+ipKey)
|
||||
}
|
||||
iface.IPs.Add(ip)
|
||||
added = append(added, "ip="+ipKey)
|
||||
go n.t.requestARP(ip)
|
||||
}
|
||||
return added
|
||||
@@ -281,8 +296,13 @@ func (n *Nodes) updateNodeInterface(node *Node, mac net.HardwareAddr, ips []net.
|
||||
iface.IPs.Add(ip)
|
||||
n.ipIndex[ipKey] = node
|
||||
|
||||
if ipOnlyIface, exists := node.Interfaces[ipKey]; exists && ipOnlyIface != iface {
|
||||
delete(node.Interfaces, ipKey)
|
||||
for key, other := range node.Interfaces {
|
||||
if other != iface && other.IPs.Has(ipKey) {
|
||||
delete(other.IPs, ipKey)
|
||||
if len(other.IPs) == 0 && other.MAC == "" {
|
||||
delete(node.Interfaces, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1992,9 +1992,21 @@
|
||||
const sacnUniverseInputs = new Map();
|
||||
const sacnUniverseOutputs = new Map();
|
||||
|
||||
function getSacnInputsFromMulticast(node) {
|
||||
const groups = node.multicast_groups || [];
|
||||
const inputs = [];
|
||||
groups.forEach(g => {
|
||||
if (typeof g === 'string' && g.startsWith('sacn:')) {
|
||||
const u = parseInt(g.substring(5), 10);
|
||||
if (!isNaN(u)) inputs.push(u);
|
||||
}
|
||||
});
|
||||
return inputs;
|
||||
}
|
||||
|
||||
nodes.forEach(node => {
|
||||
const name = getShortLabel(node);
|
||||
(node.sacn_inputs || []).forEach(u => {
|
||||
getSacnInputsFromMulticast(node).forEach(u => {
|
||||
if (!sacnUniverseInputs.has(u)) sacnUniverseInputs.set(u, []);
|
||||
sacnUniverseInputs.get(u).push(name);
|
||||
});
|
||||
@@ -2012,7 +2024,7 @@
|
||||
|
||||
nodes.forEach(node => {
|
||||
const nodeId = node.id;
|
||||
const sacnInputs = node.sacn_inputs || [];
|
||||
const sacnInputs = getSacnInputsFromMulticast(node);
|
||||
const sacnOutputs = node.sacn_outputs || [];
|
||||
|
||||
if (sacnInputs.length === 0 && sacnOutputs.length === 0) return;
|
||||
|
||||
Reference in New Issue
Block a user