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 %} +
| Time | +Name | +Type | +Public Key | +Receiver | +
|---|---|---|---|---|
| + {{ 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. | +||||
{{ 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