# Copyright 2026 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from contextlib import asynccontextmanager import logging from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import FileResponse from fastapi.staticfiles import StaticFiles from app.config import settings from app.routers import auth_router, health_router, session_router from app.services.session_manager import session_manager logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", ) logger = logging.getLogger("artemis.backend") @asynccontextmanager async def lifespan(app: FastAPI): """Application startup and shutdown hooks.""" logger.info(f"Starting {settings.APP_NAME} in [{settings.APP_ENV}] mode...") # Start the background session expiration watchdog session_manager.start_background_reaper() yield logger.info(f"Shutting down {settings.APP_NAME}...") app = FastAPI( title=settings.APP_NAME, description="Backend Management & Orchestration Service for Artemis on Container OS", version="1.0.0", lifespan=lifespan, ) # Enable CORS for local development and Nginx proxying app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Register API Routers app.include_router(health_router.router) app.include_router(auth_router.router) app.include_router(session_router.router) # Mount static asset directory if settings.STATIC_DIR.exists(): app.mount("/static", StaticFiles(directory=str(settings.STATIC_DIR)), name="static") @app.get("/", include_in_schema=False) async def serve_spa_root(): """Serve the root Single Page Application.""" index_file = settings.STATIC_DIR / "index.html" if index_file.exists(): return FileResponse(str(index_file)) return {"message": f"{settings.APP_NAME} is active. API documentation at /docs"} if __name__ == "__main__": import uvicorn uvicorn.run( "app.main:app", host=settings.HOST, port=settings.PORT, reload=settings.DEBUG, )