mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-06-11 01:14:45 +02:00
feat(dashboard): embedded FastAPI backend with REST API + WebSocket
- FastAPI runs in MeshAI asyncio loop (no separate process) - REST API: /api/status, /api/health, /api/nodes, /api/edges, /api/regions, /api/sources, /api/config, /api/alerts - WebSocket at /ws/live pushes health updates and alerts - Config CRUD: GET/PUT per section with validation and save - DashboardConfig with port/host in config.yaml
This commit is contained in:
parent
914c21e167
commit
3ec09ad158
17 changed files with 1140 additions and 103 deletions
|
|
@ -51,6 +51,7 @@ class MeshAI:
|
|||
self._loop: Optional[asyncio.AbstractEventLoop] = None
|
||||
self._last_cleanup: float = 0.0
|
||||
self._last_health_compute: float = 0.0
|
||||
self.broadcaster = None # Dashboard WebSocket broadcaster
|
||||
|
||||
async def start(self) -> None:
|
||||
"""Start the bot."""
|
||||
|
|
@ -94,12 +95,37 @@ class MeshAI:
|
|||
self.health_engine.compute(self.data_store)
|
||||
self._last_health_compute = time.time()
|
||||
|
||||
# Broadcast health update to dashboard
|
||||
if self.broadcaster and self.health_engine.mesh_health:
|
||||
try:
|
||||
mh = self.health_engine.mesh_health
|
||||
health_dict = {
|
||||
"score": round(mh.score.composite, 1),
|
||||
"tier": mh.score.tier,
|
||||
"total_nodes": mh.total_nodes,
|
||||
"total_regions": mh.total_regions,
|
||||
"infra_online": mh.score.infra_online,
|
||||
"infra_total": mh.score.infra_total,
|
||||
"last_computed": mh.last_computed,
|
||||
}
|
||||
await self.broadcaster.broadcast("health_update", health_dict)
|
||||
except Exception as e:
|
||||
logger.debug("Dashboard broadcast error: %s", e)
|
||||
|
||||
# Check for alertable conditions
|
||||
if self.alert_engine:
|
||||
alerts = self.alert_engine.check()
|
||||
if alerts:
|
||||
await self._dispatch_alerts(alerts)
|
||||
|
||||
# Broadcast alerts to dashboard
|
||||
if self.broadcaster:
|
||||
for alert in alerts:
|
||||
try:
|
||||
await self.broadcaster.broadcast("alert_fired", alert)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Check scheduled subscriptions (every 60 seconds)
|
||||
if self.subscription_manager and self.mesh_reporter:
|
||||
if time.time() - self._last_sub_check >= 60:
|
||||
|
|
@ -345,6 +371,18 @@ class MeshAI:
|
|||
# Responder
|
||||
self.responder = Responder(self.config.response, self.connector)
|
||||
|
||||
# Dashboard
|
||||
if hasattr(self.config, 'dashboard') and self.config.dashboard.enabled:
|
||||
try:
|
||||
from .dashboard.server import start_dashboard
|
||||
self.broadcaster = await start_dashboard(self)
|
||||
logger.info("Dashboard started on port %d", self.config.dashboard.port)
|
||||
except Exception as e:
|
||||
logger.warning("Dashboard failed to start: %s", e)
|
||||
self.broadcaster = None
|
||||
else:
|
||||
self.broadcaster = None
|
||||
|
||||
async def _on_message(self, message: MeshMessage) -> None:
|
||||
"""Handle incoming message."""
|
||||
try:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue