mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-09-01 16:48:19 +00:00
feat: allow filter by not supporting multibyte path/trace
depends on path resolution so will not return true yet for any nodes
This commit is contained in:
+10
-2
@@ -344,8 +344,16 @@ FROM nodes n
|
||||
LEFT JOIN node_iatas ni ON ni.node_id = n.id
|
||||
WHERE
|
||||
($1 = 0 OR n.node_type = $1)
|
||||
AND (NOT $3 OR n.supports_multibyte_paths = TRUE)
|
||||
AND (NOT $4 OR n.supports_multibyte_traces = TRUE)
|
||||
AND (
|
||||
$3::text = 'any'
|
||||
OR ($3::text = 'true' AND n.supports_multibyte_paths = TRUE)
|
||||
OR ($3::text = 'false' AND n.supports_multibyte_paths = FALSE)
|
||||
)
|
||||
AND (
|
||||
$4::text = 'any'
|
||||
OR ($4::text = 'true' AND n.supports_multibyte_traces = TRUE)
|
||||
OR ($4::text = 'false' AND n.supports_multibyte_traces = FALSE)
|
||||
)
|
||||
AND ($5::bytea IS NULL OR n.public_key = $5)
|
||||
AND ($6 = '' OR n.name ILIKE '%' || $6 || '%')
|
||||
AND ($7::timestamptz IS NULL OR n.last_seen < $7)
|
||||
|
||||
+12
-4
@@ -1370,8 +1370,16 @@ FROM nodes n
|
||||
LEFT JOIN node_iatas ni ON ni.node_id = n.id
|
||||
WHERE
|
||||
($1 = 0 OR n.node_type = $1)
|
||||
AND (NOT $3 OR n.supports_multibyte_paths = TRUE)
|
||||
AND (NOT $4 OR n.supports_multibyte_traces = TRUE)
|
||||
AND (
|
||||
$3::text = 'any'
|
||||
OR ($3::text = 'true' AND n.supports_multibyte_paths = TRUE)
|
||||
OR ($3::text = 'false' AND n.supports_multibyte_paths = FALSE)
|
||||
)
|
||||
AND (
|
||||
$4::text = 'any'
|
||||
OR ($4::text = 'true' AND n.supports_multibyte_traces = TRUE)
|
||||
OR ($4::text = 'false' AND n.supports_multibyte_traces = FALSE)
|
||||
)
|
||||
AND ($5::bytea IS NULL OR n.public_key = $5)
|
||||
AND ($6 = '' OR n.name ILIKE '%' || $6 || '%')
|
||||
AND ($7::timestamptz IS NULL OR n.last_seen < $7)
|
||||
@@ -1384,8 +1392,8 @@ LIMIT $8
|
||||
type ListNodesParams struct {
|
||||
Column1 interface{} `json:"column_1"`
|
||||
Column2 interface{} `json:"column_2"`
|
||||
Column3 interface{} `json:"column_3"`
|
||||
Column4 interface{} `json:"column_4"`
|
||||
Column3 string `json:"column_3"`
|
||||
Column4 string `json:"column_4"`
|
||||
Column5 []byte `json:"column_5"`
|
||||
Column6 interface{} `json:"column_6"`
|
||||
Column7 pgtype.Timestamptz `json:"column_7"`
|
||||
|
||||
+13
-3
@@ -818,7 +818,7 @@ func (s *Store) ListObserverAdverts(ctx context.Context, observerID uuid.UUID, c
|
||||
// ListNodes returns a paginated list of nodes with optional filters.
|
||||
// Pass 0 for nodeType, empty string for iata/name, nil for pubkey to skip those filters.
|
||||
// cursor is last_seen epoch ms; pass 0 to start from the beginning.
|
||||
func (s *Store) ListNodes(ctx context.Context, nodeType int16, iata string, supportsMultibytePaths, supportsMultibyteTraces bool, pubkey []byte, name string, cursor int64, limit int32) (api.Page[api.NodeSummary], error) {
|
||||
func (s *Store) ListNodes(ctx context.Context, nodeType int16, iata string, supportsMultibytePaths, supportsMultibyteTraces *bool, pubkey []byte, name string, cursor int64, limit int32) (api.Page[api.NodeSummary], error) {
|
||||
var cursorTS pgtype.Timestamptz
|
||||
if cursor > 0 {
|
||||
cursorTS = pgtype.Timestamptz{Time: time.UnixMilli(cursor), Valid: true}
|
||||
@@ -826,8 +826,8 @@ func (s *Store) ListNodes(ctx context.Context, nodeType int16, iata string, supp
|
||||
rows, err := s.q.ListNodes(ctx, sqlc.ListNodesParams{
|
||||
Column1: nodeType,
|
||||
Column2: iata,
|
||||
Column3: supportsMultibytePaths,
|
||||
Column4: supportsMultibyteTraces,
|
||||
Column3: tristate(supportsMultibytePaths),
|
||||
Column4: tristate(supportsMultibyteTraces),
|
||||
Column5: pubkey,
|
||||
Column6: name,
|
||||
Column7: cursorTS,
|
||||
@@ -1219,3 +1219,13 @@ func nullableUUID(id uuid.UUID) *uuid.UUID {
|
||||
}
|
||||
return &id
|
||||
}
|
||||
|
||||
func tristate(b *bool) string {
|
||||
if b == nil {
|
||||
return "any"
|
||||
}
|
||||
if *b {
|
||||
return "true"
|
||||
}
|
||||
return "false"
|
||||
}
|
||||
|
||||
@@ -32,14 +32,14 @@ func NodesRouter(reader api.Reader) http.Handler {
|
||||
// @Produce json
|
||||
// @Param type query int false "Node type integer (1=companion, 2=repeater, 3=room_server, 4=sensor)"
|
||||
// @Param typeName query string false "Node type name (companion, repeater, room_server, sensor)"
|
||||
// @Param iata query string false "Filter by IATA code (case-insensitive)"
|
||||
// @Param iata query string false "Filter by IATA code (case-insensitive); comma-separated for multiple"
|
||||
// @Param name query string false "Partial case-insensitive name match"
|
||||
// @Param pubkey query string false "Exact public key match (hex)"
|
||||
// @Param supportsMultibytePaths query bool false "Filter to nodes with firmware >= 1.14.0"
|
||||
// @Param supportsMultibyteTraces query bool false "Filter to nodes with firmware >= 1.11.0"
|
||||
// @Param supportsMultibytePaths query string false "Multibyte path support filter: any (default), true, false"
|
||||
// @Param supportsMultibyteTraces query string false "Multibyte trace support filter: any (default), true, false"
|
||||
// @Param cursor query int false "last_seen epoch ms of last item for pagination"
|
||||
// @Param limit query int false "Max results (default 50)"
|
||||
// @Success 200 {object} object
|
||||
// @Success 200 {object} api.Page[api.NodeSummary]
|
||||
// @Failure 500 {object} handlers.APIError
|
||||
// @Router /nodes [get]
|
||||
func listNodes(reader api.Reader) http.HandlerFunc {
|
||||
@@ -84,8 +84,24 @@ func listNodes(reader api.Reader) http.HandlerFunc {
|
||||
}
|
||||
iata := r.URL.Query().Get("iata")
|
||||
name := r.URL.Query().Get("name")
|
||||
supportsMultibytePaths := r.URL.Query().Get("supportsMultibytePaths") == "true"
|
||||
supportsMultibyteTraces := r.URL.Query().Get("supportsMultibyteTraces") == "true"
|
||||
var supportsMultibytePaths *bool
|
||||
if v := r.URL.Query().Get("supportsMultibytePaths"); v != "" {
|
||||
b, err := strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusBadRequest, "invalid supportsMultibytePaths value")
|
||||
return
|
||||
}
|
||||
supportsMultibytePaths = &b
|
||||
}
|
||||
var supportsMultibyteTraces *bool
|
||||
if v := r.URL.Query().Get("supportsMultibyteTraces"); v != "" {
|
||||
b, err := strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusBadRequest, "invalid supportsMultibyteTraces value")
|
||||
return
|
||||
}
|
||||
supportsMultibytePaths = &b
|
||||
}
|
||||
nodes, err := reader.ListNodes(r.Context(), nodeType, iata, supportsMultibytePaths, supportsMultibyteTraces, pubkey, name, cursor, limit)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusInternalServerError, "internal server error")
|
||||
@@ -104,6 +120,7 @@ func listNodes(reader api.Reader) http.HandlerFunc {
|
||||
// @Success 200 {object} api.Node
|
||||
// @Failure 400 {object} handlers.APIError
|
||||
// @Failure 404 {object} handlers.APIError
|
||||
// @Failure 500 {object} handlers.APIError
|
||||
// @Router /nodes/{nodeId} [get]
|
||||
func getNode(reader api.Reader) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -129,7 +146,7 @@ func getNode(reader api.Reader) http.HandlerFunc {
|
||||
// @Param nodeId path string true "Node UUID"
|
||||
// @Param cursor query int false "Observation ID of last item for pagination"
|
||||
// @Param limit query int false "Max results (default 50)"
|
||||
// @Success 200 {object} object
|
||||
// @Success 200 {object} api.Page[api.NodeObservation]
|
||||
// @Failure 400 {object} handlers.APIError
|
||||
// @Failure 500 {object} handlers.APIError
|
||||
// @Router /nodes/{nodeId}/observations [get]
|
||||
|
||||
@@ -29,12 +29,12 @@ func PacketsRouter(reader api.Reader) http.Handler {
|
||||
// @Param payloadType query int false "Filter by payload type integer"
|
||||
// @Param payloadTypeName query string false "Filter by payload type name (advert, grp_txt, txt_msg, trace, anon_req)"
|
||||
// @Param routeType query int false "Filter by route type (0=transport_flood, 1=flood, 2=direct, 3=transport_direct)"
|
||||
// @Param iata query string false "Filter by latest observation IATA (case-insensitive)"
|
||||
// @Param iata query string false "Filter by IATA code"
|
||||
// @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"
|
||||
// @Param limit query int false "Max results (default 50)"
|
||||
// @Success 200 {object} object
|
||||
// @Success 200 {object} api.Page[api.PacketSummary]
|
||||
// @Failure 500 {object} handlers.APIError
|
||||
// @Router /packets [get]
|
||||
func listPackets(reader api.Reader) http.HandlerFunc {
|
||||
@@ -116,6 +116,7 @@ func listPackets(reader api.Reader) http.HandlerFunc {
|
||||
// @Success 200 {object} api.Packet
|
||||
// @Failure 400 {object} handlers.APIError
|
||||
// @Failure 404 {object} handlers.APIError
|
||||
// @Failure 500 {object} handlers.APIError
|
||||
// @Router /packets/{packetHash} [get]
|
||||
func getPacket(reader api.Reader) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -380,7 +380,7 @@ type Reader interface {
|
||||
// ListNodes returns a paginated list of nodes with optional filters.
|
||||
// Pass 0 for nodeType, empty string for iata, nil for pubkey to skip those filters.
|
||||
// cursor is last_seen epoch ms; pass 0 to start from the beginning.
|
||||
ListNodes(ctx context.Context, nodeType int16, iata string, supportsMultibytePaths, supportsMultibyteTraces bool, pubkey []byte, name string, cursor int64, limit int32) (Page[NodeSummary], error)
|
||||
ListNodes(ctx context.Context, nodeType int16, iata string, supportsMultibytePaths, supportsMultibyteTraces *bool, pubkey []byte, name string, cursor int64, limit int32) (Page[NodeSummary], error)
|
||||
|
||||
// GetNode returns full detail for a single node by UUID.
|
||||
// Returns nil, pgx.ErrNoRows if the node is not found.
|
||||
|
||||
Reference in New Issue
Block a user