11"""Map page route."""
22
33import logging
4+ from typing import Any
45
56from fastapi import APIRouter , Request
67from fastapi .responses import HTMLResponse , JSONResponse
@@ -23,24 +24,55 @@ async def map_page(request: Request) -> HTMLResponse:
2324
2425@router .get ("/map/data" )
2526async def map_data (request : Request ) -> JSONResponse :
26- """Return node location data as JSON for the map."""
27- nodes_with_location = []
27+ """Return node location data as JSON for the map.
28+
29+ Includes role tag, member ownership info, and all data needed for filtering.
30+ """
31+ nodes_with_location : list [dict [str , Any ]] = []
32+ members_list : list [dict [str , Any ]] = []
33+ members_by_key : dict [str , dict [str , Any ]] = {}
34+ error : str | None = None
35+ total_nodes = 0
36+ nodes_with_coords = 0
2837
2938 try :
39+ # Fetch all members to build lookup by public_key
40+ members_response = await request .app .state .http_client .get (
41+ "/api/v1/members" , params = {"limit" : 500 }
42+ )
43+ if members_response .status_code == 200 :
44+ members_data = members_response .json ()
45+ for member in members_data .get ("items" , []):
46+ member_info = {
47+ "id" : member .get ("id" ),
48+ "name" : member .get ("name" ),
49+ "callsign" : member .get ("callsign" ),
50+ }
51+ members_list .append (member_info )
52+ if member .get ("public_key" ):
53+ members_by_key [member ["public_key" ]] = member_info
54+ else :
55+ logger .warning (
56+ f"Failed to fetch members: status { members_response .status_code } "
57+ )
58+
3059 # Fetch all nodes from API
3160 response = await request .app .state .http_client .get (
3261 "/api/v1/nodes" , params = {"limit" : 500 }
3362 )
3463 if response .status_code == 200 :
3564 data = response .json ()
3665 nodes = data .get ("items" , [])
66+ total_nodes = len (nodes )
3767
3868 # Filter nodes with location tags
3969 for node in nodes :
4070 tags = node .get ("tags" , [])
4171 lat = None
4272 lon = None
4373 friendly_name = None
74+ role = None
75+
4476 for tag in tags :
4577 key = tag .get ("key" )
4678 if key == "lat" :
@@ -55,37 +87,62 @@ async def map_data(request: Request) -> JSONResponse:
5587 pass
5688 elif key == "friendly_name" :
5789 friendly_name = tag .get ("value" )
90+ elif key == "role" :
91+ role = tag .get ("value" )
5892
5993 if lat is not None and lon is not None :
94+ nodes_with_coords += 1
6095 # Use friendly_name, then node name, then public key prefix
6196 display_name = (
6297 friendly_name
6398 or node .get ("name" )
6499 or node .get ("public_key" , "" )[:12 ]
65100 )
101+ public_key = node .get ("public_key" )
102+
103+ # Find owner member if exists
104+ owner = members_by_key .get (public_key )
105+
66106 nodes_with_location .append (
67107 {
68- "public_key" : node . get ( " public_key" ) ,
108+ "public_key" : public_key ,
69109 "name" : display_name ,
70110 "adv_type" : node .get ("adv_type" ),
71111 "lat" : lat ,
72112 "lon" : lon ,
73113 "last_seen" : node .get ("last_seen" ),
114+ "role" : role ,
115+ "is_infra" : role == "infra" ,
116+ "owner" : owner ,
74117 }
75118 )
119+ else :
120+ error = f"API returned status { response .status_code } "
121+ logger .warning (f"Failed to fetch nodes: { error } " )
76122
77123 except Exception as e :
124+ error = str (e )
78125 logger .warning (f"Failed to fetch nodes for map: { e } " )
79126
80127 # Get network center location
81128 network_location = request .app .state .network_location
82129
130+ logger .info (
131+ f"Map data: { total_nodes } total nodes, " f"{ nodes_with_coords } with coordinates"
132+ )
133+
83134 return JSONResponse (
84135 {
85136 "nodes" : nodes_with_location ,
137+ "members" : members_list ,
86138 "center" : {
87139 "lat" : network_location [0 ],
88140 "lon" : network_location [1 ],
89141 },
142+ "debug" : {
143+ "total_nodes" : total_nodes ,
144+ "nodes_with_coords" : nodes_with_coords ,
145+ "error" : error ,
146+ },
90147 }
91148 )
0 commit comments