Merge pull request #19 from ipnet-mesh/claude/research-node-id-usage-019DURYQHkvodx9sV39hTmNM

Research node ID and public key usage
This commit is contained in:
JingleManSweep
2025-12-05 16:59:30 +00:00
committed by GitHub
15 changed files with 45 additions and 70 deletions
@@ -20,7 +20,7 @@ async def list_advertisements(
_: RequireRead,
session: DbSession,
public_key: Optional[str] = Query(None, description="Filter by public key"),
receiver_public_key: Optional[str] = Query(
received_by: Optional[str] = Query(
None, description="Filter by receiver node public key"
),
since: Optional[datetime] = Query(None, description="Start timestamp"),
@@ -40,8 +40,8 @@ async def list_advertisements(
if public_key:
query = query.where(Advertisement.public_key == public_key)
if receiver_public_key:
query = query.where(ReceiverNode.public_key == receiver_public_key)
if received_by:
query = query.where(ReceiverNode.public_key == received_by)
if since:
query = query.where(Advertisement.received_at >= since)
@@ -59,13 +59,13 @@ async def list_advertisements(
# Execute
results = session.execute(query).all()
# Build response with receiver_public_key
# Build response with received_by
items = []
for adv, receiver_pk in results:
data = {
"id": adv.id,
"receiver_node_id": adv.receiver_node_id,
"receiver_public_key": receiver_pk,
"received_by": receiver_pk,
"node_id": adv.node_id,
"public_key": adv.public_key,
"name": adv.name,
@@ -106,7 +106,7 @@ async def get_advertisement(
data = {
"id": adv.id,
"receiver_node_id": adv.receiver_node_id,
"receiver_public_key": receiver_pk,
"received_by": receiver_pk,
"node_id": adv.node_id,
"public_key": adv.public_key,
"name": adv.name,
+6 -6
View File
@@ -22,7 +22,7 @@ async def list_messages(
message_type: Optional[str] = Query(None, description="Filter by message type"),
pubkey_prefix: Optional[str] = Query(None, description="Filter by sender prefix"),
channel_idx: Optional[int] = Query(None, description="Filter by channel"),
receiver_public_key: Optional[str] = Query(
received_by: Optional[str] = Query(
None, description="Filter by receiver node public key"
),
since: Optional[datetime] = Query(None, description="Start timestamp"),
@@ -49,8 +49,8 @@ async def list_messages(
if channel_idx is not None:
query = query.where(Message.channel_idx == channel_idx)
if receiver_public_key:
query = query.where(ReceiverNode.public_key == receiver_public_key)
if received_by:
query = query.where(ReceiverNode.public_key == received_by)
if since:
query = query.where(Message.received_at >= since)
@@ -96,13 +96,13 @@ async def list_messages(
for public_key, value in session.execute(friendly_name_query).all():
friendly_names[public_key[:12]] = value
# Build response with sender info and receiver_public_key
# Build response with sender info and received_by
items = []
for m, receiver_pk in results:
msg_dict = {
"id": m.id,
"receiver_node_id": m.receiver_node_id,
"receiver_public_key": receiver_pk,
"received_by": receiver_pk,
"message_type": m.message_type,
"pubkey_prefix": m.pubkey_prefix,
"sender_name": (
@@ -153,7 +153,7 @@ async def get_message(
data = {
"id": message.id,
"receiver_node_id": message.receiver_node_id,
"receiver_public_key": receiver_pk,
"received_by": receiver_pk,
"message_type": message.message_type,
"pubkey_prefix": message.pubkey_prefix,
"channel_idx": message.channel_idx,
+6 -6
View File
@@ -20,7 +20,7 @@ async def list_telemetry(
_: RequireRead,
session: DbSession,
node_public_key: Optional[str] = Query(None, description="Filter by node"),
receiver_public_key: Optional[str] = Query(
received_by: Optional[str] = Query(
None, description="Filter by receiver node public key"
),
since: Optional[datetime] = Query(None, description="Start timestamp"),
@@ -40,8 +40,8 @@ async def list_telemetry(
if node_public_key:
query = query.where(Telemetry.node_public_key == node_public_key)
if receiver_public_key:
query = query.where(ReceiverNode.public_key == receiver_public_key)
if received_by:
query = query.where(ReceiverNode.public_key == received_by)
if since:
query = query.where(Telemetry.received_at >= since)
@@ -59,13 +59,13 @@ async def list_telemetry(
# Execute
results = session.execute(query).all()
# Build response with receiver_public_key
# Build response with received_by
items = []
for tel, receiver_pk in results:
data = {
"id": tel.id,
"receiver_node_id": tel.receiver_node_id,
"receiver_public_key": receiver_pk,
"received_by": receiver_pk,
"node_id": tel.node_id,
"node_public_key": tel.node_public_key,
"parsed_data": tel.parsed_data,
@@ -104,7 +104,7 @@ async def get_telemetry(
data = {
"id": tel.id,
"receiver_node_id": tel.receiver_node_id,
"receiver_public_key": receiver_pk,
"received_by": receiver_pk,
"node_id": tel.node_id,
"node_public_key": tel.node_public_key,
"parsed_data": tel.parsed_data,
+6 -6
View File
@@ -19,7 +19,7 @@ router = APIRouter()
async def list_trace_paths(
_: RequireRead,
session: DbSession,
receiver_public_key: Optional[str] = Query(
received_by: Optional[str] = Query(
None, description="Filter by receiver node public key"
),
since: Optional[datetime] = Query(None, description="Start timestamp"),
@@ -36,8 +36,8 @@ async def list_trace_paths(
ReceiverNode, TracePath.receiver_node_id == ReceiverNode.id
)
if receiver_public_key:
query = query.where(ReceiverNode.public_key == receiver_public_key)
if received_by:
query = query.where(ReceiverNode.public_key == received_by)
if since:
query = query.where(TracePath.received_at >= since)
@@ -55,13 +55,13 @@ async def list_trace_paths(
# Execute
results = session.execute(query).all()
# Build response with receiver_public_key
# Build response with received_by
items = []
for tp, receiver_pk in results:
data = {
"id": tp.id,
"receiver_node_id": tp.receiver_node_id,
"receiver_public_key": receiver_pk,
"received_by": receiver_pk,
"initiator_tag": tp.initiator_tag,
"path_len": tp.path_len,
"flags": tp.flags,
@@ -104,7 +104,7 @@ async def get_trace_path(
data = {
"id": tp.id,
"receiver_node_id": tp.receiver_node_id,
"receiver_public_key": receiver_pk,
"received_by": receiver_pk,
"initiator_tag": tp.initiator_tag,
"path_len": tp.path_len,
"flags": tp.flags,
@@ -83,7 +83,6 @@ class MemberUpdate(BaseModel):
class MemberRead(BaseModel):
"""Schema for reading a member."""
id: str = Field(..., description="Member UUID")
name: str = Field(..., description="Member's display name")
callsign: Optional[str] = Field(default=None, description="Amateur radio callsign")
role: Optional[str] = Field(default=None, description="Member's role")
+4 -22
View File
@@ -9,11 +9,7 @@ from pydantic import BaseModel, Field
class MessageRead(BaseModel):
"""Schema for reading a message."""
id: str = Field(..., description="Message UUID")
receiver_node_id: Optional[str] = Field(
default=None, description="Receiving interface node UUID"
)
receiver_public_key: Optional[str] = Field(
received_by: Optional[str] = Field(
default=None, description="Receiving interface node public key"
)
message_type: str = Field(..., description="Message type (contact, channel)")
@@ -85,14 +81,9 @@ class MessageFilters(BaseModel):
class AdvertisementRead(BaseModel):
"""Schema for reading an advertisement."""
id: str = Field(..., description="Advertisement UUID")
receiver_node_id: Optional[str] = Field(
default=None, description="Receiving interface node UUID"
)
receiver_public_key: Optional[str] = Field(
received_by: Optional[str] = Field(
default=None, description="Receiving interface node public key"
)
node_id: Optional[str] = Field(default=None, description="Advertised node UUID")
public_key: str = Field(..., description="Advertised public key")
name: Optional[str] = Field(default=None, description="Advertised name")
adv_type: Optional[str] = Field(default=None, description="Node type")
@@ -116,11 +107,7 @@ class AdvertisementList(BaseModel):
class TracePathRead(BaseModel):
"""Schema for reading a trace path."""
id: str = Field(..., description="Trace path UUID")
receiver_node_id: Optional[str] = Field(
default=None, description="Receiving interface node UUID"
)
receiver_public_key: Optional[str] = Field(
received_by: Optional[str] = Field(
default=None, description="Receiving interface node public key"
)
initiator_tag: int = Field(..., description="Trace identifier")
@@ -153,14 +140,9 @@ class TracePathList(BaseModel):
class TelemetryRead(BaseModel):
"""Schema for reading a telemetry record."""
id: str = Field(..., description="Telemetry UUID")
receiver_node_id: Optional[str] = Field(
default=None, description="Receiving interface node UUID"
)
receiver_public_key: Optional[str] = Field(
received_by: Optional[str] = Field(
default=None, description="Receiving interface node public key"
)
node_id: Optional[str] = Field(default=None, description="Reporting node UUID")
node_public_key: str = Field(..., description="Reporting node public key")
parsed_data: Optional[dict] = Field(
default=None, description="Decoded sensor readings"
-3
View File
@@ -41,8 +41,6 @@ class NodeTagUpdate(BaseModel):
class NodeTagRead(BaseModel):
"""Schema for reading a node tag."""
id: str = Field(..., description="Tag UUID")
node_id: str = Field(..., description="Parent node UUID")
key: str = Field(..., description="Tag name/key")
value: Optional[str] = Field(default=None, description="Tag value")
value_type: str = Field(..., description="Value type hint")
@@ -56,7 +54,6 @@ class NodeTagRead(BaseModel):
class NodeRead(BaseModel):
"""Schema for reading a node."""
id: str = Field(..., description="Node UUID")
public_key: str = Field(..., description="Node's 64-character hex public key")
name: Optional[str] = Field(default=None, description="Node display name")
adv_type: Optional[str] = Field(default=None, description="Advertisement type")
+7 -6
View File
@@ -43,13 +43,14 @@ async def map_data(request: Request) -> JSONResponse:
if members_response.status_code == 200:
members_data = members_response.json()
for member in members_data.get("items", []):
member_info = {
"id": member.get("id"),
"name": member.get("name"),
"callsign": member.get("callsign"),
}
members_list.append(member_info)
# 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
else:
logger.warning(
+4 -4
View File
@@ -211,7 +211,7 @@
// Owner filter
if (ownerFilter) {
if (!node.owner || node.owner.id !== ownerFilter) return false;
if (!node.owner || node.owner.public_key !== ownerFilter) return false;
}
return true;
@@ -269,12 +269,12 @@
const ownersWithNodes = new Set();
allNodes.forEach(node => {
if (node.owner) {
ownersWithNodes.add(node.owner.id);
ownersWithNodes.add(node.owner.public_key);
}
});
// Filter members to only those who own nodes on the map
const relevantMembers = allMembers.filter(m => ownersWithNodes.has(m.id));
const relevantMembers = allMembers.filter(m => ownersWithNodes.has(m.public_key));
// Sort by name
relevantMembers.sort((a, b) => a.name.localeCompare(b.name));
@@ -282,7 +282,7 @@
// Add options
relevantMembers.forEach(member => {
const option = document.createElement('option');
option.value = member.id;
option.value = member.public_key;
option.textContent = member.callsign
? `${member.name} (${member.callsign})`
: member.name;
+2 -2
View File
@@ -90,8 +90,8 @@
{{ msg.text or '-' }}
</td>
<td class="text-xs">
{% if msg.receiver_public_key %}
<span class="font-mono" title="{{ msg.receiver_public_key }}">{{ msg.receiver_public_key[:8] }}...</span>
{% if msg.received_by %}
<span class="font-mono" title="{{ msg.received_by }}">{{ msg.received_by[:8] }}...</span>
{% else %}
<span class="opacity-50">-</span>
{% endif %}
@@ -113,8 +113,8 @@
<td>{{ adv.adv_type or '-' }}</td>
<td>{{ adv.name or '-' }}</td>
<td class="text-xs">
{% if adv.receiver_public_key %}
<span class="font-mono" title="{{ adv.receiver_public_key }}">{{ adv.receiver_public_key[:8] }}...</span>
{% if adv.received_by %}
<span class="font-mono" title="{{ adv.received_by }}">{{ adv.received_by[:8] }}...</span>
{% else %}
<span class="opacity-50">-</span>
{% endif %}
@@ -156,8 +156,8 @@
{% endif %}
</td>
<td class="text-xs">
{% if tel.receiver_public_key %}
<span class="font-mono" title="{{ tel.receiver_public_key }}">{{ tel.receiver_public_key[:8] }}...</span>
{% if tel.received_by %}
<span class="font-mono" title="{{ tel.received_by }}">{{ tel.received_by[:8] }}...</span>
{% else %}
<span class="opacity-50">-</span>
{% endif %}
-1
View File
@@ -49,7 +49,6 @@ class TestGetAdvertisement:
)
assert response.status_code == 200
data = response.json()
assert data["id"] == sample_advertisement.id
assert data["public_key"] == sample_advertisement.public_key
def test_get_advertisement_not_found(self, client_no_auth):
-1
View File
@@ -51,7 +51,6 @@ class TestGetMessage:
response = client_no_auth.get(f"/api/v1/messages/{sample_message.id}")
assert response.status_code == 200
data = response.json()
assert data["id"] == sample_message.id
assert data["text"] == sample_message.text
def test_get_message_not_found(self, client_no_auth):
-1
View File
@@ -45,7 +45,6 @@ class TestGetTelemetry:
response = client_no_auth.get(f"/api/v1/telemetry/{sample_telemetry.id}")
assert response.status_code == 200
data = response.json()
assert data["id"] == sample_telemetry.id
assert data["node_public_key"] == sample_telemetry.node_public_key
def test_get_telemetry_not_found(self, client_no_auth):
-1
View File
@@ -31,7 +31,6 @@ class TestGetTracePath:
response = client_no_auth.get(f"/api/v1/trace-paths/{sample_trace_path.id}")
assert response.status_code == 200
data = response.json()
assert data["id"] == sample_trace_path.id
assert data["path_hashes"] == sample_trace_path.path_hashes
def test_get_trace_path_not_found(self, client_no_auth):