f33a563003
- Add ToolManifest Pydantic models with validators for ports, mounts, health checks, and traefik config - Implement in-memory ToolRegistry with YAML loading and built-in manifest scanning - Add FastAPI CRUD routes for listing, retrieving, and creating tool manifests - Include built-in manifests for runfusion and code-server - Harden web Dockerfile with unprivileged nginx and port 8080 - Add tool manifest specification documentation and architecture updates Fusion-Task-Id: FN-003
66 lines
1.8 KiB
Python
66 lines
1.8 KiB
Python
from collections.abc import AsyncGenerator
|
|
from contextlib import asynccontextmanager
|
|
|
|
from fastapi import FastAPI
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.responses import JSONResponse
|
|
from sqlalchemy import text
|
|
|
|
from app.config import settings
|
|
from app.db import AsyncSessionLocal, engine
|
|
from app.routers import routers
|
|
from app.tools.registry import registry
|
|
from app.tools.router import router as tools_router
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
|
|
registry.load_builtin_manifests()
|
|
async with AsyncSessionLocal() as session:
|
|
try:
|
|
await session.execute(text("SELECT 1"))
|
|
except Exception:
|
|
import logging
|
|
logging.getLogger(__name__).warning("Database connectivity check failed on startup")
|
|
yield
|
|
await engine.dispose()
|
|
|
|
|
|
app = FastAPI(
|
|
title=settings.app_name,
|
|
debug=settings.debug,
|
|
lifespan=lifespan,
|
|
)
|
|
|
|
allow_origins = ["*"] if settings.debug else []
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=allow_origins,
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
for router in routers:
|
|
app.include_router(router, prefix=settings.api_v1_prefix)
|
|
|
|
app.include_router(tools_router, prefix=settings.api_v1_prefix)
|
|
|
|
|
|
@app.get("/health")
|
|
async def health() -> JSONResponse:
|
|
db_status = "connected"
|
|
try:
|
|
async with AsyncSessionLocal() as session:
|
|
await session.execute(text("SELECT 1"))
|
|
except Exception:
|
|
db_status = "unreachable"
|
|
|
|
content = {
|
|
"status": "ok" if db_status == "connected" else "degraded",
|
|
"service": settings.app_name,
|
|
"database": db_status,
|
|
}
|
|
status_code = 200 if db_status == "connected" else 503
|
|
return JSONResponse(status_code=status_code, content=content)
|