diff --git a/src/meshcore_hub/web/routes/map.py b/src/meshcore_hub/web/routes/map.py
index 1c7238c..c6b04a6 100644
--- a/src/meshcore_hub/web/routes/map.py
+++ b/src/meshcore_hub/web/routes/map.py
@@ -30,28 +30,27 @@ async def map_data(request: Request) -> JSONResponse:
"""
nodes_with_location: list[dict[str, Any]] = []
members_list: list[dict[str, Any]] = []
- members_by_key: dict[str, dict[str, Any]] = {}
+ members_by_id: dict[str, dict[str, Any]] = {}
error: str | None = None
total_nodes = 0
nodes_with_coords = 0
try:
- # Fetch all members to build lookup by public_key
+ # Fetch all members to build lookup by member_id
members_response = await request.app.state.http_client.get(
"/api/v1/members", params={"limit": 500}
)
if members_response.status_code == 200:
members_data = members_response.json()
for member in members_data.get("items", []):
- # Only include members with public_key (required for node ownership)
- if member.get("public_key"):
- member_info = {
- "public_key": member.get("public_key"),
- "name": member.get("name"),
- "callsign": member.get("callsign"),
- }
- members_list.append(member_info)
- members_by_key[member["public_key"]] = member_info
+ member_info = {
+ "member_id": member.get("member_id"),
+ "name": member.get("name"),
+ "callsign": member.get("callsign"),
+ }
+ members_list.append(member_info)
+ if member.get("member_id"):
+ members_by_id[member["member_id"]] = member_info
else:
logger.warning(
f"Failed to fetch members: status {members_response.status_code}"
@@ -73,6 +72,7 @@ async def map_data(request: Request) -> JSONResponse:
lon = None
friendly_name = None
role = None
+ node_member_id = None
for tag in tags:
key = tag.get("key")
@@ -90,6 +90,8 @@ async def map_data(request: Request) -> JSONResponse:
friendly_name = tag.get("value")
elif key == "role":
role = tag.get("value")
+ elif key == "member_id":
+ node_member_id = tag.get("value")
if lat is not None and lon is not None:
nodes_with_coords += 1
@@ -101,8 +103,10 @@ async def map_data(request: Request) -> JSONResponse:
)
public_key = node.get("public_key")
- # Find owner member if exists
- owner = members_by_key.get(public_key)
+ # Find owner member by member_id tag
+ owner = (
+ members_by_id.get(node_member_id) if node_member_id else None
+ )
nodes_with_location.append(
{
@@ -114,6 +118,7 @@ async def map_data(request: Request) -> JSONResponse:
"last_seen": node.get("last_seen"),
"role": role,
"is_infra": role == "infra",
+ "member_id": node_member_id,
"owner": owner,
}
)
diff --git a/src/meshcore_hub/web/templates/map.html b/src/meshcore_hub/web/templates/map.html
index 73d733f..976291f 100644
--- a/src/meshcore_hub/web/templates/map.html
+++ b/src/meshcore_hub/web/templates/map.html
@@ -45,10 +45,10 @@
-
@@ -190,16 +190,16 @@
// Core filter logic - returns filtered nodes and updates markers
function applyFiltersCore() {
const typeFilter = document.getElementById('filter-type').value;
- const ownerFilter = document.getElementById('filter-owner').value;
+ const memberFilter = document.getElementById('filter-member').value;
// Filter nodes
const filteredNodes = allNodes.filter(node => {
// Type filter (case-insensitive)
if (typeFilter && normalizeType(node.adv_type) !== typeFilter) return false;
- // Owner filter
- if (ownerFilter) {
- if (!node.owner || node.owner.public_key !== ownerFilter) return false;
+ // Member filter - match node's member_id tag to selected member_id
+ if (memberFilter) {
+ if (node.member_id !== memberFilter) return false;
}
return true;
@@ -249,45 +249,36 @@
applyFiltersCore();
}
- // Populate owner filter dropdown
- function populateOwnerFilter() {
- const select = document.getElementById('filter-owner');
+ // Populate member filter dropdown
+ function populateMemberFilter() {
+ const select = document.getElementById('filter-member');
- // Get unique owners from nodes that have locations
- const ownersWithNodes = new Set();
- allNodes.forEach(node => {
- if (node.owner) {
- ownersWithNodes.add(node.owner.public_key);
+ // Sort members by name
+ const sortedMembers = [...allMembers].sort((a, b) => a.name.localeCompare(b.name));
+
+ // Add options for all members
+ sortedMembers.forEach(member => {
+ if (member.member_id) {
+ const option = document.createElement('option');
+ option.value = member.member_id;
+ option.textContent = member.callsign
+ ? `${member.name} (${member.callsign})`
+ : member.name;
+ select.appendChild(option);
}
});
-
- // Filter members to only those who own nodes on the map
- const relevantMembers = allMembers.filter(m => ownersWithNodes.has(m.public_key));
-
- // Sort by name
- relevantMembers.sort((a, b) => a.name.localeCompare(b.name));
-
- // Add options
- relevantMembers.forEach(member => {
- const option = document.createElement('option');
- option.value = member.public_key;
- option.textContent = member.callsign
- ? `${member.name} (${member.callsign})`
- : member.name;
- select.appendChild(option);
- });
}
// Clear all filters
function clearFilters() {
document.getElementById('filter-type').value = '';
- document.getElementById('filter-owner').value = '';
+ document.getElementById('filter-member').value = '';
applyFilters();
}
// Event listeners for filters
document.getElementById('filter-type').addEventListener('change', applyFilters);
- document.getElementById('filter-owner').addEventListener('change', applyFilters);
+ document.getElementById('filter-member').addEventListener('change', applyFilters);
document.getElementById('clear-filters').addEventListener('click', clearFilters);
// Fetch and display nodes
@@ -318,8 +309,8 @@
return;
}
- // Populate owner filter
- populateOwnerFilter();
+ // Populate member filter
+ populateMemberFilter();
// Initial display - center map on nodes if available
if (allNodes.length > 0) {