mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-09-01 16:48:19 +00:00
feat(api): differentiate traces and pings
This commit is contained in:
@@ -812,7 +812,8 @@ SELECT
|
||||
MIN(p.first_heard_at)::timestamptz AS first_heard_at,
|
||||
MAX(p.last_heard_at)::timestamptz AS last_heard_at,
|
||||
COUNT(DISTINCT p.packet_hash) AS packet_count,
|
||||
COUNT(DISTINCT po.iata) AS iata_count
|
||||
COUNT(DISTINCT po.iata) AS iata_count,
|
||||
MAX(p.parsed_payload->>'type')::text AS trace_type
|
||||
FROM packets p
|
||||
LEFT JOIN packet_observations po ON po.packet_hash = p.packet_hash
|
||||
WHERE p.trace_tag IS NOT NULL
|
||||
@@ -821,6 +822,7 @@ WHERE p.trace_tag IS NOT NULL
|
||||
AND ($3::timestamptz IS NULL OR p.first_heard_at >= $3)
|
||||
AND ($4::timestamptz IS NULL OR p.first_heard_at <= $4)
|
||||
AND ($5::timestamptz IS NULL OR p.last_heard_at < $5)
|
||||
AND ($7::text = '' OR p.parsed_payload->>'type' = $7)
|
||||
GROUP BY p.trace_tag
|
||||
ORDER BY MAX(p.last_heard_at) DESC
|
||||
LIMIT $6;
|
||||
|
||||
@@ -2620,7 +2620,8 @@ SELECT
|
||||
MIN(p.first_heard_at)::timestamptz AS first_heard_at,
|
||||
MAX(p.last_heard_at)::timestamptz AS last_heard_at,
|
||||
COUNT(DISTINCT p.packet_hash) AS packet_count,
|
||||
COUNT(DISTINCT po.iata) AS iata_count
|
||||
COUNT(DISTINCT po.iata) AS iata_count,
|
||||
MAX(p.parsed_payload->>'type')::text AS trace_type
|
||||
FROM packets p
|
||||
LEFT JOIN packet_observations po ON po.packet_hash = p.packet_hash
|
||||
WHERE p.trace_tag IS NOT NULL
|
||||
@@ -2629,6 +2630,7 @@ WHERE p.trace_tag IS NOT NULL
|
||||
AND ($3::timestamptz IS NULL OR p.first_heard_at >= $3)
|
||||
AND ($4::timestamptz IS NULL OR p.first_heard_at <= $4)
|
||||
AND ($5::timestamptz IS NULL OR p.last_heard_at < $5)
|
||||
AND ($7::text = '' OR p.parsed_payload->>'type' = $7)
|
||||
GROUP BY p.trace_tag
|
||||
ORDER BY MAX(p.last_heard_at) DESC
|
||||
LIMIT $6
|
||||
@@ -2641,6 +2643,7 @@ type ListTraceTagsParams struct {
|
||||
Column4 pgtype.Timestamptz `json:"column_4"`
|
||||
Column5 pgtype.Timestamptz `json:"column_5"`
|
||||
Limit int32 `json:"limit"`
|
||||
Column7 string `json:"column_7"`
|
||||
}
|
||||
|
||||
type ListTraceTagsRow struct {
|
||||
@@ -2649,6 +2652,7 @@ type ListTraceTagsRow struct {
|
||||
LastHeardAt pgtype.Timestamptz `json:"last_heard_at"`
|
||||
PacketCount int64 `json:"packet_count"`
|
||||
IataCount int64 `json:"iata_count"`
|
||||
TraceType string `json:"trace_type"`
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
@@ -2663,6 +2667,7 @@ func (q *Queries) ListTraceTags(ctx context.Context, arg ListTraceTagsParams) ([
|
||||
arg.Column4,
|
||||
arg.Column5,
|
||||
arg.Limit,
|
||||
arg.Column7,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -2677,6 +2682,7 @@ func (q *Queries) ListTraceTags(ctx context.Context, arg ListTraceTagsParams) ([
|
||||
&i.LastHeardAt,
|
||||
&i.PacketCount,
|
||||
&i.IataCount,
|
||||
&i.TraceType,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+3
-1
@@ -21,7 +21,7 @@ type tracePayload struct {
|
||||
SNRValues []float32 `json:"snrValues"`
|
||||
}
|
||||
|
||||
func (s *Store) ListTraceTags(ctx context.Context, iatas []string, scope string, since, until time.Time, cursor time.Time, limit int32) ([]api.TraceTagSummary, error) {
|
||||
func (s *Store) ListTraceTags(ctx context.Context, iatas []string, scope, traceType string, since, until time.Time, cursor time.Time, limit int32) ([]api.TraceTagSummary, error) {
|
||||
iataFilter := strings.Join(iatas, ",")
|
||||
var sinceTS, untilTS, cursorTS pgtype.Timestamptz
|
||||
if !since.IsZero() {
|
||||
@@ -40,6 +40,7 @@ func (s *Store) ListTraceTags(ctx context.Context, iatas []string, scope string,
|
||||
Column4: untilTS,
|
||||
Column5: cursorTS,
|
||||
Limit: limit,
|
||||
Column7: traceType,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -52,6 +53,7 @@ func (s *Store) ListTraceTags(ctx context.Context, iatas []string, scope string,
|
||||
LastHeardAt: r.LastHeardAt.Time.UnixMilli(),
|
||||
PacketCount: r.PacketCount,
|
||||
IATACount: r.IataCount,
|
||||
TraceType: r.TraceType,
|
||||
})
|
||||
}
|
||||
return items, nil
|
||||
|
||||
@@ -1941,6 +1941,12 @@ const docTemplate = `{
|
||||
"name": "scope",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Filter by type: TRACE or PING (default: all)",
|
||||
"name": "type",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Filter by first_heard_at \u003e= since (epoch ms)",
|
||||
@@ -3123,6 +3129,7 @@ const docTemplate = `{
|
||||
"type": "string"
|
||||
},
|
||||
"snr": {
|
||||
"description": "signal-to-noise ratio in dB, if available",
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
@@ -3358,6 +3365,7 @@ const docTemplate = `{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"packets": {
|
||||
"description": "all packets observed for this trace",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/github_com_MeshCore-Beacon_beacon-server_internal_api.TracePacket"
|
||||
@@ -3381,27 +3389,33 @@ const docTemplate = `{
|
||||
"type": "integer"
|
||||
},
|
||||
"packetHash": {
|
||||
"description": "hex-encoded packet hash",
|
||||
"type": "string"
|
||||
},
|
||||
"rawPath": {
|
||||
"description": "hops as received in the packet",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/github_com_MeshCore-Beacon_beacon-server_internal_api.RawHop"
|
||||
}
|
||||
},
|
||||
"resolvedRoute": {
|
||||
"description": "hops resolved to known nodes",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/github_com_MeshCore-Beacon_beacon-server_internal_api.ResolvedHop"
|
||||
}
|
||||
},
|
||||
"routeType": {
|
||||
"description": "numeric route type",
|
||||
"type": "integer"
|
||||
},
|
||||
"routeTypeName": {
|
||||
"description": "human-readable route type",
|
||||
"type": "string"
|
||||
},
|
||||
"scope": {
|
||||
"description": "transport scope name, if known",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
@@ -3428,6 +3442,10 @@ const docTemplate = `{
|
||||
"traceTag": {
|
||||
"description": "hex-encoded 4-byte tag",
|
||||
"type": "string"
|
||||
},
|
||||
"traceType": {
|
||||
"description": "TRACE or PING",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1939,6 +1939,12 @@
|
||||
"name": "scope",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Filter by type: TRACE or PING (default: all)",
|
||||
"name": "type",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Filter by first_heard_at \u003e= since (epoch ms)",
|
||||
@@ -3121,6 +3127,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"snr": {
|
||||
"description": "signal-to-noise ratio in dB, if available",
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
@@ -3356,6 +3363,7 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"packets": {
|
||||
"description": "all packets observed for this trace",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/github_com_MeshCore-Beacon_beacon-server_internal_api.TracePacket"
|
||||
@@ -3379,27 +3387,33 @@
|
||||
"type": "integer"
|
||||
},
|
||||
"packetHash": {
|
||||
"description": "hex-encoded packet hash",
|
||||
"type": "string"
|
||||
},
|
||||
"rawPath": {
|
||||
"description": "hops as received in the packet",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/github_com_MeshCore-Beacon_beacon-server_internal_api.RawHop"
|
||||
}
|
||||
},
|
||||
"resolvedRoute": {
|
||||
"description": "hops resolved to known nodes",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/github_com_MeshCore-Beacon_beacon-server_internal_api.ResolvedHop"
|
||||
}
|
||||
},
|
||||
"routeType": {
|
||||
"description": "numeric route type",
|
||||
"type": "integer"
|
||||
},
|
||||
"routeTypeName": {
|
||||
"description": "human-readable route type",
|
||||
"type": "string"
|
||||
},
|
||||
"scope": {
|
||||
"description": "transport scope name, if known",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
@@ -3426,6 +3440,10 @@
|
||||
"traceTag": {
|
||||
"description": "hex-encoded 4-byte tag",
|
||||
"type": "string"
|
||||
},
|
||||
"traceType": {
|
||||
"description": "TRACE or PING",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -755,6 +755,7 @@ definitions:
|
||||
description: hex-encoded path hash
|
||||
type: string
|
||||
snr:
|
||||
description: signal-to-noise ratio in dB, if available
|
||||
type: number
|
||||
type: object
|
||||
github_com_MeshCore-Beacon_beacon-server_internal_api.Region:
|
||||
@@ -912,6 +913,7 @@ definitions:
|
||||
github_com_MeshCore-Beacon_beacon-server_internal_api.TraceDetail:
|
||||
properties:
|
||||
packets:
|
||||
description: all packets observed for this trace
|
||||
items:
|
||||
$ref: '#/definitions/github_com_MeshCore-Beacon_beacon-server_internal_api.TracePacket'
|
||||
type: array
|
||||
@@ -928,20 +930,26 @@ definitions:
|
||||
description: epoch ms
|
||||
type: integer
|
||||
packetHash:
|
||||
description: hex-encoded packet hash
|
||||
type: string
|
||||
rawPath:
|
||||
description: hops as received in the packet
|
||||
items:
|
||||
$ref: '#/definitions/github_com_MeshCore-Beacon_beacon-server_internal_api.RawHop'
|
||||
type: array
|
||||
resolvedRoute:
|
||||
description: hops resolved to known nodes
|
||||
items:
|
||||
$ref: '#/definitions/github_com_MeshCore-Beacon_beacon-server_internal_api.ResolvedHop'
|
||||
type: array
|
||||
routeType:
|
||||
description: numeric route type
|
||||
type: integer
|
||||
routeTypeName:
|
||||
description: human-readable route type
|
||||
type: string
|
||||
scope:
|
||||
description: transport scope name, if known
|
||||
type: string
|
||||
type: object
|
||||
github_com_MeshCore-Beacon_beacon-server_internal_api.TraceTagSummary:
|
||||
@@ -961,6 +969,9 @@ definitions:
|
||||
traceTag:
|
||||
description: hex-encoded 4-byte tag
|
||||
type: string
|
||||
traceType:
|
||||
description: TRACE or PING
|
||||
type: string
|
||||
type: object
|
||||
internal_api_handlers.APIError:
|
||||
properties:
|
||||
@@ -2246,6 +2257,10 @@ paths:
|
||||
in: query
|
||||
name: scope
|
||||
type: string
|
||||
- description: 'Filter by type: TRACE or PING (default: all)'
|
||||
in: query
|
||||
name: type
|
||||
type: string
|
||||
- description: Filter by first_heard_at >= since (epoch ms)
|
||||
in: query
|
||||
name: since
|
||||
|
||||
@@ -148,7 +148,7 @@ func (stubReader) GetScopeByName(ctx context.Context, name string) (*api.ScopeDe
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (stubReader) ListTraceTags(ctx context.Context, iatas []string, scope string, since, until time.Time, cursor time.Time, limit int32) ([]api.TraceTagSummary, error) {
|
||||
func (stubReader) ListTraceTags(ctx context.Context, iatas []string, scope, traceType string, since, until time.Time, cursor time.Time, limit int32) ([]api.TraceTagSummary, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ package handlers
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/MeshCore-Beacon/beacon-server/internal/api"
|
||||
@@ -32,6 +33,7 @@ func TracesRouter(reader api.Reader) http.Handler {
|
||||
// @Param region query string false "Filter by region slug"
|
||||
// @Param regionId query int false "Filter by region ID"
|
||||
// @Param scope query string false "Filter by transport scope name"
|
||||
// @Param type query string false "Filter by type: TRACE or PING (default: all)"
|
||||
// @Param since query int false "Filter by first_heard_at >= since (epoch ms)"
|
||||
// @Param until query int false "Filter by first_heard_at <= until (epoch ms)"
|
||||
// @Param cursor query int false "last_heard_at epoch ms of last item for pagination"
|
||||
@@ -73,7 +75,8 @@ func listTraceTags(reader api.Reader) http.HandlerFunc {
|
||||
iatas = append(iatas, regionIATAs...)
|
||||
}
|
||||
scope := r.URL.Query().Get("scope")
|
||||
tags, err := reader.ListTraceTags(r.Context(), iatas, scope, since, until, cursor, limit)
|
||||
traceType := strings.ToUpper(r.URL.Query().Get("type"))
|
||||
tags, err := reader.ListTraceTags(r.Context(), iatas, scope, traceType, since, until, cursor, limit)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusInternalServerError, "internal server error")
|
||||
return
|
||||
|
||||
@@ -172,7 +172,7 @@ type Reader interface {
|
||||
GetScopeByName(ctx context.Context, name string) (*ScopeDetail, error)
|
||||
|
||||
// ListTraceTags returns a paginated list of trace tags with aggregate metadata.
|
||||
ListTraceTags(ctx context.Context, iatas []string, scope string, since, until time.Time, cursor time.Time, limit int32) ([]TraceTagSummary, error)
|
||||
ListTraceTags(ctx context.Context, iatas []string, scope, traceType string, since, until time.Time, cursor time.Time, limit int32) ([]TraceTagSummary, error)
|
||||
|
||||
// GetTraceByTag returns all packets for a given trace tag with resolved routes.
|
||||
GetTraceByTag(ctx context.Context, tag string) (*TraceDetail, error)
|
||||
|
||||
+13
-11
@@ -10,28 +10,30 @@ type TraceTagSummary struct {
|
||||
LastHeardAt int64 `json:"lastHeardAt"` // epoch ms
|
||||
PacketCount int64 `json:"packetCount"` // number of packets with this trace tag
|
||||
IATACount int64 `json:"iataCount"` // number of distinct IATAs where heard
|
||||
TraceType string `json:"traceType"` // TRACE or PING
|
||||
}
|
||||
|
||||
// TracePacket is a single packet within a trace series, including its
|
||||
// resolved route derived from the trace path hashes.
|
||||
type TracePacket struct {
|
||||
PacketHash string `json:"packetHash"`
|
||||
RouteType int16 `json:"routeType"`
|
||||
RouteTypeName string `json:"routeTypeName"`
|
||||
Scope *string `json:"scope,omitempty"`
|
||||
FirstHeardAt int64 `json:"firstHeardAt"` // epoch ms
|
||||
LastHeardAt int64 `json:"lastHeardAt"` // epoch ms
|
||||
RawPath []RawHop `json:"rawPath"`
|
||||
ResolvedRoute []ResolvedHop `json:"resolvedRoute"`
|
||||
PacketHash string `json:"packetHash"` // hex-encoded packet hash
|
||||
RouteType int16 `json:"routeType"` // numeric route type
|
||||
RouteTypeName string `json:"routeTypeName"` // human-readable route type
|
||||
Scope *string `json:"scope,omitempty"` // transport scope name, if known
|
||||
FirstHeardAt int64 `json:"firstHeardAt"` // epoch ms
|
||||
LastHeardAt int64 `json:"lastHeardAt"` // epoch ms
|
||||
RawPath []RawHop `json:"rawPath"` // hops as received in the packet
|
||||
ResolvedRoute []ResolvedHop `json:"resolvedRoute"` // hops resolved to known nodes
|
||||
}
|
||||
|
||||
// RawHop is a single hop in a trace path as received in the packet.
|
||||
type RawHop struct {
|
||||
Hash string `json:"hash"` // hex-encoded path hash
|
||||
SNR *float32 `json:"snr,omitempty"`
|
||||
Hash string `json:"hash"` // hex-encoded path hash
|
||||
SNR *float32 `json:"snr,omitempty"` // signal-to-noise ratio in dB, if available
|
||||
}
|
||||
|
||||
// TraceDetail is the full trace series for a given trace tag.
|
||||
type TraceDetail struct {
|
||||
TraceTag string `json:"traceTag"` // hex-encoded 4-byte tag
|
||||
Packets []TracePacket `json:"packets"`
|
||||
Packets []TracePacket `json:"packets"` // all packets observed for this trace
|
||||
}
|
||||
|
||||
Vendored
+2
-2
@@ -389,6 +389,6 @@ func (cr *CachedReader) SearchCrossIATARoutes(ctx context.Context, fromHash, fro
|
||||
}
|
||||
|
||||
// ListTraceTags implements [api.Reader].
|
||||
func (cr *CachedReader) ListTraceTags(ctx context.Context, iatas []string, scope string, since, until time.Time, cursor time.Time, limit int32) ([]api.TraceTagSummary, error) {
|
||||
return cr.inner.ListTraceTags(ctx, iatas, scope, since, until, cursor, limit)
|
||||
func (cr *CachedReader) ListTraceTags(ctx context.Context, iatas []string, scope, traceType string, since, until time.Time, cursor time.Time, limit int32) ([]api.TraceTagSummary, error) {
|
||||
return cr.inner.ListTraceTags(ctx, iatas, scope, traceType, since, until, cursor, limit)
|
||||
}
|
||||
|
||||
@@ -447,9 +447,13 @@ func (w *Worker) handlePacket(ctx context.Context, iata, pubkeyHex string, raw [
|
||||
for _, b := range packet.Path {
|
||||
snrValues = append(snrValues, float32(int8(b))/4.0)
|
||||
}
|
||||
traceType := "TRACE"
|
||||
if len(hashes) == 1 {
|
||||
traceType = "PING"
|
||||
}
|
||||
pt := parsedTrace{
|
||||
Raw: hex.EncodeToString(packet.Payload),
|
||||
Type: "TRACE",
|
||||
Type: traceType,
|
||||
TraceTag: hex.EncodeToString(uint32ToBytes(trace.Tag)),
|
||||
AuthCode: trace.AuthCode,
|
||||
Flags: trace.Flags,
|
||||
|
||||
Reference in New Issue
Block a user