Refactor backend to use request-scoped SQLite connections
This commit is contained in:
@@ -23,7 +23,6 @@ if TYPE_CHECKING:
|
||||
from starlette.responses import Response as StarletteResponse
|
||||
|
||||
import aiohttp
|
||||
import aiosqlite
|
||||
import structlog
|
||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler # type: ignore[import-untyped]
|
||||
from fastapi import FastAPI, Request, status
|
||||
@@ -33,7 +32,7 @@ from starlette.middleware.base import BaseHTTPMiddleware
|
||||
|
||||
from app import __version__
|
||||
from app.config import Settings, get_settings
|
||||
from app.db import init_db
|
||||
from app.db import init_db, open_db
|
||||
from app.routers import (
|
||||
auth,
|
||||
bans,
|
||||
@@ -145,11 +144,19 @@ async def _lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
|
||||
# --- Application database ---
|
||||
db_path: Path = Path(settings.database_path)
|
||||
db_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
from app.services import geo_service # noqa: PLC0415
|
||||
|
||||
log.debug("database_directory_ensured", directory=str(db_path.parent))
|
||||
db: aiosqlite.Connection = await aiosqlite.connect(settings.database_path)
|
||||
db.row_factory = aiosqlite.Row
|
||||
await init_db(db)
|
||||
app.state.db = db
|
||||
db = await open_db(settings.database_path)
|
||||
try:
|
||||
await init_db(db)
|
||||
await geo_service.load_cache_from_db(db)
|
||||
unresolved_count = await geo_service.count_unresolved(db)
|
||||
finally:
|
||||
await db.close()
|
||||
|
||||
if unresolved_count > 0:
|
||||
log.warning("geo_cache_unresolved_ips", unresolved=unresolved_count)
|
||||
|
||||
# --- Shared HTTP client session ---
|
||||
http_session: aiohttp.ClientSession = aiohttp.ClientSession()
|
||||
@@ -159,12 +166,6 @@ async def _lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
|
||||
from app.services import geo_service # noqa: PLC0415
|
||||
|
||||
geo_service.init_geoip(settings.geoip_db_path)
|
||||
await geo_service.load_cache_from_db(db)
|
||||
|
||||
# Log unresolved geo entries so the operator can see the scope of the issue.
|
||||
unresolved_count = await geo_service.count_unresolved(db)
|
||||
if unresolved_count > 0:
|
||||
log.warning("geo_cache_unresolved_ips", unresolved=unresolved_count)
|
||||
|
||||
# --- Background task scheduler ---
|
||||
scheduler: AsyncIOScheduler = AsyncIOScheduler(timezone="UTC")
|
||||
@@ -328,9 +329,27 @@ class SetupRedirectMiddleware(BaseHTTPMiddleware):
|
||||
request.app.state, "_setup_complete_cached", False
|
||||
):
|
||||
from app.services import setup_service # noqa: PLC0415
|
||||
from app.db import open_db # noqa: PLC0415
|
||||
|
||||
db: aiosqlite.Connection | None = getattr(request.app.state, "db", None)
|
||||
if db is None or not await setup_service.is_setup_complete(db):
|
||||
db = getattr(request.app.state, "db", None)
|
||||
if db is None:
|
||||
settings = request.app.state.settings
|
||||
db = await open_db(settings.database_path)
|
||||
try:
|
||||
is_complete = await setup_service.is_setup_complete(db)
|
||||
except Exception:
|
||||
log.debug("setup_check_failed", reason="db_uninitialised_or_inaccessible")
|
||||
is_complete = False
|
||||
finally:
|
||||
await db.close()
|
||||
else:
|
||||
try:
|
||||
is_complete = await setup_service.is_setup_complete(db)
|
||||
except Exception:
|
||||
log.debug("setup_check_failed", reason="db_uninitialised_or_inaccessible")
|
||||
is_complete = False
|
||||
|
||||
if not is_complete:
|
||||
return RedirectResponse(
|
||||
url="/api/setup",
|
||||
status_code=status.HTTP_307_TEMPORARY_REDIRECT,
|
||||
|
||||
Reference in New Issue
Block a user