scholarr/app/main.py
2026-02-17 15:21:08 +01:00

70 lines
2.5 KiB
Python

from __future__ import annotations
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException
from starlette.middleware.sessions import SessionMiddleware
from app.api.errors import register_api_exception_handlers
from app.api.router import router as api_router
from app.api.runtime_deps import get_ingestion_service, get_scholar_source
from app.db.session import check_database
from app.db.session import close_engine
from app.http.middleware import RequestLoggingMiddleware, parse_skip_paths
from app.logging_config import configure_logging, parse_redact_fields
from app.security.csrf import CSRFMiddleware
from app.services.scheduler import SchedulerService
from app.settings import settings
configure_logging(
level=settings.log_level,
log_format=settings.log_format,
redact_fields=parse_redact_fields(settings.log_redact_fields),
include_uvicorn_access=settings.log_uvicorn_access,
)
scheduler_service = SchedulerService(
enabled=settings.scheduler_enabled,
tick_seconds=settings.scheduler_tick_seconds,
network_error_retries=settings.ingestion_network_error_retries,
retry_backoff_seconds=settings.ingestion_retry_backoff_seconds,
max_pages_per_scholar=settings.ingestion_max_pages_per_scholar,
page_size=settings.ingestion_page_size,
continuation_queue_enabled=settings.ingestion_continuation_queue_enabled,
continuation_base_delay_seconds=settings.ingestion_continuation_base_delay_seconds,
continuation_max_delay_seconds=settings.ingestion_continuation_max_delay_seconds,
continuation_max_attempts=settings.ingestion_continuation_max_attempts,
queue_batch_size=settings.scheduler_queue_batch_size,
)
@asynccontextmanager
async def lifespan(_: FastAPI):
await scheduler_service.start()
yield
await scheduler_service.stop()
await close_engine()
app = FastAPI(title=settings.app_name, lifespan=lifespan)
register_api_exception_handlers(app)
app.add_middleware(CSRFMiddleware)
app.add_middleware(
SessionMiddleware,
secret_key=settings.session_secret_key,
same_site="lax",
https_only=settings.session_cookie_secure,
)
app.add_middleware(
RequestLoggingMiddleware,
log_requests=settings.log_requests,
skip_paths=parse_skip_paths(settings.log_request_skip_paths),
)
app.include_router(api_router)
@app.get("/healthz")
async def healthz() -> dict[str, str]:
if await check_database():
return {"status": "ok"}
raise HTTPException(status_code=500, detail="database unavailable")