143 lines
3.8 KiB
Go
143 lines
3.8 KiB
Go
// svxdr — live-data daemon for Reflector Dashboard (nodelist + proxy + astmeta + xlink)
|
|
// Copyright (C) 2026 SA7AUX (Björn)
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify it under
|
|
// the terms of the GNU Affero General Public License as published by the Free
|
|
// Software Foundation, either version 3 of the License, or (at your option) any
|
|
// later version. See the LICENSE file for details.
|
|
|
|
// Package statusnorm holds the ONE rule that turns a reflector's shared sat-login
|
|
// (e.g. KT4ROY-ASL, used by all its TG nodes) into the per-TG display callsign
|
|
// (KT4ROY-ASL1..4). It is applied ONCE where a status is ingested (the proxy's
|
|
// cache fill, and the federation crawl), so every consumer downstream reads
|
|
// already-correct data — no per-view special-casing anywhere.
|
|
package statusnorm
|
|
|
|
import "encoding/json"
|
|
|
|
// Normalize rewrites the callsign of every node in trunks[].nodes and
|
|
// satellites[].nodes: rename(callsign, tg) returns the replacement, or "" to keep
|
|
// it. All other data is preserved. Parse failure or no change -> raw unchanged.
|
|
func Normalize(raw []byte, rename func(callsign string, tg int) string) []byte {
|
|
var top map[string]json.RawMessage
|
|
if json.Unmarshal(raw, &top) != nil {
|
|
return raw
|
|
}
|
|
changed := false
|
|
|
|
// Top-level nodes[]: a group can present as a plain node (e.g. a DMR gateway),
|
|
// not only under a trunk/satellite. Keyed by the node's login/identity, so rename
|
|
// by that KEY (its callsign field may be the operator, not the login).
|
|
if blk, ok := top["nodes"]; ok {
|
|
var nm map[string]json.RawMessage
|
|
if json.Unmarshal(blk, &nm) == nil {
|
|
blkChanged := false
|
|
for id, nRaw := range nm {
|
|
var n map[string]json.RawMessage
|
|
if json.Unmarshal(nRaw, &n) != nil {
|
|
continue
|
|
}
|
|
cs, tg := callsignTG(n)
|
|
repl := rename(id, tg) // match on the node's KEY (its login)
|
|
if repl == "" || repl == cs {
|
|
continue
|
|
}
|
|
b, _ := json.Marshal(repl)
|
|
n["callsign"] = b
|
|
if enc, err := json.Marshal(n); err == nil {
|
|
nm[id] = enc
|
|
blkChanged = true
|
|
}
|
|
}
|
|
if blkChanged {
|
|
if enc, err := json.Marshal(nm); err == nil {
|
|
top["nodes"] = enc
|
|
changed = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, key := range []string{"trunks", "satellites"} {
|
|
blk, ok := top[key]
|
|
if !ok {
|
|
continue
|
|
}
|
|
var group map[string]json.RawMessage
|
|
if json.Unmarshal(blk, &group) != nil {
|
|
continue
|
|
}
|
|
blkChanged := false
|
|
for id, objRaw := range group {
|
|
var obj map[string]json.RawMessage
|
|
if json.Unmarshal(objRaw, &obj) != nil {
|
|
continue
|
|
}
|
|
nodesRaw, ok := obj["nodes"]
|
|
if !ok {
|
|
continue
|
|
}
|
|
var nodes []json.RawMessage
|
|
if json.Unmarshal(nodesRaw, &nodes) != nil {
|
|
continue
|
|
}
|
|
nodesChanged := false
|
|
for i, nRaw := range nodes {
|
|
var n map[string]json.RawMessage
|
|
if json.Unmarshal(nRaw, &n) != nil {
|
|
continue
|
|
}
|
|
cs, tg := callsignTG(n)
|
|
if cs == "" {
|
|
continue
|
|
}
|
|
repl := rename(cs, tg)
|
|
if repl == "" || repl == cs {
|
|
continue
|
|
}
|
|
b, _ := json.Marshal(repl)
|
|
n["callsign"] = b
|
|
if enc, err := json.Marshal(n); err == nil {
|
|
nodes[i] = enc
|
|
nodesChanged = true
|
|
}
|
|
}
|
|
if nodesChanged {
|
|
if enc, err := json.Marshal(nodes); err == nil {
|
|
obj["nodes"] = enc
|
|
if oenc, err := json.Marshal(obj); err == nil {
|
|
group[id] = oenc
|
|
blkChanged = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if blkChanged {
|
|
if enc, err := json.Marshal(group); err == nil {
|
|
top[key] = enc
|
|
changed = true
|
|
}
|
|
}
|
|
}
|
|
if !changed {
|
|
return raw
|
|
}
|
|
if enc, err := json.Marshal(top); err == nil {
|
|
return enc
|
|
}
|
|
return raw
|
|
}
|
|
|
|
func callsignTG(n map[string]json.RawMessage) (string, int) {
|
|
var cs string
|
|
if v, ok := n["callsign"]; ok {
|
|
_ = json.Unmarshal(v, &cs)
|
|
}
|
|
var tg int
|
|
if v, ok := n["tg"]; ok {
|
|
_ = json.Unmarshal(v, &tg)
|
|
}
|
|
return cs, tg
|
|
}
|