from fastapi import FastAPI, Depends, HTTPException, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.exceptions import RequestValidationError import uvicorn from pathlib import Path from sqlalchemy.orm import Session import logging import time from app.database import get_db, Base, engine from app.api.routers import assets, exchanges, markets, rates, health from app.exceptions import ( CoinCapAPIException, validation_exception_handler, http_exception_handler, coincap_api_exception_handler, general_exception_handler ) from app.utils.logging import setup_logging from starlette.exceptions import HTTPException as StarletteHTTPException # Setup logging setup_logging(log_level="INFO") logger = logging.getLogger(__name__) # Create the FastAPI app app = FastAPI( title="Cryptocurrency Data Service", description="Backend service for cryptocurrency data from CoinCap API", version="0.1.0", ) # Register exception handlers app.add_exception_handler(RequestValidationError, validation_exception_handler) app.add_exception_handler(StarletteHTTPException, http_exception_handler) app.add_exception_handler(CoinCapAPIException, coincap_api_exception_handler) app.add_exception_handler(Exception, general_exception_handler) # CORS middleware configuration app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Request timing middleware @app.middleware("http") async def add_process_time_header(request: Request, call_next): start_time = time.time() response = await call_next(request) process_time = time.time() - start_time response.headers["X-Process-Time"] = str(process_time) logger.debug(f"Request to {request.url.path} processed in {process_time:.4f} seconds") return response # Include routers app.include_router(health.router) app.include_router(assets.router, prefix="/api") app.include_router(exchanges.router, prefix="/api") app.include_router(markets.router, prefix="/api") app.include_router(rates.router, prefix="/api") # Startup event @app.on_event("startup") async def startup_event(): logger.info("Starting up Cryptocurrency Data Service") # Create database tables at startup # In production, you should use Alembic migrations instead # Base.metadata.create_all(bind=engine) # Shutdown event @app.on_event("shutdown") async def shutdown_event(): logger.info("Shutting down Cryptocurrency Data Service") if __name__ == "__main__": uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)