diff --git a/db/queries/queries.sql b/db/queries/queries.sql index f77466b..a84bfde 100644 --- a/db/queries/queries.sql +++ b/db/queries/queries.sql @@ -310,14 +310,25 @@ UPDATE nodes SET supports_multibyte_traces = TRUE WHERE id = $1 AND supports_multibyte_traces = FALSE; -- name: GetNodeByPubkey :one -SELECT * FROM nodes WHERE public_key = $1; +SELECT *, + 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 +FROM nodes n +WHERE n.public_key = $1; -- name: GetNodeByID :one -SELECT * FROM nodes WHERE id = $1; +SELECT *, + 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 +FROM nodes n +WHERE n.id = $1; + -- name: ListNodes :many SELECT n.id, n.public_key, n.node_type, n.name, n.latitude, n.longitude, n.last_seen, - array_remove(array_agg(DISTINCT ni.iata ORDER BY ni.iata), NULL)::text[] AS iatas + array_remove(array_agg(DISTINCT ni.iata ORDER BY ni.iata), NULL)::text[] 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 FROM nodes n LEFT JOIN node_iatas ni ON ni.node_id = n.id WHERE diff --git a/db/sqlc/queries.sql.go b/db/sqlc/queries.sql.go index 2577a9b..ff5bdce 100644 --- a/db/sqlc/queries.sql.go +++ b/db/sqlc/queries.sql.go @@ -206,12 +206,35 @@ func (q *Queries) GetIATA(ctx context.Context, iata string) (IataCode, error) { } const getNodeByID = `-- name: GetNodeByID :one -SELECT id, public_key, node_type, name, latitude, longitude, location_source, last_advert_at, supports_multibyte_paths, supports_multibyte_traces, min_firmware_version, first_seen, last_seen, metadata FROM nodes WHERE id = $1 +SELECT id, public_key, node_type, name, latitude, longitude, location_source, last_advert_at, supports_multibyte_paths, supports_multibyte_traces, min_firmware_version, first_seen, last_seen, metadata, + 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 +FROM nodes n +WHERE n.id = $1 ` -func (q *Queries) GetNodeByID(ctx context.Context, id uuid.UUID) (Node, error) { +type GetNodeByIDRow 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"` + LocationSource *string `json:"location_source"` + LastAdvertAt pgtype.Timestamptz `json:"last_advert_at"` + SupportsMultibytePaths bool `json:"supports_multibyte_paths"` + SupportsMultibyteTraces bool `json:"supports_multibyte_traces"` + MinFirmwareVersion *string `json:"min_firmware_version"` + FirstSeen pgtype.Timestamptz `json:"first_seen"` + LastSeen pgtype.Timestamptz `json:"last_seen"` + Metadata []byte `json:"metadata"` + IsObserver bool `json:"is_observer"` + ObserverID uuid.UUID `json:"observer_id"` +} + +func (q *Queries) GetNodeByID(ctx context.Context, id uuid.UUID) (GetNodeByIDRow, error) { row := q.db.QueryRow(ctx, getNodeByID, id) - var i Node + var i GetNodeByIDRow err := row.Scan( &i.ID, &i.PublicKey, @@ -227,17 +250,42 @@ func (q *Queries) GetNodeByID(ctx context.Context, id uuid.UUID) (Node, error) { &i.FirstSeen, &i.LastSeen, &i.Metadata, + &i.IsObserver, + &i.ObserverID, ) return i, err } const getNodeByPubkey = `-- name: GetNodeByPubkey :one -SELECT id, public_key, node_type, name, latitude, longitude, location_source, last_advert_at, supports_multibyte_paths, supports_multibyte_traces, min_firmware_version, first_seen, last_seen, metadata FROM nodes WHERE public_key = $1 +SELECT id, public_key, node_type, name, latitude, longitude, location_source, last_advert_at, supports_multibyte_paths, supports_multibyte_traces, min_firmware_version, first_seen, last_seen, metadata, + 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 +FROM nodes n +WHERE n.public_key = $1 ` -func (q *Queries) GetNodeByPubkey(ctx context.Context, publicKey []byte) (Node, error) { +type GetNodeByPubkeyRow 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"` + LocationSource *string `json:"location_source"` + LastAdvertAt pgtype.Timestamptz `json:"last_advert_at"` + SupportsMultibytePaths bool `json:"supports_multibyte_paths"` + SupportsMultibyteTraces bool `json:"supports_multibyte_traces"` + MinFirmwareVersion *string `json:"min_firmware_version"` + FirstSeen pgtype.Timestamptz `json:"first_seen"` + LastSeen pgtype.Timestamptz `json:"last_seen"` + Metadata []byte `json:"metadata"` + IsObserver bool `json:"is_observer"` + ObserverID uuid.UUID `json:"observer_id"` +} + +func (q *Queries) GetNodeByPubkey(ctx context.Context, publicKey []byte) (GetNodeByPubkeyRow, error) { row := q.db.QueryRow(ctx, getNodeByPubkey, publicKey) - var i Node + var i GetNodeByPubkeyRow err := row.Scan( &i.ID, &i.PublicKey, @@ -253,6 +301,8 @@ func (q *Queries) GetNodeByPubkey(ctx context.Context, publicKey []byte) (Node, &i.FirstSeen, &i.LastSeen, &i.Metadata, + &i.IsObserver, + &i.ObserverID, ) return i, err } @@ -1292,7 +1342,9 @@ func (q *Queries) ListNodeObservations(ctx context.Context, arg ListNodeObservat const listNodes = `-- name: ListNodes :many SELECT n.id, n.public_key, n.node_type, n.name, n.latitude, n.longitude, n.last_seen, - array_remove(array_agg(DISTINCT ni.iata ORDER BY ni.iata), NULL)::text[] AS iatas + array_remove(array_agg(DISTINCT ni.iata ORDER BY ni.iata), NULL)::text[] 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 FROM nodes n LEFT JOIN node_iatas ni ON ni.node_id = n.id WHERE @@ -1320,14 +1372,16 @@ 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"` - Iatas []string `json:"iatas"` + 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"` + Iatas []string `json:"iatas"` + IsObserver bool `json:"is_observer"` + ObserverID uuid.UUID `json:"observer_id"` } func (q *Queries) ListNodes(ctx context.Context, arg ListNodesParams) ([]ListNodesRow, error) { @@ -1357,6 +1411,8 @@ func (q *Queries) ListNodes(ctx context.Context, arg ListNodesParams) ([]ListNod &i.Longitude, &i.LastSeen, &i.Iatas, + &i.IsObserver, + &i.ObserverID, ); err != nil { return nil, err } diff --git a/db/store.go b/db/store.go index 239e8af..b9113c0 100644 --- a/db/store.go +++ b/db/store.go @@ -840,6 +840,8 @@ func (s *Store) ListNodes(ctx context.Context, nodeType int16, iata string, supp Latitude: v.Latitude, Longitude: v.Longitude, IATAs: v.Iatas, + IsObserver: v.IsObserver, + ObvserverID: nullableUUID(v.ObserverID), }) } var nextCursor *int64 @@ -875,6 +877,8 @@ func (s *Store) GetNode(ctx context.Context, nodeID uuid.UUID) (*api.Node, error Latitude: row.Latitude, Longitude: row.Longitude, IATAs: iatas, + IsObserver: row.IsObserver, + ObvserverID: nullableUUID(row.ObserverID), }, LocationSource: row.LocationSource, SupportsMultibytePaths: row.SupportsMultibytePaths, @@ -1182,3 +1186,10 @@ func (s *Store) GetStatsTopObservers(ctx context.Context, iata string, since tim } return items, nil } + +func nullableUUID(id uuid.UUID) *uuid.UUID { + if id == (uuid.UUID{}) { + return nil + } + return &id +} diff --git a/internal/api/reader.go b/internal/api/reader.go index cc10600..3156bf4 100644 --- a/internal/api/reader.go +++ b/internal/api/reader.go @@ -182,14 +182,16 @@ type ObserverTelemetry 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 public key - NodeType int16 `json:"nodeType"` // 1=companion, 2=repeater, 3=room server - NodeTypeName string `json:"nodeTypeName"` - Name *string `json:"name,omitempty"` - Latitude *float64 `json:"lat,omitempty"` - Longitude *float64 `json:"lng,omitempty"` - IATAs []string `json:"iatas"` + ID uuid.UUID `json:"id"` + PublicKey string `json:"publicKey"` // hex-encoded public key + NodeType int16 `json:"nodeType"` // 1=companion, 2=repeater, 3=room server + NodeTypeName string `json:"nodeTypeName"` + Name *string `json:"name,omitempty"` + IsObserver bool `json:"isObserver"` + ObvserverID *uuid.UUID `json:"observerId,omitempty"` + Latitude *float64 `json:"lat,omitempty"` + Longitude *float64 `json:"lng,omitempty"` + IATAs []string `json:"iatas"` } // Node is the full node representation including firmware capability flags,