diff --git a/src/meshcore_hub/web/routes/__init__.py b/src/meshcore_hub/web/routes/__init__.py index 4d35f07..43736b4 100644 --- a/src/meshcore_hub/web/routes/__init__.py +++ b/src/meshcore_hub/web/routes/__init__.py @@ -6,6 +6,7 @@ from meshcore_hub.web.routes.home import router as home_router from meshcore_hub.web.routes.network import router as network_router from meshcore_hub.web.routes.nodes import router as nodes_router from meshcore_hub.web.routes.messages import router as messages_router +from meshcore_hub.web.routes.advertisements import router as advertisements_router from meshcore_hub.web.routes.map import router as map_router from meshcore_hub.web.routes.members import router as members_router @@ -17,6 +18,7 @@ web_router.include_router(home_router) web_router.include_router(network_router) web_router.include_router(nodes_router) web_router.include_router(messages_router) +web_router.include_router(advertisements_router) web_router.include_router(map_router) web_router.include_router(members_router) diff --git a/src/meshcore_hub/web/routes/advertisements.py b/src/meshcore_hub/web/routes/advertisements.py new file mode 100644 index 0000000..a51a80b --- /dev/null +++ b/src/meshcore_hub/web/routes/advertisements.py @@ -0,0 +1,64 @@ +"""Advertisements page route.""" + +import logging + +from fastapi import APIRouter, Query, Request +from fastapi.responses import HTMLResponse + +from meshcore_hub.web.app import get_network_context, get_templates + +logger = logging.getLogger(__name__) +router = APIRouter() + + +@router.get("/advertisements", response_class=HTMLResponse) +async def advertisements_list( + request: Request, + public_key: str | None = Query(None, description="Filter by public key"), + page: int = Query(1, ge=1, description="Page number"), + limit: int = Query(50, ge=1, le=100, description="Items per page"), +) -> HTMLResponse: + """Render the advertisements list page.""" + templates = get_templates(request) + context = get_network_context(request) + context["request"] = request + + # Calculate offset + offset = (page - 1) * limit + + # Build query params + params: dict[str, int | str] = {"limit": limit, "offset": offset} + if public_key: + params["public_key"] = public_key + + # Fetch advertisements from API + advertisements = [] + total = 0 + + try: + response = await request.app.state.http_client.get( + "/api/v1/advertisements", params=params + ) + if response.status_code == 200: + data = response.json() + advertisements = data.get("items", []) + total = data.get("total", 0) + except Exception as e: + logger.warning(f"Failed to fetch advertisements from API: {e}") + context["api_error"] = str(e) + + # Calculate pagination + total_pages = (total + limit - 1) // limit if total > 0 else 1 + + context.update( + { + "advertisements": advertisements, + "total": total, + "page": page, + "limit": limit, + "total_pages": total_pages, + "public_key": public_key or "", + } + ) + + return templates.TemplateResponse("advertisements.html", context) diff --git a/src/meshcore_hub/web/routes/home.py b/src/meshcore_hub/web/routes/home.py index 03a37cf..dc2b852 100644 --- a/src/meshcore_hub/web/routes/home.py +++ b/src/meshcore_hub/web/routes/home.py @@ -1,10 +1,13 @@ """Home page route.""" +import logging + from fastapi import APIRouter, Request from fastapi.responses import HTMLResponse from meshcore_hub.web.app import get_network_context, get_templates +logger = logging.getLogger(__name__) router = APIRouter() @@ -15,4 +18,24 @@ async def home(request: Request) -> HTMLResponse: context = get_network_context(request) context["request"] = request + # Fetch stats from API + stats = { + "total_nodes": 0, + "active_nodes": 0, + "total_messages": 0, + "messages_today": 0, + "total_advertisements": 0, + "advertisements_24h": 0, + } + + try: + response = await request.app.state.http_client.get("/api/v1/dashboard/stats") + if response.status_code == 200: + stats = response.json() + except Exception as e: + logger.warning(f"Failed to fetch stats from API: {e}") + context["api_error"] = str(e) + + context["stats"] = stats + return templates.TemplateResponse("home.html", context) diff --git a/src/meshcore_hub/web/templates/advertisements.html b/src/meshcore_hub/web/templates/advertisements.html new file mode 100644 index 0000000..1f8a4d2 --- /dev/null +++ b/src/meshcore_hub/web/templates/advertisements.html @@ -0,0 +1,122 @@ +{% extends "base.html" %} + +{% block title %}{{ network_name }} - Advertisements{% endblock %} + +{% block content %} +
+

Advertisements

