mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-08-30 23:58:19 +00:00
229 lines
14 KiB
Go
229 lines
14 KiB
Go
// Copyright 2026 Beacon Contributors
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
// Package api defines the response types and read interface for the Beacon REST API.
|
|
package api
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// Page is a generic paginated response envelope used by all list endpoints
|
|
// that support cursor-based pagination. NextCursor is the ID of the last item
|
|
// returned and should be passed as the cursor param in the next request.
|
|
// HasMore is true when additional results exist beyond the current page.
|
|
type Page[T any] struct {
|
|
Items []T `json:"items"`
|
|
NextCursor *int64 `json:"nextCursor,omitempty"`
|
|
HasMore bool `json:"hasMore"`
|
|
}
|
|
|
|
type Reader interface {
|
|
// ListIATAs returns all known IATA codes with display name and coordinates.
|
|
// IATAs are auto-created on first packet arrival from that location.
|
|
ListIATAs(ctx context.Context) ([]IATA, error)
|
|
|
|
// GetIATA returns a single IATA code by its 3-letter identifier.
|
|
// Returns nil, error if the IATA code is not found.
|
|
GetIATA(ctx context.Context, iata string) (*IATA, error)
|
|
|
|
// GetIATABorder returns the raw GeoJSON Feature JSON for the given IATA's border. Returns
|
|
// nil (no error) if the IATA exists but has no border configured -- the client renders
|
|
// that as "nothing to draw", not an error. Returns an error if the IATA itself is unknown.
|
|
GetIATABorder(ctx context.Context, iata string) (json.RawMessage, error)
|
|
|
|
// ListRegions returns a summary list of all regions ordered by display_order then name.
|
|
// Use GetRegion for full detail including associated IATAs.
|
|
ListRegions(ctx context.Context) ([]RegionSummary, error)
|
|
|
|
// GetRegion returns full detail for a single region including its associated IATA codes.
|
|
// Returns nil, pgx.ErrNoRows if the region is not found.
|
|
GetRegion(ctx context.Context, regionID int32) (*Region, error)
|
|
|
|
// GetRegionBySlug returns full detail for a single region by its URL-safe slug.
|
|
// Returns nil, pgx.ErrNoRows if the region is not found.
|
|
GetRegionBySlug(ctx context.Context, slug string) (*Region, error)
|
|
|
|
// ListChannels returns a paginated list of channels ordered by last seen.
|
|
// Includes both hashtag-derived and explicit key channels.
|
|
// Pass nil hash to skip hash filtering. Pass empty iatas to return all channels;
|
|
// IATAs must be uppercase.
|
|
// cursor is last_seen epoch ms of the last item; pass 0 to start from the beginning.
|
|
ListChannels(ctx context.Context, limit int32, hash []byte, iatas []string, cursor int64) (Page[ChannelSummary], error)
|
|
|
|
// GetChannel returns full detail for a single channel by its integer ID.
|
|
// Returns nil, pgx.ErrNoRows if the channel is not found.
|
|
GetChannel(ctx context.Context, channelID int32) (*Channel, error)
|
|
|
|
// ListChannelMessages returns paginated messages for a channel identified by its integer ID.
|
|
// Used by the /channels/{id}/messages endpoint.
|
|
// Pass a zero time.Time for since to return all messages up to limit.
|
|
// Pass empty string iata to return messages from all IATAs.
|
|
// Pass cursor=0 to start from the beginning.
|
|
ListChannelMessages(ctx context.Context, channelID *int32, since time.Time, limit int32, iatas []string, scope string, cursor int64) (Page[ChannelMessage], error)
|
|
|
|
// ListChannelMessagesByHash returns paginated messages for all channels matching the given hash.
|
|
// Used by the /messages?hash= endpoint. May return messages from multiple channels
|
|
// if the hash collides across different keys.
|
|
// Pass a zero time.Time for since to return all messages up to limit.
|
|
// Pass empty string iata to return messages from all IATAs.
|
|
// Pass cursor=0 to start from the beginning.
|
|
ListChannelMessagesByHash(ctx context.Context, hash []byte, since time.Time, limit int32, iatas []string, scope string, cursor int64) (Page[ChannelMessage], error)
|
|
|
|
// ListMessagesAfterID returns channel messages after the given message ID,
|
|
// ordered oldest first. Used for WS reconnect backfill.
|
|
ListMessagesAfterID(ctx context.Context, afterID int64, iatas []string, scope string, limit int32) ([]ChannelMessage, error)
|
|
|
|
// ListObservers returns a paginated list of observers with optional filters.
|
|
// All filter params are optional — pass empty string or nil to skip a filter.
|
|
// status is "online" or "offline" derived from last_status_at recency.
|
|
// cursor is last_seen epoch ms of the last observer; pass 0 to start from the beginning.
|
|
ListObservers(ctx context.Context, iatas []string, observerType, broker, status, name, scope string, cursor int64, limit int32) (Page[ObserverSummary], error)
|
|
|
|
// GetObserver returns full detail for a single observer by UUID.
|
|
// Returns nil, pgx.ErrNoRows if the observer is not found.
|
|
GetObserver(ctx context.Context, observerID uuid.UUID) (*Observer, error)
|
|
|
|
// GetObserverTelemetry returns telemetry points for an observer within the given time range.
|
|
// since and until define the window; pass zero times to use defaults (last 24h).
|
|
GetObserverTelemetry(ctx context.Context, observerID uuid.UUID, since, until time.Time, afterID int64) (*ObserverTelemetry, error)
|
|
|
|
// GetObserverTelemetryBucketed returns telemetry points for an observer bucketed into N-hour intervals.
|
|
GetObserverTelemetryBucketed(ctx context.Context, observerID uuid.UUID, since, until time.Time, bucketHours int32) ([]ObserverTelemetryPoint, error)
|
|
|
|
// GetObserverScopes returns the names of all transport scopes an observer has
|
|
// been seen forwarding packets for, ordered alphabetically.
|
|
GetObserverScopes(ctx context.Context, observerID uuid.UUID) ([]string, error)
|
|
|
|
// ListObserverAdverts returns a paginated list of advert packets heard by an observer.
|
|
// Pass cursor=0 to start from the beginning.
|
|
ListObserverAdverts(ctx context.Context, observerID uuid.UUID, cursor int64, limit int32) (Page[AdvertObservation], error)
|
|
|
|
// ListNodes returns a paginated list of nodes with optional filters.
|
|
// When includeNeighbors is true, each NodeSummary's NeighborIDs field is
|
|
// populated with the distinct set of neighbor node IDs (across all
|
|
// IATAs); otherwise it's left nil to avoid the extra aggregation.
|
|
ListNodes(ctx context.Context, nodeType int16, iatas []string, supportsMultibytePaths, supportsMultibyteTraces *bool, pubkey []byte, pubkeyPrefix, name, scope string, cursor int64, limit int32, includeNeighbors bool) (Page[NodeSummary], error)
|
|
|
|
// GetNode returns full detail for a single node by UUID.
|
|
// Returns nil, pgx.ErrNoRows if the node is not found.
|
|
GetNode(ctx context.Context, nodeID uuid.UUID) (*Node, error)
|
|
|
|
// GetNodesByIDs returns a map of node ID to resolved node details for the given IDs.
|
|
GetNodesByIDs(ctx context.Context, ids []uuid.UUID) (map[uuid.UUID]*ResolvedNode, error)
|
|
|
|
// ListNodeObservations returns a paginated list of packet observations originating from a node.
|
|
// Pass cursor=0 to start from the beginning.
|
|
ListNodeObservations(ctx context.Context, nodeID uuid.UUID, cursor int64, limit int32) (Page[PacketObservationSummary], error)
|
|
|
|
// ListPackets returns a paginated list of packets with the latest observation rolled in.
|
|
// Pass 0 for payloadType/routeType to skip those filters.
|
|
// Pass nil for iatas, zero times for since/until to skip those filters.
|
|
// Unfiltered lists order by global last_heard_at; when iatas are set, ordering
|
|
// and the cursor are site-local (heard_at at the requested sites) instead.
|
|
// cursor is epoch ms in either case; pass 0 to start from the beginning.
|
|
// payloadTypes/routeTypes/scopes are OR'd within each filter, ANDed across filters; pass
|
|
// nil/empty to skip a filter.
|
|
ListPackets(ctx context.Context, payloadTypes, routeTypes []int16, iatas []string, scopes []string, since, until time.Time, cursor int64, limit int32) (Page[PacketSummary], error)
|
|
|
|
// ListPacketsAfterID returns packets with observations after the given observation ID,
|
|
// ordered oldest first. Used for WS reconnect backfill.
|
|
ListPacketsAfterID(ctx context.Context, afterObservationID int64, payloadType, routeType int16, iatas []string, scope string, limit int32) ([]PacketSummary, error)
|
|
|
|
// GetPacket returns full packet detail including all observations with radio settings.
|
|
// Returns nil, pgx.ErrNoRows if not found.
|
|
GetPacket(ctx context.Context, packetHash []byte) (*Packet, error)
|
|
// GetRadioPresets returns radio preset usage grouped by preset and IATA(s).
|
|
// Pass empty string for preset or nil iatas to skip those filters.
|
|
GetRadioPresets(ctx context.Context, preset string, iatas []string) ([]RadioPreset, error)
|
|
|
|
// GetStatsOverview returns top-line network figures for the last 24 hours.
|
|
// Pass nil iatas to return stats across all IATAs.
|
|
GetStatsOverview(ctx context.Context, iatas []string) (*StatsOverview, error)
|
|
|
|
// GetStatsObservations returns hourly observation counts for charting.
|
|
// Pass nil for iatas to return stats across all IATAs.
|
|
// since defines the start of the window; pass zero time for default (last 7 days).
|
|
GetStatsObservations(ctx context.Context, iatas []string, since time.Time) ([]ObservationPoint, error)
|
|
|
|
// GetStatsPayloadBreakdown returns observation counts grouped by payload type.
|
|
// Pass nil for iatas to return stats across all IATAs.
|
|
// since defines the start of the window; pass zero time for default (last 24h).
|
|
GetStatsPayloadBreakdown(ctx context.Context, iatas []string, since time.Time) ([]PayloadBreakdownItem, error)
|
|
|
|
// GetStatsTopNodes returns the top N nodes by observation count.
|
|
// Pass nil for iatas to return stats across all IATAs.
|
|
GetStatsTopNodes(ctx context.Context, iatas []string, limit int32) ([]TopNode, error)
|
|
|
|
// GetStatsTopObservers returns the top N observers by observation count.
|
|
// Pass nil for iatas to return stats across all IATAs.
|
|
// since defines the start of the window; pass zero time for default (last 24h).
|
|
GetStatsTopObservers(ctx context.Context, iatas []string, since time.Time, limit int32) ([]TopObserver, error)
|
|
|
|
// GetStatsTopAdvertisers returns the top N nodes by distinct ADVERT packet count.
|
|
// Pass nil for iatas to return stats across all IATAs.
|
|
// since defines the start of the window; pass zero time for default (last 24h).
|
|
GetStatsTopAdvertisers(ctx context.Context, iatas []string, since time.Time, limit int32) ([]TopAdvertiser, error)
|
|
|
|
// GetStatsClockDrift returns repeaters/room servers whose most recent advert-derived
|
|
// clock drift exceeds the configured threshold (nodes.clock_drift_threshold), worst
|
|
// drift first. Pass nil for iatas to return across all IATAs. Unlike the other stats
|
|
// endpoints this isn't time-windowed -- it reflects each node's current drift state.
|
|
GetStatsClockDrift(ctx context.Context, iatas []string, limit int32) ([]ClockDriftEntry, error)
|
|
|
|
// GetStatsTopTalkers returns the top N companion names by decrypted channel message count.
|
|
// Pass nil for iatas to return stats across all IATAs.
|
|
// since defines the start of the window; pass zero time for default (last 24h).
|
|
GetStatsTopTalkers(ctx context.Context, iatas []string, since time.Time, limit int32) ([]TopTalker, error)
|
|
|
|
// GetScopeStats returns aggregate packet, observer and node counts per transport scope.
|
|
GetScopeStats(ctx context.Context) ([]ScopeStats, error)
|
|
|
|
// GetStatsNodeTypes returns node counts grouped by type, optionally filtered by IATA.
|
|
GetStatsNodeTypes(ctx context.Context, iatas []string) ([]NodeTypeCount, error)
|
|
|
|
// GetScopeNames returns the names of all configured transport scopes, ordered alphabetically.
|
|
// Use when no geographic filter is applied — returns names only for a lightweight response.
|
|
GetScopeNames(ctx context.Context) ([]string, error)
|
|
|
|
// GetScopesByIATAs returns scope summaries filtered by the given IATA codes,
|
|
// including observer, node and IATA counts. Expands region/regionId to IATAs automatically.
|
|
GetScopesByIATAs(ctx context.Context, iatas []string) ([]ScopeSummary, error)
|
|
|
|
// GetScopeByName returns full detail for a single scope by its normalized name (e.g. "#bc"),
|
|
// including packet count, observer count, node count, and the list of IATAs it is active in.
|
|
// Returns nil if the scope is not found.
|
|
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, 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)
|
|
|
|
// ListKnownRoutes returns known routes filtered by IATA and optional hop count.
|
|
ListKnownRoutes(ctx context.Context, iata string, hopCount int32, cursor time.Time, limit int32) ([]KnownRoute, error)
|
|
|
|
// SearchKnownRoutes returns known routes containing a path from source to destination hash.
|
|
SearchKnownRoutes(ctx context.Context, iata, fromHash, toHash string) ([]KnownRoute, error)
|
|
|
|
// GetNodeNeighbors returns the neighbors of a node ordered by most recently seen.
|
|
GetNodeNeighbors(ctx context.Context, nodeID uuid.UUID) ([]NodeNeighbor, error)
|
|
|
|
// GetKnownRoutesByNode returns all known routes in a given IATA that contain
|
|
// the specified node UUID anywhere in their hop sequence.
|
|
GetKnownRoutesByNode(ctx context.Context, iata string, nodeID uuid.UUID) ([]KnownRoute, error)
|
|
|
|
// GetCrossIATANeighbors returns neighbors of a node that were observed in a
|
|
// different IATA — indicating a potential cross-IATA radio link.
|
|
GetCrossIATANeighbors(ctx context.Context, nodeID uuid.UUID, iata string) ([]NodeNeighbor, error)
|
|
|
|
// SearchCrossIATARoutes finds routes that cross IATA boundaries between
|
|
// a source node/IATA and a destination node/IATA.
|
|
SearchCrossIATARoutes(ctx context.Context, fromHash, fromIATA, toHash, toIATA string) ([]CrossIATARoute, error)
|
|
}
|