|
|
"""Simple FastAPI server for testing HF integration""" |
|
|
import asyncio |
|
|
from fastapi import FastAPI |
|
|
from fastapi.middleware.cors import CORSMiddleware |
|
|
from fastapi.responses import FileResponse, JSONResponse |
|
|
from fastapi.staticfiles import StaticFiles |
|
|
import uvicorn |
|
|
|
|
|
|
|
|
app = FastAPI(title="Crypto API Monitor - Simple", version="1.0.0") |
|
|
|
|
|
|
|
|
app.add_middleware( |
|
|
CORSMiddleware, |
|
|
allow_origins=["*"], |
|
|
allow_credentials=True, |
|
|
allow_methods=["*"], |
|
|
allow_headers=["*"], |
|
|
) |
|
|
|
|
|
|
|
|
try: |
|
|
from backend.routers import hf_connect |
|
|
app.include_router(hf_connect.router) |
|
|
print("β HF router loaded") |
|
|
except Exception as e: |
|
|
print(f"β HF router failed: {e}") |
|
|
|
|
|
|
|
|
@app.on_event("startup") |
|
|
async def startup_hf(): |
|
|
try: |
|
|
from backend.services.hf_registry import periodic_refresh |
|
|
asyncio.create_task(periodic_refresh()) |
|
|
print("β HF background refresh started") |
|
|
except Exception as e: |
|
|
print(f"β HF background refresh failed: {e}") |
|
|
|
|
|
|
|
|
@app.get("/health") |
|
|
async def health(): |
|
|
return {"status": "healthy", "service": "crypto-api-monitor"} |
|
|
|
|
|
@app.get("/api/health") |
|
|
async def api_health(): |
|
|
return {"status": "healthy", "service": "crypto-api-monitor-api"} |
|
|
|
|
|
|
|
|
@app.get("/") |
|
|
async def root(): |
|
|
return FileResponse("index.html") |
|
|
|
|
|
@app.get("/index.html") |
|
|
async def index(): |
|
|
return FileResponse("index.html") |
|
|
|
|
|
@app.get("/hf_console.html") |
|
|
async def hf_console(): |
|
|
return FileResponse("hf_console.html") |
|
|
|
|
|
|
|
|
@app.get("/api/status") |
|
|
async def api_status(): |
|
|
"""Mock status endpoint""" |
|
|
return { |
|
|
"total_providers": 9, |
|
|
"online": 7, |
|
|
"degraded": 1, |
|
|
"offline": 1, |
|
|
"avg_response_time_ms": 245, |
|
|
"total_requests_hour": 156, |
|
|
"total_failures_hour": 3, |
|
|
"system_health": "healthy", |
|
|
"timestamp": "2025-11-11T01:30:00Z" |
|
|
} |
|
|
|
|
|
@app.get("/api/categories") |
|
|
async def api_categories(): |
|
|
"""Mock categories endpoint""" |
|
|
return [ |
|
|
{ |
|
|
"name": "market_data", |
|
|
"total_sources": 3, |
|
|
"online_sources": 3, |
|
|
"avg_response_time_ms": 180, |
|
|
"rate_limited_count": 0, |
|
|
"last_updated": "2025-11-11T01:30:00Z", |
|
|
"status": "online" |
|
|
}, |
|
|
{ |
|
|
"name": "blockchain_explorers", |
|
|
"total_sources": 3, |
|
|
"online_sources": 2, |
|
|
"avg_response_time_ms": 320, |
|
|
"rate_limited_count": 1, |
|
|
"last_updated": "2025-11-11T01:29:00Z", |
|
|
"status": "online" |
|
|
}, |
|
|
{ |
|
|
"name": "news", |
|
|
"total_sources": 2, |
|
|
"online_sources": 2, |
|
|
"avg_response_time_ms": 450, |
|
|
"rate_limited_count": 0, |
|
|
"last_updated": "2025-11-11T01:28:00Z", |
|
|
"status": "online" |
|
|
}, |
|
|
{ |
|
|
"name": "sentiment", |
|
|
"total_sources": 1, |
|
|
"online_sources": 1, |
|
|
"avg_response_time_ms": 200, |
|
|
"rate_limited_count": 0, |
|
|
"last_updated": "2025-11-11T01:30:00Z", |
|
|
"status": "online" |
|
|
} |
|
|
] |
|
|
|
|
|
@app.get("/api/providers") |
|
|
async def api_providers(): |
|
|
"""Mock providers endpoint""" |
|
|
return [ |
|
|
{ |
|
|
"id": 1, |
|
|
"name": "CoinGecko", |
|
|
"category": "market_data", |
|
|
"status": "online", |
|
|
"response_time_ms": 150, |
|
|
"last_fetch": "2025-11-11T01:30:00Z", |
|
|
"has_key": False, |
|
|
"rate_limit": None |
|
|
}, |
|
|
{ |
|
|
"id": 2, |
|
|
"name": "Binance", |
|
|
"category": "market_data", |
|
|
"status": "online", |
|
|
"response_time_ms": 120, |
|
|
"last_fetch": "2025-11-11T01:30:00Z", |
|
|
"has_key": False, |
|
|
"rate_limit": None |
|
|
}, |
|
|
{ |
|
|
"id": 3, |
|
|
"name": "Alternative.me", |
|
|
"category": "sentiment", |
|
|
"status": "online", |
|
|
"response_time_ms": 200, |
|
|
"last_fetch": "2025-11-11T01:29:00Z", |
|
|
"has_key": False, |
|
|
"rate_limit": None |
|
|
}, |
|
|
{ |
|
|
"id": 4, |
|
|
"name": "Etherscan", |
|
|
"category": "blockchain_explorers", |
|
|
"status": "online", |
|
|
"response_time_ms": 280, |
|
|
"last_fetch": "2025-11-11T01:29:30Z", |
|
|
"has_key": True, |
|
|
"rate_limit": {"used": 45, "total": 100} |
|
|
}, |
|
|
{ |
|
|
"id": 5, |
|
|
"name": "CryptoPanic", |
|
|
"category": "news", |
|
|
"status": "online", |
|
|
"response_time_ms": 380, |
|
|
"last_fetch": "2025-11-11T01:28:00Z", |
|
|
"has_key": False, |
|
|
"rate_limit": None |
|
|
} |
|
|
] |
|
|
|
|
|
@app.get("/api/charts/health-history") |
|
|
async def api_health_history(hours: int = 24): |
|
|
"""Mock health history chart data""" |
|
|
import random |
|
|
from datetime import datetime, timedelta |
|
|
now = datetime.now() |
|
|
timestamps = [(now - timedelta(hours=i)).isoformat() for i in range(23, -1, -1)] |
|
|
return { |
|
|
"timestamps": timestamps, |
|
|
"success_rate": [random.randint(85, 100) for _ in range(24)] |
|
|
} |
|
|
|
|
|
@app.get("/api/charts/compliance") |
|
|
async def api_compliance(days: int = 7): |
|
|
"""Mock compliance chart data""" |
|
|
import random |
|
|
from datetime import datetime, timedelta |
|
|
now = datetime.now() |
|
|
dates = [(now - timedelta(days=i)).strftime("%a") for i in range(6, -1, -1)] |
|
|
return { |
|
|
"dates": dates, |
|
|
"compliance_percentage": [random.randint(90, 100) for _ in range(7)] |
|
|
} |
|
|
|
|
|
@app.get("/api/logs") |
|
|
async def api_logs(): |
|
|
"""Mock logs endpoint""" |
|
|
return [ |
|
|
{ |
|
|
"timestamp": "2025-11-11T01:30:00Z", |
|
|
"provider": "CoinGecko", |
|
|
"endpoint": "/api/v3/ping", |
|
|
"status": "success", |
|
|
"response_time_ms": 150, |
|
|
"http_code": 200, |
|
|
"error_message": None |
|
|
}, |
|
|
{ |
|
|
"timestamp": "2025-11-11T01:29:30Z", |
|
|
"provider": "Binance", |
|
|
"endpoint": "/api/v3/klines", |
|
|
"status": "success", |
|
|
"response_time_ms": 120, |
|
|
"http_code": 200, |
|
|
"error_message": None |
|
|
}, |
|
|
{ |
|
|
"timestamp": "2025-11-11T01:29:00Z", |
|
|
"provider": "Alternative.me", |
|
|
"endpoint": "/fng/", |
|
|
"status": "success", |
|
|
"response_time_ms": 200, |
|
|
"http_code": 200, |
|
|
"error_message": None |
|
|
} |
|
|
] |
|
|
|
|
|
@app.get("/api/rate-limits") |
|
|
async def api_rate_limits(): |
|
|
"""Mock rate limits endpoint""" |
|
|
return [ |
|
|
{ |
|
|
"provider": "CoinGecko", |
|
|
"limit_type": "per_minute", |
|
|
"limit_value": 50, |
|
|
"current_usage": 12, |
|
|
"percentage": 24.0, |
|
|
"reset_in_seconds": 45 |
|
|
}, |
|
|
{ |
|
|
"provider": "Etherscan", |
|
|
"limit_type": "per_second", |
|
|
"limit_value": 5, |
|
|
"current_usage": 3, |
|
|
"percentage": 60.0, |
|
|
"reset_in_seconds": 1 |
|
|
} |
|
|
] |
|
|
|
|
|
@app.get("/api/charts/rate-limit-history") |
|
|
async def api_rate_limit_history(hours: int = 24): |
|
|
"""Mock rate limit history chart data""" |
|
|
import random |
|
|
from datetime import datetime, timedelta |
|
|
now = datetime.now() |
|
|
timestamps = [(now - timedelta(hours=i)).strftime("%H:00") for i in range(23, -1, -1)] |
|
|
return { |
|
|
"timestamps": timestamps, |
|
|
"providers": { |
|
|
"CoinGecko": [random.randint(10, 40) for _ in range(24)], |
|
|
"Etherscan": [random.randint(40, 80) for _ in range(24)] |
|
|
} |
|
|
} |
|
|
|
|
|
@app.get("/api/schedule") |
|
|
async def api_schedule(): |
|
|
"""Mock schedule endpoint""" |
|
|
return [ |
|
|
{ |
|
|
"provider": "CoinGecko", |
|
|
"category": "market_data", |
|
|
"schedule": "every_1_min", |
|
|
"last_run": "2025-11-11T01:30:00Z", |
|
|
"next_run": "2025-11-11T01:31:00Z", |
|
|
"on_time_percentage": 98.5, |
|
|
"status": "active" |
|
|
}, |
|
|
{ |
|
|
"provider": "Binance", |
|
|
"category": "market_data", |
|
|
"schedule": "every_1_min", |
|
|
"last_run": "2025-11-11T01:30:00Z", |
|
|
"next_run": "2025-11-11T01:31:00Z", |
|
|
"on_time_percentage": 99.2, |
|
|
"status": "active" |
|
|
}, |
|
|
{ |
|
|
"provider": "Alternative.me", |
|
|
"category": "sentiment", |
|
|
"schedule": "every_15_min", |
|
|
"last_run": "2025-11-11T01:15:00Z", |
|
|
"next_run": "2025-11-11T01:30:00Z", |
|
|
"on_time_percentage": 97.8, |
|
|
"status": "active" |
|
|
} |
|
|
] |
|
|
|
|
|
@app.get("/api/freshness") |
|
|
async def api_freshness(): |
|
|
"""Mock freshness endpoint""" |
|
|
return [ |
|
|
{ |
|
|
"provider": "CoinGecko", |
|
|
"category": "market_data", |
|
|
"fetch_time": "2025-11-11T01:30:00Z", |
|
|
"data_timestamp": "2025-11-11T01:29:55Z", |
|
|
"staleness_minutes": 0.08, |
|
|
"ttl_minutes": 5, |
|
|
"status": "fresh" |
|
|
}, |
|
|
{ |
|
|
"provider": "Binance", |
|
|
"category": "market_data", |
|
|
"fetch_time": "2025-11-11T01:30:00Z", |
|
|
"data_timestamp": "2025-11-11T01:29:58Z", |
|
|
"staleness_minutes": 0.03, |
|
|
"ttl_minutes": 5, |
|
|
"status": "fresh" |
|
|
} |
|
|
] |
|
|
|
|
|
@app.get("/api/failures") |
|
|
async def api_failures(): |
|
|
"""Mock failures endpoint""" |
|
|
return { |
|
|
"recent_failures": [ |
|
|
{ |
|
|
"timestamp": "2025-11-11T01:25:00Z", |
|
|
"provider": "NewsAPI", |
|
|
"error_type": "timeout", |
|
|
"error_message": "Request timeout after 10s", |
|
|
"retry_attempted": True, |
|
|
"retry_result": "success" |
|
|
} |
|
|
], |
|
|
"error_type_distribution": { |
|
|
"timeout": 2, |
|
|
"rate_limit": 1, |
|
|
"connection_error": 0 |
|
|
}, |
|
|
"top_failing_providers": [ |
|
|
{"provider": "NewsAPI", "failure_count": 2}, |
|
|
{"provider": "TronScan", "failure_count": 1} |
|
|
], |
|
|
"remediation_suggestions": [ |
|
|
{ |
|
|
"provider": "NewsAPI", |
|
|
"issue": "Frequent timeouts", |
|
|
"suggestion": "Consider increasing timeout threshold or checking network connectivity" |
|
|
} |
|
|
] |
|
|
} |
|
|
|
|
|
@app.get("/api/charts/freshness-history") |
|
|
async def api_freshness_history(hours: int = 24): |
|
|
"""Mock freshness history chart data""" |
|
|
import random |
|
|
from datetime import datetime, timedelta |
|
|
now = datetime.now() |
|
|
timestamps = [(now - timedelta(hours=i)).strftime("%H:00") for i in range(23, -1, -1)] |
|
|
return { |
|
|
"timestamps": timestamps, |
|
|
"providers": { |
|
|
"CoinGecko": [random.uniform(0.1, 2.0) for _ in range(24)], |
|
|
"Binance": [random.uniform(0.05, 1.5) for _ in range(24)] |
|
|
} |
|
|
} |
|
|
|
|
|
@app.get("/api/config/keys") |
|
|
async def api_config_keys(): |
|
|
"""Mock API keys config""" |
|
|
return [ |
|
|
{ |
|
|
"provider": "Etherscan", |
|
|
"key_masked": "YourApiKeyToken...abc123", |
|
|
"expires_at": None, |
|
|
"status": "active" |
|
|
}, |
|
|
{ |
|
|
"provider": "CoinMarketCap", |
|
|
"key_masked": "b54bcf4d-1bca...xyz789", |
|
|
"expires_at": "2025-12-31", |
|
|
"status": "active" |
|
|
} |
|
|
] |
|
|
|
|
|
if __name__ == "__main__": |
|
|
print("=" * 70) |
|
|
print("π Starting Crypto API Monitor - Simple Server") |
|
|
print("=" * 70) |
|
|
print("π Server: http://localhost:7860") |
|
|
print("π Main Dashboard: http://localhost:7860/index.html") |
|
|
print("π€ HF Console: http://localhost:7860/hf_console.html") |
|
|
print("π API Docs: http://localhost:7860/docs") |
|
|
print("=" * 70) |
|
|
print() |
|
|
|
|
|
uvicorn.run( |
|
|
app, |
|
|
host="0.0.0.0", |
|
|
port=7860, |
|
|
log_level="info" |
|
|
) |
|
|
|