Convert setup guard to startup-driven cache and update tests
This commit is contained in:
@@ -50,6 +50,10 @@ from app.routers import (
|
||||
from app.tasks import blocklist_import, geo_cache_flush, geo_re_resolve, health_check, history_sync
|
||||
from app.utils.fail2ban_client import Fail2BanConnectionError, Fail2BanProtocolError
|
||||
from app.utils.jail_config import ensure_jail_configs
|
||||
from app.utils.setup_state import (
|
||||
is_setup_complete_cached,
|
||||
set_setup_complete_cache,
|
||||
)
|
||||
|
||||
log: structlog.stdlib.BoundLogger = structlog.get_logger()
|
||||
|
||||
@@ -122,6 +126,11 @@ async def _lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
|
||||
await init_db(db)
|
||||
await geo_service.load_cache_from_db(db)
|
||||
unresolved_count = await geo_service.count_unresolved(db)
|
||||
from app.services import setup_service # noqa: PLC0415
|
||||
|
||||
setup_complete = await setup_service.is_setup_complete(db)
|
||||
set_setup_complete_cache(app, setup_complete)
|
||||
log.debug("setup_completion_cached", completed=setup_complete)
|
||||
finally:
|
||||
await db.close()
|
||||
|
||||
@@ -133,8 +142,6 @@ async def _lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
|
||||
app.state.http_session = http_session
|
||||
|
||||
# --- Pre-warm geo cache from the persistent store ---
|
||||
from app.services import geo_service # noqa: PLC0415
|
||||
|
||||
geo_service.init_geoip(settings.geoip_db_path)
|
||||
|
||||
# --- Background task scheduler ---
|
||||
@@ -292,39 +299,14 @@ class SetupRedirectMiddleware(BaseHTTPMiddleware):
|
||||
return await call_next(request)
|
||||
|
||||
# If setup is not complete, block all other API requests.
|
||||
# Fast path: setup completion is a one-way transition. Once it is
|
||||
# True it is cached on app.state so all subsequent requests skip the
|
||||
# DB query entirely. The flag is reset only when the app restarts.
|
||||
if path.startswith("/api") and not getattr(
|
||||
request.app.state, "_setup_complete_cached", False
|
||||
):
|
||||
from app.db import open_db # noqa: PLC0415
|
||||
from app.services import setup_service # noqa: PLC0415
|
||||
|
||||
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,
|
||||
)
|
||||
request.app.state._setup_complete_cached = True
|
||||
# The setup completion state is resolved at startup and stored in
|
||||
# ``app.state.setup_complete_cached`` so this middleware does not
|
||||
# perform any database queries during normal request handling.
|
||||
if path.startswith("/api") and not is_setup_complete_cached(request.app):
|
||||
return RedirectResponse(
|
||||
url="/api/setup",
|
||||
status_code=status.HTTP_307_TEMPORARY_REDIRECT,
|
||||
)
|
||||
|
||||
return await call_next(request)
|
||||
|
||||
@@ -360,6 +342,7 @@ def create_app(settings: Settings | None = None) -> FastAPI:
|
||||
|
||||
# Store settings on app.state so the lifespan handler can access them.
|
||||
app.state.settings = resolved_settings
|
||||
set_setup_complete_cache(app, False)
|
||||
|
||||
# --- CORS ---
|
||||
# In production the frontend is served by the same origin.
|
||||
|
||||
Reference in New Issue
Block a user