webui fixes

This commit is contained in:
Dobromir Popov
2025-08-05 16:05:05 +03:00
parent bf4d43f6f7
commit 468fa0dcd6
4 changed files with 244 additions and 89 deletions

View File

@ -98,7 +98,8 @@
- Create comprehensive API documentation
- _Requirements: 4.1, 4.2, 4.4, 6.3_
- [-] 9. Implement web dashboard for visualization
- [ ] 9. Implement web dashboard for visualization

View File

@ -4,7 +4,7 @@ REST API server for COBY system.
from fastapi import FastAPI, HTTPException, Request, Query, Path, WebSocket, WebSocketDisconnect
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from fastapi.responses import JSONResponse, HTMLResponse
from fastapi.staticfiles import StaticFiles
from typing import Optional, List
import asyncio
@ -77,8 +77,6 @@ def create_app(config_obj=None) -> FastAPI:
redoc_url="/redoc"
)
# We'll mount static files AFTER defining all API routes to avoid conflicts
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
@ -98,58 +96,94 @@ def create_app(config_obj=None) -> FastAPI:
@app.websocket("/ws/dashboard")
async def websocket_endpoint(websocket: WebSocket):
"""WebSocket endpoint for dashboard real-time updates"""
"""WebSocket endpoint for real-time dashboard 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 = {
# Send periodic status updates
status_data = {
"type": "status",
"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
}
"connections": len(connection_manager.active_connections),
"system": "healthy"
}
await connection_manager.send_personal_message(
json.dumps(system_data),
websocket
json.dumps(status_data), websocket
)
await asyncio.sleep(30) # Send update every 30 seconds
except WebSocketDisconnect:
connection_manager.disconnect(websocket)
except Exception as e:
logger.error(f"WebSocket error: {e}")
connection_manager.disconnect(websocket)
@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")
)
@app.get("/health")
async def health_check():
"""Health check endpoint"""
return response_formatter.health(healthy=True, components={
"api": {"healthy": True},
"cache": {"healthy": redis_manager.is_connected()},
"database": {"healthy": True} # Stub
})
try:
# Check Redis connection
redis_healthy = await redis_manager.ping()
@app.get("/")
health_data = {
'status': 'healthy' if redis_healthy else 'degraded',
'redis': 'connected' if redis_healthy else 'disconnected',
'version': '1.0.0'
}
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")
)
@app.get("/", response_class=HTMLResponse)
async def root():
"""Root endpoint - serve dashboard"""
return {"message": "COBY Multi-Exchange Data Aggregation System", "status": "running"}
"""Root endpoint - serve dashboard HTML"""
static_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "web", "static")
index_path = os.path.join(static_path, "index.html")
if os.path.exists(index_path):
with open(index_path, 'r', encoding='utf-8') as f:
return HTMLResponse(content=f.read())
else:
# Fallback if index.html doesn't exist
return HTMLResponse(content="""
<!DOCTYPE html>
<html>
<head><title>COBY System</title></head>
<body>
<h1>COBY Multi-Exchange Data Aggregation System</h1>
<p>System is running. Dashboard files not found.</p>
<p><a href="/api/health">Health Check</a></p>
</body>
</html>
""")
@app.middleware("http")
async def rate_limit_middleware(request: Request, call_next):
@ -203,53 +237,6 @@ 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():
"""Health check endpoint"""
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'
}
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")
)
# Heatmap endpoints
@app.get("/api/v1/heatmap/{symbol}")
async def get_heatmap(
@ -519,8 +506,7 @@ def create_app(config_obj=None) -> FastAPI:
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")
# Don't mount at root to avoid conflicts with WebSocket and API routes
return app

163
COBY/test_fixes.py Normal file
View File

@ -0,0 +1,163 @@
#!/usr/bin/env python3
"""
Test script to verify COBY system fixes.
"""
import asyncio
import sys
import os
# Add the current directory to Python path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
try:
from api.rest_api import create_app
from caching.redis_manager import redis_manager
from utils.logging import get_logger, setup_logging
print("✓ All imports successful")
except ImportError as e:
print(f"✗ Import error: {e}")
sys.exit(1)
logger = get_logger(__name__)
async def test_health_endpoints():
"""Test health endpoints"""
print("\n--- Testing Health Endpoints ---")
try:
# Test Redis manager
await redis_manager.initialize()
ping_result = await redis_manager.ping()
print(f"✓ Redis ping: {ping_result}")
# Test app creation
app = create_app()
print("✓ FastAPI app created successfully")
# Test health endpoint logic
from api.response_formatter import ResponseFormatter
formatter = ResponseFormatter()
health_data = {
'status': 'healthy' if ping_result else 'degraded',
'redis': 'connected' if ping_result else 'disconnected',
'version': '1.0.0'
}
response = formatter.status_response(health_data)
print(f"✓ Health response format: {type(response)}")
return True
except Exception as e:
print(f"✗ Health endpoint test failed: {e}")
return False
async def test_static_files():
"""Test static file serving"""
print("\n--- Testing Static Files ---")
try:
static_path = os.path.join(os.path.dirname(__file__), "web", "static")
index_path = os.path.join(static_path, "index.html")
if os.path.exists(static_path):
print(f"✓ Static directory exists: {static_path}")
else:
print(f"✗ Static directory missing: {static_path}")
return False
if os.path.exists(index_path):
print(f"✓ Index.html exists: {index_path}")
# Test reading the file
with open(index_path, 'r', encoding='utf-8') as f:
content = f.read()
if "COBY" in content:
print("✓ Index.html contains COBY content")
else:
print("✗ Index.html missing COBY content")
return False
else:
print(f"✗ Index.html missing: {index_path}")
return False
return True
except Exception as e:
print(f"✗ Static files test failed: {e}")
return False
async def test_websocket_config():
"""Test WebSocket configuration"""
print("\n--- Testing WebSocket Configuration ---")
try:
from api.simple_websocket_server import WebSocketServer
from simple_config import config
ws_server = WebSocketServer(
host=config.api.host,
port=config.api.websocket_port
)
print(f"✓ WebSocket server configured: {config.api.host}:{config.api.websocket_port}")
return True
except Exception as e:
print(f"✗ WebSocket config test failed: {e}")
return False
async def main():
"""Run all tests"""
print("COBY System Fix Verification")
print("=" * 40)
# Setup logging
setup_logging(level='INFO')
tests = [
test_health_endpoints,
test_static_files,
test_websocket_config
]
results = []
for test in tests:
try:
result = await test()
results.append(result)
except Exception as e:
print(f"✗ Test {test.__name__} failed with exception: {e}")
results.append(False)
print("\n" + "=" * 40)
print("Test Results Summary:")
passed = sum(results)
total = len(results)
for i, result in enumerate(results):
status = "✓ PASS" if result else "✗ FAIL"
print(f" Test {i+1}: {status}")
print(f"\nOverall: {passed}/{total} tests passed")
if passed == total:
print("🎉 All tests passed! COBY system should work correctly.")
return 0
else:
print("❌ Some tests failed. Please check the issues above.")
return 1
if __name__ == "__main__":
try:
exit_code = asyncio.run(main())
sys.exit(exit_code)
except KeyboardInterrupt:
print("\nTest interrupted by user")
sys.exit(1)
except Exception as e:
print(f"Test failed with error: {e}")
sys.exit(1)

View File

@ -322,7 +322,9 @@
function updateSystemStatus(data) {
const systemStatus = document.getElementById('system-status');
const status = data.status || 'unknown';
// The API returns nested data structure: data.data.status
const actualData = data.data || data;
const status = actualData.status || 'unknown';
const healthy = status === 'healthy';
systemStatus.innerHTML = `
@ -333,6 +335,9 @@
<p style="margin-top: 0.5rem; font-size: 0.9rem; color: #666;">
Last updated: ${new Date(data.timestamp || Date.now()).toLocaleString()}
</p>
<p style="margin-top: 0.25rem; font-size: 0.8rem; color: #888;">
Redis: ${actualData.redis || 'unknown'} | Version: ${actualData.version || 'unknown'}
</p>
`;
}