mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-09-02 09:03:45 +00:00
NodeTypeName is fine as is with possible concatenation of the value as there is unlikely to be more than 256 node types
99 lines
4.3 KiB
Go
99 lines
4.3 KiB
Go
// Copyright 2026 Beacon Contributors
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
package api
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/meshcore-go/meshcore-go"
|
|
)
|
|
|
|
// NodeNeighbor represents a neighboring node relationship observed in a given IATA.
|
|
type NodeNeighbor struct {
|
|
ID uuid.UUID `json:"id"`
|
|
Name *string `json:"name,omitempty"`
|
|
PublicKey string `json:"publicKey"`
|
|
NodeType int16 `json:"nodeType"`
|
|
NodeTypeName string `json:"nodeTypeName"`
|
|
Latitude *float64 `json:"lat,omitempty"`
|
|
Longitude *float64 `json:"lng,omitempty"`
|
|
IATA string `json:"iata"`
|
|
ObservationCount int64 `json:"observationCount"`
|
|
FirstSeen int64 `json:"firstSeen"` // epoch ms
|
|
LastSeen int64 `json:"lastSeen"` // epoch ms
|
|
}
|
|
|
|
// NodeIATA represents a single IATA code and the last time the node was heard there.
|
|
type NodeIATA struct {
|
|
IATA string `json:"iata"`
|
|
LastHeard int64 `json:"lastHeard"` // epoch ms
|
|
}
|
|
|
|
// NodeSummary is the minimal node representation used in list responses.
|
|
type NodeSummary struct {
|
|
ID uuid.UUID `json:"id"`
|
|
PublicKey string `json:"publicKey"` // hex-encoded Ed25519 public key
|
|
NodeType int16 `json:"nodeType"` // 1=companion, 2=repeater, 3=room_server, 4=sensor
|
|
NodeTypeName string `json:"nodeTypeName"`
|
|
Name *string `json:"name,omitempty"`
|
|
IsObserver bool `json:"isObserver"` // true if this node is also a known observer
|
|
ObserverID *uuid.UUID `json:"observerId,omitempty"` // UUID of the associated observer row, if any
|
|
Latitude *float64 `json:"lat,omitempty"` // decimal degrees, from advert AppData
|
|
Longitude *float64 `json:"lng,omitempty"` // decimal degrees, from advert AppData
|
|
Radio *string `json:"radio,omitempty"` // shorthand: "freqMhz,bwKhz,sf" e.g. "910.5,62.5,7"
|
|
IATAs []NodeIATA `json:"iatas"` // IATAs where this node has been heard, with last heard timestamps
|
|
DefaultScope *string `json:"defaultScope,omitempty"` // most recently matched transport scope name e.g. "#bc"
|
|
KnownNeighborCount int64 `json:"knownNeighborCount"`
|
|
}
|
|
|
|
// Node is the full node representation including firmware capability flags,
|
|
// location source, and timing metadata.
|
|
type Node struct {
|
|
NodeSummary
|
|
LocationSource *string `json:"locationSource,omitempty"` // "advert" or "manual"
|
|
LastAdvertAt *int64 `json:"lastAdvertAt,omitempty"` // epoch ms, nil if no advert received
|
|
SupportsMultibytePaths bool `json:"supportsMultibytePaths"` // firmware >= 1.14.0; detected via path hash size
|
|
SupportsMultibyteTraces bool `json:"supportsMultibyteTraces"` // firmware >= 1.11.0; detected via trace hash size
|
|
MinFirmwareVersion *string `json:"minFirmwareVersion,omitempty"` // derived from capability flags
|
|
FirstSeen int64 `json:"firstSeen"` // epoch ms
|
|
LastSeen int64 `json:"lastSeen"` // epoch ms
|
|
Metadata any `json:"metadata,omitempty"` // raw JSONB metadata
|
|
Neighbors []NodeNeighbor `json:"neighbors"`
|
|
}
|
|
|
|
// NodeTypeName returns a human-readable name for a node type integer.
|
|
// NOTE: truncation is fine here until there are at least over 200 types of node
|
|
func NodeTypeName(t int16) string {
|
|
switch byte(t) {
|
|
case meshcore.AdvertTypeChat:
|
|
return "companion"
|
|
case meshcore.AdvertTypeRepeater:
|
|
return "repeater"
|
|
case meshcore.AdvertTypeRoom:
|
|
return "room_server"
|
|
case meshcore.AdvertTypeSensor:
|
|
return "sensor"
|
|
default:
|
|
return "unknown"
|
|
}
|
|
}
|
|
|
|
// NodeTypeFromString returns the integer node type for a given name.
|
|
// Returns 0 (no filter) if the string is empty or unrecognized.
|
|
func NodeTypeFromString(s string) int16 {
|
|
switch strings.ToLower(s) {
|
|
case "companion", "chat":
|
|
return int16(meshcore.AdvertTypeChat)
|
|
case "repeater":
|
|
return int16(meshcore.AdvertTypeRepeater)
|
|
case "room_server", "roomserver", "room-server", "room":
|
|
return int16(meshcore.AdvertTypeRoom)
|
|
case "sensor":
|
|
return int16(meshcore.AdvertTypeSensor)
|
|
default:
|
|
return 0
|
|
}
|
|
}
|