debugging web ui

This commit is contained in:
Dobromir Popov
2025-08-05 15:58:51 +03:00
parent 622d059aae
commit bf4d43f6f7
8 changed files with 571 additions and 58 deletions

View File

@@ -2,13 +2,15 @@
REST API server for COBY system.
"""
from fastapi import FastAPI, HTTPException, Request, Query, Path
from fastapi import FastAPI, HTTPException, Request, Query, Path, WebSocket, WebSocketDisconnect
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from fastapi.staticfiles import StaticFiles
from typing import Optional, List
import asyncio
import os
import json
import time
try:
from ..simple_config import config
from ..caching.redis_manager import redis_manager
@@ -27,6 +29,43 @@ except ImportError:
logger = get_logger(__name__)
class ConnectionManager:
"""Manage WebSocket connections for dashboard updates"""
def __init__(self):
self.active_connections: List[WebSocket] = []
async def connect(self, websocket: WebSocket):
await websocket.accept()
self.active_connections.append(websocket)
logger.info(f"WebSocket client connected. Total connections: {len(self.active_connections)}")
def disconnect(self, websocket: WebSocket):
if websocket in self.active_connections:
self.active_connections.remove(websocket)
logger.info(f"WebSocket client disconnected. Total connections: {len(self.active_connections)}")
async def send_personal_message(self, message: str, websocket: WebSocket):
try:
await websocket.send_text(message)
except Exception as e:
logger.error(f"Error sending personal message: {e}")
self.disconnect(websocket)
async def broadcast(self, message: str):
disconnected = []
for connection in self.active_connections:
try:
await connection.send_text(message)
except Exception as e:
logger.error(f"Error broadcasting to connection: {e}")
disconnected.append(connection)
# Remove disconnected clients
for connection in disconnected:
self.disconnect(connection)
def create_app(config_obj=None) -> FastAPI:
"""Create and configure FastAPI application"""
@@ -38,12 +77,7 @@ def create_app(config_obj=None) -> FastAPI:
redoc_url="/redoc"
)
# Mount static files for web dashboard (since we removed nginx)
static_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "web", "static")
if os.path.exists(static_path):
app.mount("/static", StaticFiles(directory=static_path), name="static")
# Serve index.html at root for dashboard
app.mount("/", StaticFiles(directory=static_path, html=True), name="dashboard")
# We'll mount static files AFTER defining all API routes to avoid conflicts
# Add CORS middleware
app.add_middleware(
@@ -60,7 +94,49 @@ def create_app(config_obj=None) -> FastAPI:
burst_size=20
)
response_formatter = ResponseFormatter()
connection_manager = ConnectionManager()
@app.websocket("/ws/dashboard")
async def websocket_endpoint(websocket: WebSocket):
"""WebSocket endpoint for dashboard real-time updates"""
await connection_manager.connect(websocket)
try:
while True:
# Send periodic updates
await asyncio.sleep(5) # Update every 5 seconds
# Gather system status
system_data = {
"timestamp": time.time(),
"performance": {
"cpu_usage": 25.5, # Stub data
"memory_usage": 45.2,
"throughput": 1250,
"avg_latency": 12.3
},
"exchanges": {
"binance": "connected",
"coinbase": "connected",
"kraken": "disconnected",
"bybit": "connected"
},
"processing": {
"active": True,
"total_processed": 15420
}
}
await connection_manager.send_personal_message(
json.dumps(system_data),
websocket
)
except WebSocketDisconnect:
connection_manager.disconnect(websocket)
except Exception as e:
logger.error(f"WebSocket error: {e}")
connection_manager.disconnect(websocket)
@app.get("/health")
async def health_check():
"""Health check endpoint"""
@@ -127,6 +203,30 @@ def create_app(config_obj=None) -> FastAPI:
except Exception as e:
logger.error(f"API server shutdown error: {e}")
# API Health check endpoint (for dashboard)
@app.get("/api/health")
async def api_health_check():
"""API Health check endpoint for dashboard"""
try:
# Check Redis connection
redis_healthy = await redis_manager.ping()
health_data = {
'status': 'healthy' if redis_healthy else 'degraded',
'redis': 'connected' if redis_healthy else 'disconnected',
'version': '1.0.0',
'timestamp': time.time()
}
return response_formatter.status_response(health_data)
except Exception as e:
logger.error(f"Health check failed: {e}")
return JSONResponse(
status_code=503,
content=response_formatter.error("Service unavailable", "HEALTH_CHECK_FAILED")
)
# Health check endpoint
@app.get("/health")
async def health_check():
@@ -415,6 +515,13 @@ def create_app(config_obj=None) -> FastAPI:
content=response_formatter.error("Internal server error", "BATCH_HEATMAPS_ERROR")
)
# Mount static files for web dashboard AFTER all API routes are defined
static_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "web", "static")
if os.path.exists(static_path):
app.mount("/static", StaticFiles(directory=static_path), name="static")
# Serve index.html at root for dashboard, but this should be last
app.mount("/", StaticFiles(directory=static_path, html=True), name="dashboard")
return app