4201326467
Remove stale ToolConfig and ConfigFolder backend/frontend surfaces after the ConfigProfile refactor. Drop dead routers, schemas, model exports, frontend routes, clients, pages, and tests; keep ToolType API compatibility for existing interface/is_builtin response shape. Quality gates: backend LSP diagnostics passed; backend py_compile passed; backend ruff passed; frontend ToolWorkshopPage test passed. Frontend typecheck blocked by unrelated missing xterm-addon-serialize types.
134 lines
4.4 KiB
Python
134 lines
4.4 KiB
Python
import logging
|
|
import os
|
|
|
|
from fastapi import FastAPI, Request
|
|
from fastapi.exceptions import RequestValidationError
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.responses import JSONResponse
|
|
from fastapi.staticfiles import StaticFiles
|
|
from src.api.auth import router as auth_router
|
|
from src.api.dashboard import router as dashboard_router
|
|
from src.api.git_repositories import router as git_repositories_router
|
|
from src.api.health import router as health_router
|
|
from src.api.projects import router as projects_router
|
|
from src.api.ssh_keys import router as ssh_keys_router
|
|
from src.api.terminal import router as terminal_router
|
|
from src.api.instance_proxy import router as instance_proxy_router
|
|
from src.api.config_profiles import router as config_profiles_router
|
|
from src.api.tool_instances import router as tool_instances_router
|
|
from src.api.tool_types import router as tool_types_router
|
|
from src.api.user_config import router as user_config_router
|
|
from src.api.users import router as users_router
|
|
from src.config import Settings
|
|
from src.database import init_database
|
|
from src.logging_config import (
|
|
ExceptionLoggingMiddleware,
|
|
RequestLoggingMiddleware,
|
|
configure_logging,
|
|
)
|
|
from src.seeds.builtin_tool_types import seed_builtin_tool_types
|
|
|
|
# Configure logging early
|
|
log_level = os.getenv("LOG_LEVEL", "INFO").upper()
|
|
configure_logging(level=getattr(logging, log_level, logging.INFO))
|
|
|
|
logger = logging.getLogger(__name__)
|
|
settings = Settings()
|
|
app = FastAPI(title="Headquarter API")
|
|
|
|
# Configure CORS - must be before other middleware
|
|
# Build allowed origins list including web and api domains
|
|
cors_origins = [settings.web_base_url]
|
|
if settings.api_base_url != settings.web_base_url:
|
|
cors_origins.append(settings.api_base_url)
|
|
logger.info("CORS configured with origins: %s", cors_origins)
|
|
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=cors_origins,
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
app.add_middleware(RequestLoggingMiddleware)
|
|
app.add_middleware(ExceptionLoggingMiddleware)
|
|
|
|
|
|
def _sanitize_validation_errors(errors):
|
|
"""Convert validation errors to JSON-safe format."""
|
|
sanitized = []
|
|
for error in errors:
|
|
safe_error = {
|
|
"type": error.get("type"),
|
|
"loc": error.get("loc"),
|
|
"msg": error.get("msg"),
|
|
"input": str(error.get("input"))
|
|
if error.get("input") is not None
|
|
else None,
|
|
}
|
|
# Convert ctx to safe format
|
|
ctx = error.get("ctx")
|
|
if ctx:
|
|
safe_ctx = {}
|
|
for key, value in ctx.items():
|
|
if isinstance(value, Exception):
|
|
safe_ctx[key] = str(value)
|
|
elif isinstance(value, (str, int, float, bool, type(None))):
|
|
safe_ctx[key] = value
|
|
else:
|
|
safe_ctx[key] = str(value)
|
|
safe_error["ctx"] = safe_ctx
|
|
sanitized.append(safe_error)
|
|
return sanitized
|
|
|
|
|
|
@app.exception_handler(RequestValidationError)
|
|
async def validation_exception_handler(request: Request, exc: RequestValidationError):
|
|
"""Log validation errors and return detailed response."""
|
|
errors = exc.errors()
|
|
logger.warning(
|
|
"Validation error for %s %s: %s",
|
|
request.method,
|
|
request.url.path,
|
|
errors,
|
|
)
|
|
safe_errors = _sanitize_validation_errors(errors)
|
|
return JSONResponse(
|
|
status_code=422,
|
|
content={"detail": safe_errors},
|
|
)
|
|
|
|
|
|
@app.on_event("startup")
|
|
async def on_startup():
|
|
logger.info("Starting up Headquarter API...")
|
|
|
|
# Initialize database (run migrations)
|
|
db_ready = await init_database()
|
|
if not db_ready:
|
|
logger.error("Database initialization failed. Shutting down.")
|
|
import sys
|
|
|
|
sys.exit(1)
|
|
|
|
# Seed built-in data
|
|
await seed_builtin_tool_types()
|
|
logger.info("Startup complete.")
|
|
|
|
|
|
app.include_router(health_router)
|
|
app.include_router(auth_router)
|
|
app.include_router(dashboard_router)
|
|
app.include_router(projects_router)
|
|
app.include_router(users_router)
|
|
app.include_router(ssh_keys_router)
|
|
app.include_router(git_repositories_router)
|
|
app.include_router(user_config_router)
|
|
app.include_router(tool_types_router)
|
|
app.include_router(config_profiles_router)
|
|
app.include_router(tool_instances_router)
|
|
app.include_router(instance_proxy_router)
|
|
app.include_router(terminal_router)
|
|
app.mount("/uploads", StaticFiles(directory="uploads"), name="uploads")
|