mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-10-23 03:01:41 +00:00
add scopes api
This commit is contained in:
10 files changed
+595
No files matched your search
@@ -38,6 +38,41 @@ SELECT name, transport_key, key_fingerprint FROM transport_scopes ORDER BY name;
|
||||
-- name: GetTransportScopeByName :one
|
||||
SELECT id FROM transport_scopes WHERE name = $1;
|
||||
|
||||
-- name: GetScopeNames :many
|
||||
SELECT name FROM transport_scopes ORDER BY name;
|
||||
|
||||
-- name: GetScopesByIATAs :many
|
||||
SELECT
|
||||
ts.name,
|
||||
COUNT(DISTINCT os.observer_id) AS observer_count,
|
||||
COUNT(DISTINCT n.id) AS node_count,
|
||||
COUNT(DISTINCT po.iata) AS iata_count
|
||||
FROM transport_scopes ts
|
||||
LEFT JOIN observer_scopes os ON os.scope_id = ts.id
|
||||
LEFT JOIN observers o ON o.id = os.observer_id
|
||||
LEFT JOIN packet_observations po ON po.observer_id = o.id
|
||||
LEFT JOIN nodes n ON n.default_scope_id = ts.id
|
||||
WHERE ($1::text = '' OR po.iata = ANY(string_to_array($1::text, ',')))
|
||||
GROUP BY ts.name
|
||||
ORDER BY ts.name;
|
||||
|
||||
-- name: GetScopeByName :one
|
||||
SELECT
|
||||
ts.name,
|
||||
COUNT(DISTINCT p.packet_hash) AS packet_count,
|
||||
COUNT(DISTINCT os.observer_id) AS observer_count,
|
||||
COUNT(DISTINCT n.id) AS node_count,
|
||||
COUNT(DISTINCT po.iata) AS iata_count,
|
||||
array_remove(array_agg(DISTINCT po.iata ORDER BY po.iata), NULL)::text[] AS iatas
|
||||
FROM transport_scopes ts
|
||||
LEFT JOIN packets p ON p.scope_id = ts.id
|
||||
LEFT JOIN observer_scopes os ON os.scope_id = ts.id
|
||||
LEFT JOIN observers o ON o.id = os.observer_id
|
||||
LEFT JOIN packet_observations po ON po.observer_id = o.id
|
||||
LEFT JOIN nodes n ON n.default_scope_id = ts.id
|
||||
WHERE ts.name = $1
|
||||
GROUP BY ts.name;
|
||||
|
||||
-- ============================================================
|
||||
-- OBSERVERS
|
||||
-- ============================================================
|
||||
|
||||
@@ -2,8 +2,10 @@ package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
sqlc "github.com/MeshCore-Beacon/beacon-server/db/sqlc"
|
||||
"github.com/MeshCore-Beacon/beacon-server/internal/api"
|
||||
"github.com/MeshCore-Beacon/beacon-server/internal/scopestore"
|
||||
)
|
||||
|
||||
@@ -39,3 +41,43 @@ func (s *Store) GetTransportScopes(ctx context.Context) ([]scopestore.Entry, err
|
||||
func (s *Store) GetTransportScopeByName(ctx context.Context, name string) (int32, error) {
|
||||
return s.q.GetTransportScopeByName(ctx, name)
|
||||
}
|
||||
|
||||
// GetScopeNames returns the names of all configured transport scopes.
|
||||
func (s *Store) GetScopeNames(ctx context.Context) ([]string, error) {
|
||||
return s.q.GetScopeNames(ctx)
|
||||
}
|
||||
|
||||
// GetScopesByIATAs returns scope summaries filtered by the given IATA codes.
|
||||
func (s *Store) GetScopesByIATAs(ctx context.Context, iatas []string) ([]api.ScopeSummary, error) {
|
||||
iataFilter := strings.Join(iatas, ",")
|
||||
rows, err := s.q.GetScopesByIATAs(ctx, iataFilter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items := make([]api.ScopeSummary, 0, len(rows))
|
||||
for _, r := range rows {
|
||||
items = append(items, api.ScopeSummary{
|
||||
Name: r.Name,
|
||||
ObserverCount: r.ObserverCount,
|
||||
NodeCount: r.NodeCount,
|
||||
IATACount: r.IataCount,
|
||||
})
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
// GetScopeByName returns full detail for a single scope by its normalized name.
|
||||
func (s *Store) GetScopeByName(ctx context.Context, name string) (*api.ScopeDetail, error) {
|
||||
row, err := s.q.GetScopeByName(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &api.ScopeDetail{
|
||||
Name: row.Name,
|
||||
PacketCount: row.PacketCount,
|
||||
ObserverCount: row.ObserverCount,
|
||||
NodeCount: row.NodeCount,
|
||||
IATACount: row.IataCount,
|
||||
IATAs: row.Iatas,
|
||||
}, nil
|
||||
}
|
||||
@@ -790,6 +790,71 @@ func (q *Queries) GetRegionIATAs(ctx context.Context, regionID int32) ([]string,
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getScopeByName = `-- name: GetScopeByName :one
|
||||
SELECT
|
||||
ts.name,
|
||||
COUNT(DISTINCT p.packet_hash) AS packet_count,
|
||||
COUNT(DISTINCT os.observer_id) AS observer_count,
|
||||
COUNT(DISTINCT n.id) AS node_count,
|
||||
COUNT(DISTINCT po.iata) AS iata_count,
|
||||
array_remove(array_agg(DISTINCT po.iata ORDER BY po.iata), NULL)::text[] AS iatas
|
||||
FROM transport_scopes ts
|
||||
LEFT JOIN packets p ON p.scope_id = ts.id
|
||||
LEFT JOIN observer_scopes os ON os.scope_id = ts.id
|
||||
LEFT JOIN observers o ON o.id = os.observer_id
|
||||
LEFT JOIN packet_observations po ON po.observer_id = o.id
|
||||
LEFT JOIN nodes n ON n.default_scope_id = ts.id
|
||||
WHERE ts.name = $1
|
||||
GROUP BY ts.name
|
||||
`
|
||||
|
||||
type GetScopeByNameRow struct {
|
||||
Name string `json:"name"`
|
||||
PacketCount int64 `json:"packet_count"`
|
||||
ObserverCount int64 `json:"observer_count"`
|
||||
NodeCount int64 `json:"node_count"`
|
||||
IataCount int64 `json:"iata_count"`
|
||||
Iatas []string `json:"iatas"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetScopeByName(ctx context.Context, name string) (GetScopeByNameRow, error) {
|
||||
row := q.db.QueryRow(ctx, getScopeByName, name)
|
||||
var i GetScopeByNameRow
|
||||
err := row.Scan(
|
||||
&i.Name,
|
||||
&i.PacketCount,
|
||||
&i.ObserverCount,
|
||||
&i.NodeCount,
|
||||
&i.IataCount,
|
||||
&i.Iatas,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getScopeNames = `-- name: GetScopeNames :many
|
||||
SELECT name FROM transport_scopes ORDER BY name
|
||||
`
|
||||
|
||||
func (q *Queries) GetScopeNames(ctx context.Context) ([]string, error) {
|
||||
rows, err := q.db.Query(ctx, getScopeNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []string{}
|
||||
for rows.Next() {
|
||||
var name string
|
||||
if err := rows.Scan(&name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, name)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getScopeStats = `-- name: GetScopeStats :many
|
||||
SELECT
|
||||
ts.name,
|
||||
@@ -836,6 +901,54 @@ func (q *Queries) GetScopeStats(ctx context.Context) ([]GetScopeStatsRow, error)
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getScopesByIATAs = `-- name: GetScopesByIATAs :many
|
||||
SELECT
|
||||
ts.name,
|
||||
COUNT(DISTINCT os.observer_id) AS observer_count,
|
||||
COUNT(DISTINCT n.id) AS node_count,
|
||||
COUNT(DISTINCT po.iata) AS iata_count
|
||||
FROM transport_scopes ts
|
||||
LEFT JOIN observer_scopes os ON os.scope_id = ts.id
|
||||
LEFT JOIN observers o ON o.id = os.observer_id
|
||||
LEFT JOIN packet_observations po ON po.observer_id = o.id
|
||||
LEFT JOIN nodes n ON n.default_scope_id = ts.id
|
||||
WHERE ($1::text = '' OR po.iata = ANY(string_to_array($1::text, ',')))
|
||||
GROUP BY ts.name
|
||||
ORDER BY ts.name
|
||||
`
|
||||
|
||||
type GetScopesByIATAsRow struct {
|
||||
Name string `json:"name"`
|
||||
ObserverCount int64 `json:"observer_count"`
|
||||
NodeCount int64 `json:"node_count"`
|
||||
IataCount int64 `json:"iata_count"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetScopesByIATAs(ctx context.Context, dollar_1 string) ([]GetScopesByIATAsRow, error) {
|
||||
rows, err := q.db.Query(ctx, getScopesByIATAs, dollar_1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []GetScopesByIATAsRow{}
|
||||
for rows.Next() {
|
||||
var i GetScopesByIATAsRow
|
||||
if err := rows.Scan(
|
||||
&i.Name,
|
||||
&i.ObserverCount,
|
||||
&i.NodeCount,
|
||||
&i.IataCount,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getStatsOverview = `-- name: GetStatsOverview :one
|
||||
|
||||
SELECT
|
||||
|
||||
+111
@@ -1207,6 +1207,91 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/scopes": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Scopes"
|
||||
],
|
||||
"summary": "List transport scopes",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Filter by IATA code(s), comma-separated",
|
||||
"name": "iatas",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Filter by region slug",
|
||||
"name": "region",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Filter by region ID",
|
||||
"name": "regionId",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/internal_api_handlers.APIError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/scopes/{name}": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Scopes"
|
||||
],
|
||||
"summary": "Get scope detail by name",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Scope name e.g. %23bc (URL-encoded #bc)",
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_MeshCore-Beacon_beacon-server_internal_api.ScopeDetail"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/internal_api_handlers.APIError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/internal_api_handlers.APIError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/stats/observations": {
|
||||
"get": {
|
||||
"produces": [
|
||||
@@ -2505,6 +2590,32 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"github_com_MeshCore-Beacon_beacon-server_internal_api.ScopeDetail": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"iataCount": {
|
||||
"type": "integer"
|
||||
},
|
||||
"iatas": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"nodeCount": {
|
||||
"type": "integer"
|
||||
},
|
||||
"observerCount": {
|
||||
"type": "integer"
|
||||
},
|
||||
"packetCount": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"github_com_MeshCore-Beacon_beacon-server_internal_api.ScopeStats": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -1205,6 +1205,91 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/scopes": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Scopes"
|
||||
],
|
||||
"summary": "List transport scopes",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Filter by IATA code(s), comma-separated",
|
||||
"name": "iatas",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Filter by region slug",
|
||||
"name": "region",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "Filter by region ID",
|
||||
"name": "regionId",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/internal_api_handlers.APIError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/scopes/{name}": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Scopes"
|
||||
],
|
||||
"summary": "Get scope detail by name",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Scope name e.g. %23bc (URL-encoded #bc)",
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_MeshCore-Beacon_beacon-server_internal_api.ScopeDetail"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/internal_api_handlers.APIError"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/internal_api_handlers.APIError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/stats/observations": {
|
||||
"get": {
|
||||
"produces": [
|
||||
@@ -2503,6 +2588,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"github_com_MeshCore-Beacon_beacon-server_internal_api.ScopeDetail": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"iataCount": {
|
||||
"type": "integer"
|
||||
},
|
||||
"iatas": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"nodeCount": {
|
||||
"type": "integer"
|
||||
},
|
||||
"observerCount": {
|
||||
"type": "integer"
|
||||
},
|
||||
"packetCount": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"github_com_MeshCore-Beacon_beacon-server_internal_api.ScopeStats": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -708,6 +708,23 @@ definitions:
|
||||
description: hex-encoded prefix used for resolution
|
||||
type: string
|
||||
type: object
|
||||
github_com_MeshCore-Beacon_beacon-server_internal_api.ScopeDetail:
|
||||
properties:
|
||||
iataCount:
|
||||
type: integer
|
||||
iatas:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
name:
|
||||
type: string
|
||||
nodeCount:
|
||||
type: integer
|
||||
observerCount:
|
||||
type: integer
|
||||
packetCount:
|
||||
type: integer
|
||||
type: object
|
||||
github_com_MeshCore-Beacon_beacon-server_internal_api.ScopeStats:
|
||||
properties:
|
||||
name:
|
||||
@@ -1576,6 +1593,61 @@ paths:
|
||||
summary: Get a single region
|
||||
tags:
|
||||
- Regions
|
||||
/scopes:
|
||||
get:
|
||||
parameters:
|
||||
- description: Filter by IATA code(s), comma-separated
|
||||
in: query
|
||||
name: iatas
|
||||
type: string
|
||||
- description: Filter by region slug
|
||||
in: query
|
||||
name: region
|
||||
type: string
|
||||
- description: Filter by region ID
|
||||
in: query
|
||||
name: regionId
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
type: object
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/internal_api_handlers.APIError'
|
||||
summary: List transport scopes
|
||||
tags:
|
||||
- Scopes
|
||||
/scopes/{name}:
|
||||
get:
|
||||
parameters:
|
||||
- description: 'Scope name e.g. %23bc (URL-encoded #bc)'
|
||||
in: path
|
||||
name: name
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/github_com_MeshCore-Beacon_beacon-server_internal_api.ScopeDetail'
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
$ref: '#/definitions/internal_api_handlers.APIError'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/internal_api_handlers.APIError'
|
||||
summary: Get scope detail by name
|
||||
tags:
|
||||
- Scopes
|
||||
/stats/observations:
|
||||
get:
|
||||
parameters:
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/MeshCore-Beacon/beacon-server/internal/api"
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
// ScopesRouter mounts all /scopes routes onto a subrouter.
|
||||
//
|
||||
// GET /scopes → listScopes
|
||||
// GET /scopes/{name} → getScope
|
||||
func ScopesRouter(reader api.Reader) http.Handler {
|
||||
r := chi.NewRouter()
|
||||
r.Get("/", listScopes(reader))
|
||||
r.Get("/{name}", getScope(reader))
|
||||
return r
|
||||
}
|
||||
|
||||
// listScopes godoc
|
||||
//
|
||||
// @Summary List transport scopes
|
||||
// @Tags Scopes
|
||||
// @Produce json
|
||||
// @Param iatas query string false "Filter by IATA code(s), comma-separated"
|
||||
// @Param region query string false "Filter by region slug"
|
||||
// @Param regionId query int false "Filter by region ID"
|
||||
// @Success 200 {object} object
|
||||
// @Failure 500 {object} handlers.APIError
|
||||
// @Router /scopes [get]
|
||||
func listScopes(reader api.Reader) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
iatas := parseIATAs(r)
|
||||
if regionIDStr := r.URL.Query().Get("regionId"); regionIDStr != "" || r.URL.Query().Get("region") != "" {
|
||||
regionIATAs, err := resolveRegionIATAs(r.Context(), r.URL.Query().Get("regionId"), r.URL.Query().Get("region"), reader)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
iatas = append(iatas, regionIATAs...)
|
||||
}
|
||||
if len(iatas) == 0 {
|
||||
names, err := reader.GetScopeNames(r.Context())
|
||||
if err != nil {
|
||||
respondError(w, http.StatusInternalServerError, "internal server error")
|
||||
return
|
||||
}
|
||||
respond(w, http.StatusOK, names)
|
||||
return
|
||||
}
|
||||
scopes, err := reader.GetScopesByIATAs(r.Context(), iatas)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusInternalServerError, "internal server error")
|
||||
return
|
||||
}
|
||||
respond(w, http.StatusOK, scopes)
|
||||
}
|
||||
}
|
||||
|
||||
// getScope godoc
|
||||
//
|
||||
// @Summary Get scope detail by name
|
||||
// @Tags Scopes
|
||||
// @Produce json
|
||||
// @Param name path string true "Scope name e.g. %23bc (URL-encoded #bc)"
|
||||
// @Success 200 {object} api.ScopeDetail
|
||||
// @Failure 404 {object} handlers.APIError
|
||||
// @Failure 500 {object} handlers.APIError
|
||||
// @Router /scopes/{name} [get]
|
||||
func getScope(reader api.Reader) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
name := chi.URLParam(r, "name")
|
||||
scope, err := reader.GetScopeByName(r.Context(), name)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusNotFound, "scope not found")
|
||||
return
|
||||
}
|
||||
respond(w, http.StatusOK, scope)
|
||||
}
|
||||
}
|
||||
@@ -123,4 +123,14 @@ type Reader interface {
|
||||
GetStatsTopObservers(ctx context.Context, iata string, since time.Time, limit int32) ([]TopObserver, error)
|
||||
// GetScopeStats returns aggregate packet, observer and node counts per transport scope.
|
||||
GetScopeStats(ctx context.Context) ([]ScopeStats, 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)
|
||||
}
|
||||
@@ -71,6 +71,7 @@ func New(h *hub.Hub, reader api.Reader, workers []*ingest.Worker, maxConnsPerIP
|
||||
r.Mount("/messages", handlers.MessagesRouter(reader))
|
||||
r.Mount("/iatas", handlers.IATAsRouter(reader))
|
||||
r.Mount("/regions", handlers.RegionsRouter(reader))
|
||||
r.Mount("/scopes", handlers.ScopesRouter(reader))
|
||||
r.Mount("/stats", handlers.StatsRouter(reader))
|
||||
})
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package api
|
||||
|
||||
// ScopeSummary is the minimal scope representation used in filtered list responses.
|
||||
type ScopeSummary struct {
|
||||
Name string `json:"name"`
|
||||
ObserverCount int64 `json:"observerCount"`
|
||||
NodeCount int64 `json:"nodeCount"`
|
||||
IATACount int64 `json:"iataCount"`
|
||||
}
|
||||
|
||||
// ScopeDetail is the full scope representation including packet count and IATA list.
|
||||
type ScopeDetail struct {
|
||||
Name string `json:"name"`
|
||||
PacketCount int64 `json:"packetCount"`
|
||||
ObserverCount int64 `json:"observerCount"`
|
||||
NodeCount int64 `json:"nodeCount"`
|
||||
IATACount int64 `json:"iataCount"`
|
||||
IATAs []string `json:"iatas"`
|
||||
}
|
||||
Reference in new issue
Block a user