mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-10-22 22:21:27 +00:00
feat(topology): serve bounded route links and identify decoded Public traffic
This commit is contained in:
1 parent
53f2467864
commit
84ac3c8781
19 files changed
+486
No files matched your search
@@ -20,6 +20,7 @@ import (
|
||||
func RoutesRouter(reader api.Reader) http.Handler {
|
||||
r := chi.NewRouter()
|
||||
r.Get("/", listKnownRoutes(reader))
|
||||
r.Get("/topology", getTopologyLinks(reader))
|
||||
r.Get("/cross", searchCrossIATARoutes(reader))
|
||||
r.Get("/search", searchKnownRoutes(reader))
|
||||
r.Get("/{iata}/{pathKey}/observations", getRouteEvidence(reader))
|
||||
|
||||
@@ -417,3 +417,7 @@ func (s stubReader) GetRouteEvidence(ctx context.Context, iata, key string, q ap
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s stubReader) GetTopologyLinks(_ context.Context, _ []string, _ time.Time, _ time.Time) (*api.TopologyLinks, error) {
|
||||
return &api.TopologyLinks{}, nil
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2026 Beacon Contributors
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/MeshCore-Beacon/beacon-server/internal/api"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// getTopologyLinks godoc
|
||||
// @Summary Unique adjacent route links for a topology snapshot
|
||||
// @Description Returns up to 100000 undirected node-ID pairs, deduplicated across known routes. Minute-aligned window includes the current minute. Existing rate limits apply; no per-node lookup or route pagination is needed. Capped is true when more links exist.
|
||||
// @Tags Routes
|
||||
// @Produce json
|
||||
// @Param window query string false "History window: 15m (default), 1h, or 24h"
|
||||
// @Param iatas query string false "Comma-separated reception IATA codes"
|
||||
// @Success 200 {object} api.TopologyLinks
|
||||
// @Failure 400 {object} handlers.APIError
|
||||
// @Failure 503 {object} handlers.APIError
|
||||
// @Router /routes/topology [get]
|
||||
func getTopologyLinks(reader api.Reader) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
window := r.URL.Query().Get("window")
|
||||
if window == "" {
|
||||
window = "15m"
|
||||
}
|
||||
duration, ok := map[string]time.Duration{"15m": 15 * time.Minute, "1h": time.Hour, "24h": 24 * time.Hour}[window]
|
||||
if !ok {
|
||||
respondError(w, http.StatusBadRequest, "window must be 15m, 1h or 24h")
|
||||
return
|
||||
}
|
||||
until := time.Now().UTC().Truncate(time.Minute).Add(time.Minute)
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
|
||||
defer cancel()
|
||||
links, err := reader.GetTopologyLinks(ctx, parseIATAs(r), until.Add(-duration), until)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusServiceUnavailable, "route snapshot unavailable; try again shortly")
|
||||
return
|
||||
}
|
||||
respond(w, http.StatusOK, links)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2026 Beacon Contributors
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTopologyWindowValidation(t *testing.T) {
|
||||
for _, window := range []string{"", "15m", "1h", "24h", "72h", "0", "garbage"} {
|
||||
w := httptest.NewRecorder()
|
||||
RoutesRouter(stubReader{}).ServeHTTP(w, httptest.NewRequest("GET", "/topology?window="+window, nil))
|
||||
want := 200
|
||||
if window == "72h" || window == "0" || window == "garbage" {
|
||||
want = 400
|
||||
}
|
||||
if w.Code != want {
|
||||
t.Fatalf("%s: %d", window, w.Code)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -224,6 +224,8 @@ type Reader interface {
|
||||
// GetTraceByTag returns all packets for a given trace tag with resolved routes.
|
||||
GetTraceByTag(ctx context.Context, tag string) (*TraceDetail, error)
|
||||
|
||||
GetTopologyLinks(ctx context.Context, iatas []string, since, until time.Time) (*TopologyLinks, error)
|
||||
|
||||
// ListKnownRoutes returns known routes filtered by IATA and optional hop count.
|
||||
// A nonzero cursorID breaks ties within the cursor's millisecond.
|
||||
ListKnownRoutes(ctx context.Context, iata string, hopCount int32, cursor time.Time, cursorID int64, limit int32) ([]KnownRoute, error)
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright 2026 Beacon Contributors
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
package api
|
||||
|
||||
import "github.com/google/uuid"
|
||||
|
||||
// TopologyLinks is a bounded set of undirected, adjacent known-route segments.
|
||||
// It does not imply radio reachability or join across missing path entries.
|
||||
type TopologyLinks struct {
|
||||
Links [][2]uuid.UUID `json:"links"`
|
||||
Capped bool `json:"capped"`
|
||||
Since int64 `json:"since"`
|
||||
Until int64 `json:"until"`
|
||||
}
|
||||
Reference in new issue
Block a user