Skip to content

Commit a1cc438

Browse files
Merge pull request #16 from ipnet-mesh/claude/implement-map-page-01F5nbj6KfXDv4Dsp1KE6jJZ
Implement Map page with node filtering
2 parents d6346fd + bb0b9f0 commit a1cc438

2 files changed

Lines changed: 333 additions & 48 deletions

File tree

src/meshcore_hub/web/routes/map.py

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Map page route."""
22

33
import logging
4+
from typing import Any
45

56
from fastapi import APIRouter, Request
67
from fastapi.responses import HTMLResponse, JSONResponse
@@ -23,24 +24,55 @@ async def map_page(request: Request) -> HTMLResponse:
2324

2425
@router.get("/map/data")
2526
async 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

Comments
 (0)