+ {{ total }} total +
+ +{% if api_error %} +
+ + + + Could not fetch data from API: {{ api_error }} +
+{% endif %} + + +
+
+
+
+ + +
+ + Clear +
+
+
+ + +
+ + + + + + + + + + + + {% for ad in advertisements %} + + + + + + + + {% else %} + + + + {% endfor %} + +
TimeNameTypePublic KeyReceiver
+ {{ ad.received_at[:19].replace('T', ' ') if ad.received_at else '-' }} + + + {{ ad.name or '-' }} + + + {% if ad.adv_type and ad.adv_type|lower == 'chat' %} + 💬 + {% elif ad.adv_type and ad.adv_type|lower == 'repeater' %} + 📡 + {% elif ad.adv_type and ad.adv_type|lower == 'room' %} + 🪧 + {% elif ad.adv_type %} + 📍 + {% else %} + - + {% endif %} + + + {{ ad.public_key[:16] }}... + + + {% if ad.received_by %} + {{ ad.received_by[:8] }}... + {% else %} + - + {% endif %} +
No advertisements found.
+
+ + +{% if total_pages > 1 %} +
+
+ {% if page > 1 %} + Previous + {% else %} + + {% endif %} + + {% for p in range(1, total_pages + 1) %} + {% if p == page %} + + {% elif p == 1 or p == total_pages or (p >= page - 2 and p <= page + 2) %} + {{ p }} + {% elif p == 2 or p == total_pages - 1 %} + + {% endif %} + {% endfor %} + + {% if page < total_pages %} + Next + {% else %} + + {% endif %} +
+
+{% endif %} +{% endblock %} diff --git a/src/meshcore_hub/web/templates/base.html b/src/meshcore_hub/web/templates/base.html index 8711287..972578f 100644 --- a/src/meshcore_hub/web/templates/base.html +++ b/src/meshcore_hub/web/templates/base.html @@ -58,6 +58,7 @@
  • Home
  • Network
  • Nodes
  • +
  • Advertisements
  • Messages
  • Map
  • Members
  • @@ -75,6 +76,7 @@
  • Home
  • Network
  • Nodes
  • +
  • Advertisements
  • Messages
  • Map
  • Members
  • diff --git a/src/meshcore_hub/web/templates/home.html b/src/meshcore_hub/web/templates/home.html index 4fea129..4a9d89d 100644 --- a/src/meshcore_hub/web/templates/home.html +++ b/src/meshcore_hub/web/templates/home.html @@ -3,17 +3,17 @@ {% block title %}{{ network_name }} - Home{% endblock %} {% block content %} -
    +
    -

    {{ network_name }}

    +

    {{ network_name }}

    {% if network_city and network_country %} -

    {{ network_city }}, {{ network_country }}

    +

    {{ network_city }}, {{ network_country }}

    {% endif %} {% if network_welcome_text %} -

    {{ network_welcome_text }}

    +

    {{ network_welcome_text }}

    {% else %} -

    +

    Welcome to the {{ network_name }} mesh network dashboard. Monitor network activity, view connected nodes, and explore message history.

    @@ -23,26 +23,83 @@ - View Network Stats + Dashboard - Browse Nodes + Nodes - + - + - View Map + Advertisements + + + + + + Messages
    -
    + +
    + +
    +
    + + + +
    +
    Total Nodes
    +
    {{ stats.total_nodes }}
    +
    All discovered nodes
    +
    + + +
    +
    + + + +
    +
    Advertisements
    +
    {{ stats.advertisements_24h }}
    +
    Received in last 24 hours
    +
    + + +
    +
    + + + +
    +
    Total Messages
    +
    {{ stats.total_messages }}
    +
    All time
    +
    + + +
    +
    + + + +
    +
    Messages Today
    +
    {{ stats.messages_today }}
    +
    Last 24 hours
    +
    +
    + +
    diff --git a/src/meshcore_hub/web/templates/messages.html b/src/meshcore_hub/web/templates/messages.html index 2d7471c..55d0b26 100644 --- a/src/meshcore_hub/web/templates/messages.html +++ b/src/meshcore_hub/web/templates/messages.html @@ -59,12 +59,11 @@ Message Receiver SNR - Hops {% for msg in messages %} - + {{ msg.received_at[:19].replace('T', ' ') if msg.received_at else '-' }} @@ -75,7 +74,7 @@ Direct {% endif %} - + {% if msg.message_type == 'channel' %} CH{{ msg.channel_idx }} {% else %} @@ -86,34 +85,27 @@ {% endif %} {% endif %} - + {{ msg.text or '-' }} - + {% if msg.received_by %} {{ msg.received_by[:8] }}... {% else %} - {% endif %} - + {% if msg.snr is not none %} {{ "%.1f"|format(msg.snr) }} {% else %} - {% endif %} - - {% if msg.hops is not none %} - {{ msg.hops }} - {% else %} - - - {% endif %} - {% else %} - No messages found. + No messages found. {% endfor %}