mirror of
https://github.com/ipnet-mesh/meshcore-hub.git
synced 2026-08-07 01:13:11 +02:00
Messages list: swap Time/Type columns, add receiver node links
- Swap Time and Type columns (Type now first) - Add receiver_name and receiver_friendly_name to MessageRead schema - Update messages API to fetch receiver node names and tags - Make Receiver column a link showing name with public key prefix
This commit is contained in:
@@ -5,7 +5,7 @@ from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Query
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.orm import aliased
|
||||
from sqlalchemy.orm import aliased, selectinload
|
||||
|
||||
from meshcore_hub.api.auth import RequireRead
|
||||
from meshcore_hub.api.dependencies import DbSession
|
||||
@@ -15,6 +15,16 @@ from meshcore_hub.common.schemas.messages import MessageList, MessageRead
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
def _get_friendly_name(node: Optional[Node]) -> Optional[str]:
|
||||
"""Extract friendly_name tag from a node's tags."""
|
||||
if not node or not node.tags:
|
||||
return None
|
||||
for tag in node.tags:
|
||||
if tag.key == "friendly_name":
|
||||
return tag.value
|
||||
return None
|
||||
|
||||
|
||||
@router.get("", response_model=MessageList)
|
||||
async def list_messages(
|
||||
_: RequireRead,
|
||||
@@ -36,9 +46,12 @@ async def list_messages(
|
||||
ReceiverNode = aliased(Node)
|
||||
|
||||
# Build query with receiver node join
|
||||
query = select(Message, ReceiverNode.public_key.label("receiver_pk")).outerjoin(
|
||||
ReceiverNode, Message.receiver_node_id == ReceiverNode.id
|
||||
)
|
||||
query = select(
|
||||
Message,
|
||||
ReceiverNode.public_key.label("receiver_pk"),
|
||||
ReceiverNode.name.label("receiver_name"),
|
||||
ReceiverNode.id.label("receiver_id"),
|
||||
).outerjoin(ReceiverNode, Message.receiver_node_id == ReceiverNode.id)
|
||||
|
||||
if message_type:
|
||||
query = query.where(Message.message_type == message_type)
|
||||
@@ -96,13 +109,39 @@ async def list_messages(
|
||||
for public_key, value in session.execute(friendly_name_query).all():
|
||||
friendly_names[public_key[:12]] = value
|
||||
|
||||
# Collect receiver node IDs to fetch tags
|
||||
receiver_ids = set()
|
||||
for row in results:
|
||||
if row.receiver_id:
|
||||
receiver_ids.add(row.receiver_id)
|
||||
|
||||
# Fetch receiver nodes with tags
|
||||
receivers_by_id: dict[str, Node] = {}
|
||||
if receiver_ids:
|
||||
receivers_query = (
|
||||
select(Node)
|
||||
.where(Node.id.in_(receiver_ids))
|
||||
.options(selectinload(Node.tags))
|
||||
)
|
||||
receivers = session.execute(receivers_query).scalars().all()
|
||||
receivers_by_id = {n.id: n for n in receivers}
|
||||
|
||||
# Build response with sender info and received_by
|
||||
items = []
|
||||
for m, receiver_pk in results:
|
||||
for row in results:
|
||||
m = row[0]
|
||||
receiver_pk = row.receiver_pk
|
||||
receiver_name = row.receiver_name
|
||||
receiver_node = (
|
||||
receivers_by_id.get(row.receiver_id) if row.receiver_id else None
|
||||
)
|
||||
|
||||
msg_dict = {
|
||||
"id": m.id,
|
||||
"receiver_node_id": m.receiver_node_id,
|
||||
"received_by": receiver_pk,
|
||||
"receiver_name": receiver_name,
|
||||
"receiver_friendly_name": _get_friendly_name(receiver_node),
|
||||
"message_type": m.message_type,
|
||||
"pubkey_prefix": m.pubkey_prefix,
|
||||
"sender_name": (
|
||||
|
||||
@@ -12,6 +12,10 @@ class MessageRead(BaseModel):
|
||||
received_by: Optional[str] = Field(
|
||||
default=None, description="Receiving interface node public key"
|
||||
)
|
||||
receiver_name: Optional[str] = Field(default=None, description="Receiver node name")
|
||||
receiver_friendly_name: Optional[str] = Field(
|
||||
default=None, description="Receiver friendly name from tags"
|
||||
)
|
||||
message_type: str = Field(..., description="Message type (contact, channel)")
|
||||
pubkey_prefix: Optional[str] = Field(
|
||||
default=None, description="Sender's public key prefix (12 chars)"
|
||||
|
||||
@@ -53,8 +53,8 @@
|
||||
<table class="table table-zebra">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Time</th>
|
||||
<th>Type</th>
|
||||
<th>Time</th>
|
||||
<th>From/Channel</th>
|
||||
<th>Message</th>
|
||||
<th>Receiver</th>
|
||||
@@ -64,9 +64,6 @@
|
||||
<tbody>
|
||||
{% for msg in messages %}
|
||||
<tr class="hover align-top">
|
||||
<td class="text-xs whitespace-nowrap">
|
||||
{{ msg.received_at[:19].replace('T', ' ') if msg.received_at else '-' }}
|
||||
</td>
|
||||
<td>
|
||||
{% if msg.message_type == 'channel' %}
|
||||
<span class="badge badge-info badge-sm">Channel</span>
|
||||
@@ -74,6 +71,9 @@
|
||||
<span class="badge badge-success badge-sm">Direct</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-sm whitespace-nowrap">
|
||||
{{ msg.received_at[:19].replace('T', ' ') if msg.received_at else '-' }}
|
||||
</td>
|
||||
<td class="text-sm whitespace-nowrap">
|
||||
{% if msg.message_type == 'channel' %}
|
||||
<span class="font-mono">CH{{ msg.channel_idx }}</span>
|
||||
@@ -86,9 +86,16 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="break-words max-w-md" style="white-space: pre-wrap;">{{ msg.text or '-' }}</td>
|
||||
<td class="text-xs whitespace-nowrap">
|
||||
<td>
|
||||
{% if msg.received_by %}
|
||||
<span class="font-mono" title="{{ msg.received_by }}">{{ msg.received_by[:8] }}...</span>
|
||||
<a href="/nodes/{{ msg.received_by }}" class="link link-hover">
|
||||
{% if msg.receiver_friendly_name or msg.receiver_name %}
|
||||
<div class="font-medium">{{ msg.receiver_friendly_name or msg.receiver_name }}</div>
|
||||
<div class="text-xs font-mono opacity-70">{{ msg.received_by[:16] }}...</div>
|
||||
{% else %}
|
||||
<span class="font-mono text-sm">{{ msg.received_by[:16] }}...</span>
|
||||
{% endif %}
|
||||
</a>
|
||||
{% else %}
|
||||
<span class="opacity-50">-</span>
|
||||
{% endif %}
|
||||
|
||||
Reference in New Issue
Block a user