webui fixes
This commit is contained in:
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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
163
COBY/test_fixes.py
Normal 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)
|
@ -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>
|
||||
`;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user