mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-10-23 13:22:30 +00:00
feat(api): add knownNeighborCount to nodesummary and publicKey to node neighbor
This commit is contained in:
7 files changed
+103
-51
No files matched your search
+35
-19
@@ -106,15 +106,16 @@ func (s *Store) ListNodes(ctx context.Context, nodeType int16, iatas []string, s
|
||||
items := make([]api.NodeSummary, 0, len(rows))
|
||||
for _, v := range rows {
|
||||
node := api.NodeSummary{
|
||||
ID: v.ID,
|
||||
PublicKey: hex.EncodeToString(v.PublicKey),
|
||||
NodeType: v.NodeType,
|
||||
NodeTypeName: api.NodeTypeName(v.NodeType),
|
||||
Name: v.Name,
|
||||
Latitude: v.Latitude,
|
||||
Longitude: v.Longitude,
|
||||
IsObserver: v.IsObserver,
|
||||
ObserverID: nullableUUID(v.ObserverID),
|
||||
ID: v.ID,
|
||||
PublicKey: hex.EncodeToString(v.PublicKey),
|
||||
NodeType: v.NodeType,
|
||||
NodeTypeName: api.NodeTypeName(v.NodeType),
|
||||
Name: v.Name,
|
||||
Latitude: v.Latitude,
|
||||
Longitude: v.Longitude,
|
||||
IsObserver: v.IsObserver,
|
||||
ObserverID: nullableUUID(v.ObserverID),
|
||||
KnownNeighborCount: v.KnownNeighborCount,
|
||||
}
|
||||
if len(v.Iatas) > 0 {
|
||||
if err := json.Unmarshal(v.Iatas, &node.IATAs); err != nil {
|
||||
@@ -147,16 +148,17 @@ func (s *Store) GetNode(ctx context.Context, nodeID uuid.UUID) (*api.Node, error
|
||||
}
|
||||
node := &api.Node{
|
||||
NodeSummary: api.NodeSummary{
|
||||
ID: row.ID,
|
||||
PublicKey: hex.EncodeToString(row.PublicKey),
|
||||
NodeType: row.NodeType,
|
||||
NodeTypeName: api.NodeTypeName(row.NodeType),
|
||||
Name: row.Name,
|
||||
Latitude: row.Latitude,
|
||||
Longitude: row.Longitude,
|
||||
IsObserver: row.IsObserver,
|
||||
ObserverID: nullableUUID(row.ObserverID),
|
||||
DefaultScope: row.DefaultScopeName,
|
||||
ID: row.ID,
|
||||
PublicKey: hex.EncodeToString(row.PublicKey),
|
||||
NodeType: row.NodeType,
|
||||
NodeTypeName: api.NodeTypeName(row.NodeType),
|
||||
Name: row.Name,
|
||||
Latitude: row.Latitude,
|
||||
Longitude: row.Longitude,
|
||||
IsObserver: row.IsObserver,
|
||||
ObserverID: nullableUUID(row.ObserverID),
|
||||
DefaultScope: row.DefaultScopeName,
|
||||
KnownNeighborCount: row.KnownNeighborCount,
|
||||
},
|
||||
LocationSource: row.LocationSource,
|
||||
SupportsMultibytePaths: row.SupportsMultibytePaths,
|
||||
@@ -212,11 +214,25 @@ func (s *Store) GetNodeNeighbors(ctx context.Context, nodeID uuid.UUID) ([]api.N
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
seen := make(map[uuid.UUID]int)
|
||||
items := make([]api.NodeNeighbor, 0, len(rows))
|
||||
for _, r := range rows {
|
||||
if idx, ok := seen[r.ID]; ok {
|
||||
items[idx].ObservationCount += r.ObservationCount
|
||||
if r.LastSeen.Time.After(time.UnixMilli(items[idx].LastSeen)) {
|
||||
items[idx].LastSeen = r.LastSeen.Time.UnixMilli()
|
||||
items[idx].IATA = r.Iata
|
||||
}
|
||||
if r.FirstSeen.Time.Before(time.UnixMilli(items[idx].FirstSeen)) {
|
||||
items[idx].FirstSeen = r.FirstSeen.Time.UnixMilli()
|
||||
}
|
||||
continue
|
||||
}
|
||||
seen[r.ID] = len(items)
|
||||
items = append(items, api.NodeNeighbor{
|
||||
ID: r.ID,
|
||||
Name: r.Name,
|
||||
PublicKey: hex.EncodeToString(r.PublicKey),
|
||||
NodeType: r.NodeType,
|
||||
NodeTypeName: api.NodeTypeName(r.NodeType),
|
||||
Latitude: r.Latitude,
|
||||
|
||||
@@ -472,7 +472,8 @@ SELECT n.*, ts.name AS default_scope_name,
|
||||
EXISTS (SELECT 1 FROM observers o WHERE o.public_key = n.public_key) AS is_observer,
|
||||
(SELECT o.id FROM observers o WHERE o.public_key = n.public_key LIMIT 1) AS observer_id,
|
||||
(SELECT json_agg(json_build_object('iata', ni.iata, 'lastHeard', (extract(epoch from ni.last_heard) * 1000)::bigint) ORDER BY ni.last_heard DESC)
|
||||
FROM node_iatas ni WHERE ni.node_id = n.id) AS iatas
|
||||
FROM node_iatas ni WHERE ni.node_id = n.id) AS iatas,
|
||||
(SELECT COUNT(*) FROM node_neighbors nn WHERE nn.node_id = n.id)::bigint AS known_neighbor_count
|
||||
FROM nodes n
|
||||
LEFT JOIN transport_scopes ts ON ts.id = n.default_scope_id
|
||||
WHERE n.id = $1;
|
||||
@@ -488,7 +489,8 @@ SELECT n.id, n.public_key, n.node_type, n.name, n.latitude, n.longitude, n.last_
|
||||
ts.name AS default_scope_name,
|
||||
json_agg(json_build_object('iata', ni.iata, 'lastHeard', (extract(epoch from ni.last_heard) * 1000)::bigint) ORDER BY ni.last_heard DESC) FILTER (WHERE ni.iata IS NOT NULL) AS iatas,
|
||||
EXISTS (SELECT 1 FROM observers o WHERE o.public_key = n.public_key) AS is_observer,
|
||||
(SELECT o.id FROM observers o WHERE o.public_key = n.public_key LIMIT 1) AS observer_id
|
||||
(SELECT o.id FROM observers o WHERE o.public_key = n.public_key LIMIT 1) AS observer_id,
|
||||
(SELECT COUNT(*) FROM node_neighbors nn WHERE nn.node_id = n.id)::bigint AS known_neighbor_count
|
||||
FROM nodes n
|
||||
LEFT JOIN node_iatas ni ON ni.node_id = n.id
|
||||
LEFT JOIN transport_scopes ts ON ts.id = n.default_scope_id
|
||||
@@ -880,7 +882,7 @@ ON CONFLICT (node_id, neighbor_id, iata) DO UPDATE SET
|
||||
-- name: GetNodeNeighbors :many
|
||||
-- Returns the neighbors of a node with details, ordered by most recently seen.
|
||||
SELECT
|
||||
n.id, n.name, n.node_type, n.latitude, n.longitude,
|
||||
n.id, n.public_key, n.name, n.node_type, n.latitude, n.longitude,
|
||||
nn.iata, nn.observation_count, nn.first_seen, nn.last_seen
|
||||
FROM node_neighbors nn
|
||||
JOIN nodes n ON n.id = nn.neighbor_id
|
||||
|
||||
+25
-17
@@ -216,7 +216,8 @@ SELECT n.id, n.public_key, n.node_type, n.name, n.latitude, n.longitude, n.locat
|
||||
EXISTS (SELECT 1 FROM observers o WHERE o.public_key = n.public_key) AS is_observer,
|
||||
(SELECT o.id FROM observers o WHERE o.public_key = n.public_key LIMIT 1) AS observer_id,
|
||||
(SELECT json_agg(json_build_object('iata', ni.iata, 'lastHeard', (extract(epoch from ni.last_heard) * 1000)::bigint) ORDER BY ni.last_heard DESC)
|
||||
FROM node_iatas ni WHERE ni.node_id = n.id) AS iatas
|
||||
FROM node_iatas ni WHERE ni.node_id = n.id) AS iatas,
|
||||
(SELECT COUNT(*) FROM node_neighbors nn WHERE nn.node_id = n.id)::bigint AS known_neighbor_count
|
||||
FROM nodes n
|
||||
LEFT JOIN transport_scopes ts ON ts.id = n.default_scope_id
|
||||
WHERE n.id = $1
|
||||
@@ -245,6 +246,7 @@ type GetNodeByIDRow struct {
|
||||
IsObserver bool `json:"is_observer"`
|
||||
ObserverID uuid.UUID `json:"observer_id"`
|
||||
Iatas []byte `json:"iatas"`
|
||||
KnownNeighborCount int64 `json:"known_neighbor_count"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetNodeByID(ctx context.Context, id uuid.UUID) (GetNodeByIDRow, error) {
|
||||
@@ -273,13 +275,14 @@ func (q *Queries) GetNodeByID(ctx context.Context, id uuid.UUID) (GetNodeByIDRow
|
||||
&i.IsObserver,
|
||||
&i.ObserverID,
|
||||
&i.Iatas,
|
||||
&i.KnownNeighborCount,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getNodeNeighbors = `-- name: GetNodeNeighbors :many
|
||||
SELECT
|
||||
n.id, n.name, n.node_type, n.latitude, n.longitude,
|
||||
n.id, n.public_key, n.name, n.node_type, n.latitude, n.longitude,
|
||||
nn.iata, nn.observation_count, nn.first_seen, nn.last_seen
|
||||
FROM node_neighbors nn
|
||||
JOIN nodes n ON n.id = nn.neighbor_id
|
||||
@@ -289,6 +292,7 @@ ORDER BY nn.last_seen DESC
|
||||
|
||||
type GetNodeNeighborsRow struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
PublicKey []byte `json:"public_key"`
|
||||
Name *string `json:"name"`
|
||||
NodeType int16 `json:"node_type"`
|
||||
Latitude *float64 `json:"latitude"`
|
||||
@@ -311,6 +315,7 @@ func (q *Queries) GetNodeNeighbors(ctx context.Context, nodeID uuid.UUID) ([]Get
|
||||
var i GetNodeNeighborsRow
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.PublicKey,
|
||||
&i.Name,
|
||||
&i.NodeType,
|
||||
&i.Latitude,
|
||||
@@ -2018,7 +2023,8 @@ SELECT n.id, n.public_key, n.node_type, n.name, n.latitude, n.longitude, n.last_
|
||||
ts.name AS default_scope_name,
|
||||
json_agg(json_build_object('iata', ni.iata, 'lastHeard', (extract(epoch from ni.last_heard) * 1000)::bigint) ORDER BY ni.last_heard DESC) FILTER (WHERE ni.iata IS NOT NULL) AS iatas,
|
||||
EXISTS (SELECT 1 FROM observers o WHERE o.public_key = n.public_key) AS is_observer,
|
||||
(SELECT o.id FROM observers o WHERE o.public_key = n.public_key LIMIT 1) AS observer_id
|
||||
(SELECT o.id FROM observers o WHERE o.public_key = n.public_key LIMIT 1) AS observer_id,
|
||||
(SELECT COUNT(*) FROM node_neighbors nn WHERE nn.node_id = n.id)::bigint AS known_neighbor_count
|
||||
FROM nodes n
|
||||
LEFT JOIN node_iatas ni ON ni.node_id = n.id
|
||||
LEFT JOIN transport_scopes ts ON ts.id = n.default_scope_id
|
||||
@@ -2057,20 +2063,21 @@ type ListNodesParams struct {
|
||||
}
|
||||
|
||||
type ListNodesRow struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
PublicKey []byte `json:"public_key"`
|
||||
NodeType int16 `json:"node_type"`
|
||||
Name *string `json:"name"`
|
||||
Latitude *float64 `json:"latitude"`
|
||||
Longitude *float64 `json:"longitude"`
|
||||
LastSeen pgtype.Timestamptz `json:"last_seen"`
|
||||
RadioFreqMhz *float32 `json:"radio_freq_mhz"`
|
||||
RadioSf *int16 `json:"radio_sf"`
|
||||
RadioBwKhz *float32 `json:"radio_bw_khz"`
|
||||
DefaultScopeName *string `json:"default_scope_name"`
|
||||
Iatas []byte `json:"iatas"`
|
||||
IsObserver bool `json:"is_observer"`
|
||||
ObserverID uuid.UUID `json:"observer_id"`
|
||||
ID uuid.UUID `json:"id"`
|
||||
PublicKey []byte `json:"public_key"`
|
||||
NodeType int16 `json:"node_type"`
|
||||
Name *string `json:"name"`
|
||||
Latitude *float64 `json:"latitude"`
|
||||
Longitude *float64 `json:"longitude"`
|
||||
LastSeen pgtype.Timestamptz `json:"last_seen"`
|
||||
RadioFreqMhz *float32 `json:"radio_freq_mhz"`
|
||||
RadioSf *int16 `json:"radio_sf"`
|
||||
RadioBwKhz *float32 `json:"radio_bw_khz"`
|
||||
DefaultScopeName *string `json:"default_scope_name"`
|
||||
Iatas []byte `json:"iatas"`
|
||||
IsObserver bool `json:"is_observer"`
|
||||
ObserverID uuid.UUID `json:"observer_id"`
|
||||
KnownNeighborCount int64 `json:"known_neighbor_count"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListNodes(ctx context.Context, arg ListNodesParams) ([]ListNodesRow, error) {
|
||||
@@ -2107,6 +2114,7 @@ func (q *Queries) ListNodes(ctx context.Context, arg ListNodesParams) ([]ListNod
|
||||
&i.Iatas,
|
||||
&i.IsObserver,
|
||||
&i.ObserverID,
|
||||
&i.KnownNeighborCount,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -2302,6 +2302,9 @@ const docTemplate = `{
|
||||
"description": "true if this node is also a known observer",
|
||||
"type": "boolean"
|
||||
},
|
||||
"knownNeighborCount": {
|
||||
"type": "integer"
|
||||
},
|
||||
"lastAdvertAt": {
|
||||
"description": "epoch ms, nil if no advert received",
|
||||
"type": "integer"
|
||||
@@ -2413,6 +2416,9 @@ const docTemplate = `{
|
||||
},
|
||||
"observationCount": {
|
||||
"type": "integer"
|
||||
},
|
||||
"publicKey": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2437,6 +2443,9 @@ const docTemplate = `{
|
||||
"description": "true if this node is also a known observer",
|
||||
"type": "boolean"
|
||||
},
|
||||
"knownNeighborCount": {
|
||||
"type": "integer"
|
||||
},
|
||||
"lat": {
|
||||
"description": "decimal degrees, from advert AppData",
|
||||
"type": "number"
|
||||
|
||||
@@ -2300,6 +2300,9 @@
|
||||
"description": "true if this node is also a known observer",
|
||||
"type": "boolean"
|
||||
},
|
||||
"knownNeighborCount": {
|
||||
"type": "integer"
|
||||
},
|
||||
"lastAdvertAt": {
|
||||
"description": "epoch ms, nil if no advert received",
|
||||
"type": "integer"
|
||||
@@ -2411,6 +2414,9 @@
|
||||
},
|
||||
"observationCount": {
|
||||
"type": "integer"
|
||||
},
|
||||
"publicKey": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2435,6 +2441,9 @@
|
||||
"description": "true if this node is also a known observer",
|
||||
"type": "boolean"
|
||||
},
|
||||
"knownNeighborCount": {
|
||||
"type": "integer"
|
||||
},
|
||||
"lat": {
|
||||
"description": "decimal degrees, from advert AppData",
|
||||
"type": "number"
|
||||
|
||||
@@ -188,6 +188,8 @@ definitions:
|
||||
isObserver:
|
||||
description: true if this node is also a known observer
|
||||
type: boolean
|
||||
knownNeighborCount:
|
||||
type: integer
|
||||
lastAdvertAt:
|
||||
description: epoch ms, nil if no advert received
|
||||
type: integer
|
||||
@@ -267,6 +269,8 @@ definitions:
|
||||
type: string
|
||||
observationCount:
|
||||
type: integer
|
||||
publicKey:
|
||||
type: string
|
||||
type: object
|
||||
github_com_MeshCore-Beacon_beacon-server_internal_api.NodeSummary:
|
||||
properties:
|
||||
@@ -283,6 +287,8 @@ definitions:
|
||||
isObserver:
|
||||
description: true if this node is also a known observer
|
||||
type: boolean
|
||||
knownNeighborCount:
|
||||
type: integer
|
||||
lat:
|
||||
description: decimal degrees, from advert AppData
|
||||
type: number
|
||||
|
||||
+14
-12
@@ -14,6 +14,7 @@ import (
|
||||
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"`
|
||||
@@ -32,18 +33,19 @@ type NodeIATA struct {
|
||||
|
||||
// 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"
|
||||
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,
|
||||
|
||||
Reference in new issue
Block a user