This commit is contained in:
2026-04-06 19:49:53 +02:00
parent 5107ff10d7
commit f0ee466603
6 changed files with 121 additions and 29 deletions

View File

@@ -18,6 +18,9 @@ from app.config import Settings
from app.models.auth import Session
from app.utils.time_utils import utc_now
import aiohttp
from apscheduler.schedulers.asyncio import AsyncIOScheduler # type: ignore[import-untyped]
log: structlog.stdlib.BoundLogger = structlog.get_logger()
@@ -25,6 +28,8 @@ class AppState(Protocol):
"""Partial view of the FastAPI application state used by dependencies."""
settings: Settings
http_session: aiohttp.ClientSession
scheduler: AsyncIOScheduler
_COOKIE_NAME = "bangui_session"
@@ -106,6 +111,67 @@ async def get_settings(request: Request) -> Settings:
return state.settings
async def get_http_session(request: Request) -> aiohttp.ClientSession:
"""Provide the shared HTTP client session from application state.
Args:
request: The current FastAPI request.
Returns:
A shared :class:`aiohttp.ClientSession` managed by the lifespan.
Raises:
HTTPException: If the session is unavailable.
"""
state = cast("AppState", request.app.state)
http_session = getattr(state, "http_session", None)
if http_session is None:
log.error("http_session_unavailable")
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="HTTP session is not available.",
)
return http_session
async def get_scheduler(request: Request) -> AsyncIOScheduler:
"""Provide the shared scheduler from application state.
Args:
request: The current FastAPI request.
Returns:
The :class:`apscheduler.schedulers.asyncio.AsyncIOScheduler` instance.
Raises:
HTTPException: If the scheduler is unavailable.
"""
state = cast("AppState", request.app.state)
scheduler = getattr(state, "scheduler", None)
if scheduler is None:
log.error("scheduler_unavailable")
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="Scheduler is not available.",
)
return scheduler
async def get_fail2ban_socket(settings: Settings = Depends(get_settings)) -> str:
"""Provide the configured path to the fail2ban Unix domain socket."""
return settings.fail2ban_socket
async def get_fail2ban_config_dir(settings: Settings = Depends(get_settings)) -> str:
"""Provide the configured fail2ban configuration directory."""
return settings.fail2ban_config_dir
async def get_fail2ban_start_command(settings: Settings = Depends(get_settings)) -> str:
"""Provide the configured fail2ban start command."""
return settings.fail2ban_start_command
async def require_auth(
request: Request,
db: Annotated[aiosqlite.Connection, Depends(get_db)],
@@ -171,4 +237,9 @@ async def require_auth(
# Convenience type aliases for route signatures.
DbDep = Annotated[aiosqlite.Connection, Depends(get_db)]
SettingsDep = Annotated[Settings, Depends(get_settings)]
HttpSessionDep = Annotated[aiohttp.ClientSession, Depends(get_http_session)]
SchedulerDep = Annotated[AsyncIOScheduler, Depends(get_scheduler)]
Fail2BanSocketDep = Annotated[str, Depends(get_fail2ban_socket)]
Fail2BanConfigDirDep = Annotated[str, Depends(get_fail2ban_config_dir)]
Fail2BanStartCommandDep = Annotated[str, Depends(get_fail2ban_start_command)]
AuthDep = Annotated[Session, Depends(require_auth)]