Files
calminer/main.py

118 lines
4.1 KiB
Python

import logging
from contextlib import asynccontextmanager
from typing import Awaitable, Callable
from fastapi import FastAPI, Request, Response
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
from config.settings import get_settings
from middleware.auth_session import AuthSessionMiddleware
from middleware.metrics import MetricsMiddleware
from middleware.validation import validate_json
from routes.auth import router as auth_router
from routes.dashboard import router as dashboard_router
from routes.calculations import router as calculations_router
from routes.imports import router as imports_router
from routes.exports import router as exports_router
from routes.projects import router as projects_router
from routes.reports import router as reports_router
from routes.scenarios import router as scenarios_router
from routes.ui import router as ui_router
from monitoring import router as monitoring_router
from services.bootstrap import bootstrap_admin, bootstrap_pricing_settings
from scripts.init_db import init_db as init_db_script
logger = logging.getLogger(__name__)
async def _bootstrap_startup() -> None:
settings = get_settings()
admin_settings = settings.admin_bootstrap_settings()
pricing_metadata = settings.pricing_metadata()
try:
try:
init_db_script()
except Exception:
logger.exception(
"DB initializer failed; continuing to bootstrap (non-fatal)")
role_result, admin_result = bootstrap_admin(settings=admin_settings)
pricing_result = bootstrap_pricing_settings(metadata=pricing_metadata)
logger.info(
"Admin bootstrap completed: roles=%s created=%s updated=%s rotated=%s assigned=%s",
role_result.ensured,
admin_result.created_user,
admin_result.updated_user,
admin_result.password_rotated,
admin_result.roles_granted,
)
try:
seed = pricing_result.seed
slug = getattr(seed.settings, "slug", None) if seed and getattr(
seed, "settings", None) else None
created = getattr(seed, "created", None)
updated_fields = getattr(seed, "updated_fields", None)
impurity_upserts = getattr(seed, "impurity_upserts", None)
logger.info(
"Pricing settings bootstrap completed: slug=%s created=%s updated_fields=%s impurity_upserts=%s projects_assigned=%s",
slug,
created,
updated_fields,
impurity_upserts,
pricing_result.projects_assigned,
)
except Exception:
logger.info(
"Pricing settings bootstrap completed (partial): projects_assigned=%s",
pricing_result.projects_assigned,
)
except Exception: # pragma: no cover - defensive logging
logger.exception(
"Failed to bootstrap administrator or pricing settings")
@asynccontextmanager
async def app_lifespan(_: FastAPI):
await _bootstrap_startup()
yield
app = FastAPI(lifespan=app_lifespan)
app.add_middleware(AuthSessionMiddleware)
app.add_middleware(MetricsMiddleware)
@app.middleware("http")
async def json_validation(
request: Request, call_next: Callable[[Request], Awaitable[Response]]
) -> Response:
return await validate_json(request, call_next)
@app.get("/health", summary="Container health probe")
async def health() -> dict[str, str]:
return {"status": "ok"}
@app.get("/favicon.ico", include_in_schema=False)
async def favicon() -> Response:
static_directory = "static"
favicon_img = "favicon.ico"
return FileResponse(f"{static_directory}/{favicon_img}")
app.include_router(dashboard_router)
app.include_router(calculations_router)
app.include_router(auth_router)
app.include_router(imports_router)
app.include_router(exports_router)
app.include_router(projects_router)
app.include_router(scenarios_router)
app.include_router(reports_router)
app.include_router(ui_router)
app.include_router(monitoring_router)
app.mount("/static", StaticFiles(directory="static"), name="static")