""" Cosmo - Deep Space Explorer Backend API FastAPI application entry point """ import sys from pathlib import Path # Add backend directory to Python path for direct execution backend_dir = Path(__file__).resolve().parent.parent if str(backend_dir) not in sys.path: sys.path.insert(0, str(backend_dir)) import logging from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from app.config import settings from app.api.auth import router as auth_router from app.api.user import router as user_router from app.api.system import router as system_router from app.api.danmaku import router as danmaku_router from app.api.task import router as task_router from app.api.cache import router as cache_router from app.api.celestial_static import router as celestial_static_router from app.api.celestial_body import router as celestial_body_router from app.api.celestial_resource import router as celestial_resource_router from app.api.celestial_orbit import router as celestial_orbit_router from app.api.nasa_download import router as nasa_download_router from app.api.celestial_position import router as celestial_position_router from app.services.redis_cache import redis_cache from app.services.cache_preheat import preheat_all_caches from app.database import close_db # Configure logging logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", ) logger = logging.getLogger(__name__) @asynccontextmanager async def lifespan(app: FastAPI): """Application lifespan manager - startup and shutdown events""" # Startup logger.info("=" * 60) logger.info("Starting Cosmo Backend API...") logger.info("=" * 60) # Connect to Redis await redis_cache.connect() # Initialize database tables (create if not exist) from app.database import engine, Base from app.models.db import SystemSettings # Import to register the model async with engine.begin() as conn: await conn.run_sync(Base.metadata.create_all) logger.info("✓ Database tables initialized") # Initialize default system settings from app.database import AsyncSessionLocal from app.services.system_settings_service import system_settings_service async with AsyncSessionLocal() as db: await system_settings_service.initialize_default_settings(db) await db.commit() logger.info("✓ Default system settings initialized") # Preheat caches (load from database to Redis) await preheat_all_caches() logger.info("✓ Application started successfully") logger.info("=" * 60) yield # Shutdown logger.info("=" * 60) logger.info("Shutting down Cosmo Backend API...") # Disconnect Redis await redis_cache.disconnect() # Close database connections await close_db() logger.info("✓ Application shutdown complete") logger.info("=" * 60) # Create FastAPI app app = FastAPI( title=settings.app_name, description="Backend API for deep space probe visualization using NASA JPL Horizons data", version="1.0.0", lifespan=lifespan, ) # Configure CORS app.add_middleware( CORSMiddleware, allow_origins=settings.cors_origins, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Include routers app.include_router(auth_router, prefix=settings.api_prefix) app.include_router(user_router, prefix=settings.api_prefix) app.include_router(system_router, prefix=settings.api_prefix) app.include_router(danmaku_router, prefix=settings.api_prefix) # Celestial body related routers app.include_router(celestial_body_router, prefix=settings.api_prefix) app.include_router(celestial_position_router, prefix=settings.api_prefix) app.include_router(celestial_resource_router, prefix=settings.api_prefix) app.include_router(celestial_orbit_router, prefix=settings.api_prefix) app.include_router(celestial_static_router, prefix=settings.api_prefix) # Admin and utility routers app.include_router(cache_router, prefix=settings.api_prefix) app.include_router(nasa_download_router, prefix=settings.api_prefix) app.include_router(task_router, prefix=settings.api_prefix) # Mount static files for uploaded resources upload_dir = Path(__file__).parent.parent / "upload" upload_dir.mkdir(exist_ok=True) app.mount("/upload", StaticFiles(directory=str(upload_dir)), name="upload") logger.info(f"Static files mounted at /upload -> {upload_dir}") # Mount public assets directory public_assets_dir = Path(__file__).parent.parent / "public" / "assets" public_assets_dir.mkdir(parents=True, exist_ok=True) app.mount("/public/assets", StaticFiles(directory=str(public_assets_dir)), name="public_assets") logger.info(f"Public assets mounted at /public/assets -> {public_assets_dir}") @app.get("/") async def root(): """Root endpoint""" return { "app": settings.app_name, "version": "1.0.0", "docs": "/docs", "api": settings.api_prefix, } @app.get("/health") async def health(): """Health check endpoint with service status""" from app.services.redis_cache import redis_cache redis_stats = await redis_cache.get_stats() return { "status": "healthy", "redis": redis_stats, "database": "connected", # If we got here, database is working } if __name__ == "__main__": import uvicorn uvicorn.run( "app.main:app", host="0.0.0.0", port=8000, reload=True, log_level="info", )