|
|
""" |
|
|
Enhanced Production Server |
|
|
Integrates all services for comprehensive crypto data tracking |
|
|
with real-time updates, persistence, and scheduling |
|
|
""" |
|
|
import asyncio |
|
|
import logging |
|
|
from fastapi import FastAPI |
|
|
from fastapi.middleware.cors import CORSMiddleware |
|
|
from fastapi.staticfiles import StaticFiles |
|
|
from contextlib import asynccontextmanager |
|
|
import uvicorn |
|
|
import os |
|
|
|
|
|
|
|
|
from backend.services.unified_config_loader import UnifiedConfigLoader |
|
|
from backend.services.scheduler_service import SchedulerService |
|
|
from backend.services.persistence_service import PersistenceService |
|
|
from backend.services.websocket_service import WebSocketService |
|
|
|
|
|
|
|
|
try: |
|
|
from database.db_manager import DatabaseManager |
|
|
except ImportError: |
|
|
DatabaseManager = None |
|
|
|
|
|
|
|
|
from backend.routers.integrated_api import router as integrated_router, set_services |
|
|
from backend.routers.advanced_api import router as advanced_router |
|
|
|
|
|
|
|
|
logging.basicConfig( |
|
|
level=logging.INFO, |
|
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' |
|
|
) |
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
|
|
|
config_loader = None |
|
|
scheduler_service = None |
|
|
persistence_service = None |
|
|
websocket_service = None |
|
|
db_manager = None |
|
|
|
|
|
|
|
|
@asynccontextmanager |
|
|
async def lifespan(app: FastAPI): |
|
|
"""Lifespan context manager for startup and shutdown""" |
|
|
global config_loader, scheduler_service, persistence_service, websocket_service, db_manager |
|
|
|
|
|
logger.info("=" * 80) |
|
|
logger.info("π Starting Enhanced Crypto Data Tracker") |
|
|
logger.info("=" * 80) |
|
|
|
|
|
try: |
|
|
|
|
|
if DatabaseManager: |
|
|
db_manager = DatabaseManager("data/crypto_tracker.db") |
|
|
db_manager.init_database() |
|
|
logger.info("β Database initialized") |
|
|
else: |
|
|
logger.warning("β Database manager not available") |
|
|
|
|
|
|
|
|
logger.info("π₯ Loading configurations...") |
|
|
config_loader = UnifiedConfigLoader() |
|
|
logger.info(f"β Loaded {len(config_loader.apis)} APIs from config files") |
|
|
|
|
|
|
|
|
logger.info("πΎ Initializing persistence service...") |
|
|
persistence_service = PersistenceService(db_manager=db_manager) |
|
|
logger.info("β Persistence service ready") |
|
|
|
|
|
|
|
|
logger.info("β° Initializing scheduler service...") |
|
|
scheduler_service = SchedulerService( |
|
|
config_loader=config_loader, |
|
|
db_manager=db_manager |
|
|
) |
|
|
|
|
|
|
|
|
logger.info("π Initializing WebSocket service...") |
|
|
websocket_service = WebSocketService( |
|
|
scheduler_service=scheduler_service, |
|
|
persistence_service=persistence_service |
|
|
) |
|
|
logger.info("β WebSocket service ready") |
|
|
|
|
|
|
|
|
set_services(config_loader, scheduler_service, persistence_service, websocket_service) |
|
|
logger.info("β Services registered with API router") |
|
|
|
|
|
|
|
|
def data_update_callback(api_id: str, data: dict): |
|
|
"""Callback for data updates from scheduler""" |
|
|
|
|
|
asyncio.create_task(persistence_service.save_api_data( |
|
|
api_id, |
|
|
data, |
|
|
metadata={'source': 'scheduler'} |
|
|
)) |
|
|
|
|
|
|
|
|
asyncio.create_task(websocket_service.notify_data_update( |
|
|
api_id, |
|
|
data, |
|
|
metadata={'source': 'scheduler'} |
|
|
)) |
|
|
|
|
|
|
|
|
for api_id in config_loader.apis.keys(): |
|
|
scheduler_service.register_callback(api_id, data_update_callback) |
|
|
|
|
|
logger.info("β Data update callbacks registered") |
|
|
|
|
|
|
|
|
logger.info("βΆοΈ Starting scheduler...") |
|
|
await scheduler_service.start() |
|
|
logger.info("β Scheduler started") |
|
|
|
|
|
logger.info("=" * 80) |
|
|
logger.info("β
All services started successfully!") |
|
|
logger.info("=" * 80) |
|
|
logger.info("") |
|
|
logger.info("π Service Summary:") |
|
|
logger.info(f" β’ APIs configured: {len(config_loader.apis)}") |
|
|
logger.info(f" β’ Categories: {len(config_loader.get_categories())}") |
|
|
logger.info(f" β’ Scheduled tasks: {len(scheduler_service.tasks)}") |
|
|
logger.info(f" β’ Real-time tasks: {len(scheduler_service.realtime_tasks)}") |
|
|
logger.info("") |
|
|
logger.info("π Access points:") |
|
|
logger.info(" β’ Main Dashboard: http://localhost:8000/") |
|
|
logger.info(" β’ API Documentation: http://localhost:8000/docs") |
|
|
logger.info(" β’ WebSocket: ws://localhost:8000/api/v2/ws") |
|
|
logger.info("") |
|
|
|
|
|
yield |
|
|
|
|
|
|
|
|
logger.info("") |
|
|
logger.info("=" * 80) |
|
|
logger.info("π Shutting down services...") |
|
|
logger.info("=" * 80) |
|
|
|
|
|
|
|
|
if scheduler_service: |
|
|
logger.info("βΈοΈ Stopping scheduler...") |
|
|
await scheduler_service.stop() |
|
|
logger.info("β Scheduler stopped") |
|
|
|
|
|
|
|
|
if persistence_service: |
|
|
logger.info("πΎ Creating final backup...") |
|
|
try: |
|
|
backup_file = await persistence_service.backup_all_data() |
|
|
logger.info(f"β Backup created: {backup_file}") |
|
|
except Exception as e: |
|
|
logger.error(f"β Backup failed: {e}") |
|
|
|
|
|
logger.info("=" * 80) |
|
|
logger.info("β
Shutdown complete") |
|
|
logger.info("=" * 80) |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"β Error during startup: {e}", exc_info=True) |
|
|
raise |
|
|
|
|
|
|
|
|
|
|
|
app = FastAPI( |
|
|
title="Enhanced Crypto Data Tracker", |
|
|
description="Comprehensive cryptocurrency data tracking with real-time updates, persistence, and scheduling", |
|
|
version="2.0.0", |
|
|
lifespan=lifespan |
|
|
) |
|
|
|
|
|
|
|
|
app.add_middleware( |
|
|
CORSMiddleware, |
|
|
allow_origins=["*"], |
|
|
allow_credentials=True, |
|
|
allow_methods=["*"], |
|
|
allow_headers=["*"], |
|
|
) |
|
|
|
|
|
|
|
|
app.include_router(integrated_router) |
|
|
app.include_router(advanced_router) |
|
|
|
|
|
|
|
|
try: |
|
|
app.mount("/static", StaticFiles(directory="static"), name="static") |
|
|
except: |
|
|
logger.warning("β Static files directory not found") |
|
|
|
|
|
|
|
|
from fastapi.responses import HTMLResponse, FileResponse |
|
|
|
|
|
|
|
|
@app.get("/", response_class=HTMLResponse) |
|
|
async def root(): |
|
|
"""Serve main dashboard""" |
|
|
if os.path.exists("index.html"): |
|
|
return FileResponse("index.html") |
|
|
else: |
|
|
return HTMLResponse(""" |
|
|
<html> |
|
|
<head> |
|
|
<title>Enhanced Crypto Data Tracker</title> |
|
|
<style> |
|
|
body { |
|
|
font-family: 'Inter', sans-serif; |
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
|
color: white; |
|
|
display: flex; |
|
|
justify-content: center; |
|
|
align-items: center; |
|
|
height: 100vh; |
|
|
margin: 0; |
|
|
} |
|
|
.container { |
|
|
text-align: center; |
|
|
background: rgba(255, 255, 255, 0.1); |
|
|
padding: 40px; |
|
|
border-radius: 20px; |
|
|
backdrop-filter: blur(10px); |
|
|
} |
|
|
h1 { margin: 0 0 20px 0; } |
|
|
.links { margin-top: 30px; } |
|
|
a { |
|
|
color: white; |
|
|
text-decoration: none; |
|
|
padding: 10px 20px; |
|
|
background: rgba(255, 255, 255, 0.2); |
|
|
border-radius: 10px; |
|
|
margin: 0 10px; |
|
|
display: inline-block; |
|
|
} |
|
|
a:hover { |
|
|
background: rgba(255, 255, 255, 0.3); |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body> |
|
|
<div class="container"> |
|
|
<h1>π Enhanced Crypto Data Tracker</h1> |
|
|
<p>Real-time cryptocurrency data tracking and monitoring</p> |
|
|
<div class="links"> |
|
|
<a href="/docs">π API Documentation</a> |
|
|
<a href="/api/v2/status">π System Status</a> |
|
|
</div> |
|
|
</div> |
|
|
</body> |
|
|
</html> |
|
|
""") |
|
|
|
|
|
|
|
|
@app.get("/dashboard.html", response_class=HTMLResponse) |
|
|
async def dashboard(): |
|
|
"""Serve simple dashboard""" |
|
|
if os.path.exists("dashboard.html"): |
|
|
return FileResponse("dashboard.html") |
|
|
return HTMLResponse("<h1>Dashboard not found</h1>") |
|
|
|
|
|
|
|
|
@app.get("/hf_console.html", response_class=HTMLResponse) |
|
|
async def hf_console(): |
|
|
"""Serve HuggingFace console""" |
|
|
if os.path.exists("hf_console.html"): |
|
|
return FileResponse("hf_console.html") |
|
|
return HTMLResponse("<h1>HF Console not found</h1>") |
|
|
|
|
|
|
|
|
@app.get("/admin.html", response_class=HTMLResponse) |
|
|
async def admin(): |
|
|
"""Serve admin panel""" |
|
|
if os.path.exists("admin.html"): |
|
|
return FileResponse("admin.html") |
|
|
return HTMLResponse("<h1>Admin panel not found</h1>") |
|
|
|
|
|
|
|
|
@app.get("/admin_advanced.html", response_class=HTMLResponse) |
|
|
async def admin_advanced(): |
|
|
"""Serve advanced admin panel""" |
|
|
if os.path.exists("admin_advanced.html"): |
|
|
return FileResponse("admin_advanced.html") |
|
|
return HTMLResponse("<h1>Advanced admin panel not found</h1>") |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
|
|
os.makedirs("data", exist_ok=True) |
|
|
os.makedirs("data/exports", exist_ok=True) |
|
|
os.makedirs("data/backups", exist_ok=True) |
|
|
|
|
|
|
|
|
uvicorn.run( |
|
|
"enhanced_server:app", |
|
|
host="0.0.0.0", |
|
|
port=8000, |
|
|
reload=False, |
|
|
log_level="info" |
|
|
) |
|
|
|