Refactor: Move module-level mutable flags to JailServiceState
TASK-004: Replace module-level mutable runtime flags in service layer with injected state holder, eliminating hidden global state and improving testability and synchronization boundaries. Changes: - Create JailServiceState dataclass in app/utils/runtime_state.py to hold backend capability cache and synchronization lock - Add JailServiceState as a field in RuntimeState (with default_factory) - Remove module-level _backend_cmd_supported and _backend_cmd_lock from jail_service.py - Refactor _check_backend_cmd_supported() to accept state parameter - Inject JailServiceState into list_jails() and _fetch_jail_summary() via parameters - Add get_jail_service_state() dependency provider in app/dependencies.py - Add JailServiceStateDep type alias for router injection - Update jails router to receive and pass state to service functions - Update all tests to use jail_service_state fixture and pass state to functions - Remove duplicate _MAX_PAGE_SIZE constant definition - Document mutable state management in Backend-Development.md - Update Architecture.md to describe JailServiceState and state nesting pattern Benefits: - Eliminates global mutable state and associated race conditions - Makes state visible to callers (not hidden in module scope) - Enables test isolation (each test gets fresh state) - Prepares codebase for multi-worker deployments (state can be extracted to shared backend) - Synchronization boundaries are now explicit (state.get_backend_cmd_lock()) Compliance: - All tests pass (17 passed in TestListJails, TestGetJail, TestLockInitialization) - No ruff linting errors - Type-safe: JailServiceState properly typed with asyncio.Lock, bool | None Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -29,6 +29,7 @@ from app.dependencies import (
|
||||
Fail2BanSocketDep,
|
||||
GeoCacheDep,
|
||||
HttpSessionDep,
|
||||
JailServiceStateDep,
|
||||
)
|
||||
from app.models.ban import JailBannedIpsResponse
|
||||
from app.models.jail import (
|
||||
@@ -56,6 +57,7 @@ _NamePath = Annotated[str, Path(description="Jail name as configured in fail2ban
|
||||
async def get_jails(
|
||||
_auth: AuthDep,
|
||||
socket_path: Fail2BanSocketDep,
|
||||
state: JailServiceStateDep,
|
||||
) -> JailListResponse:
|
||||
"""Return a summary of every active fail2ban jail.
|
||||
|
||||
@@ -65,11 +67,13 @@ async def get_jails(
|
||||
|
||||
Args:
|
||||
_auth: Validated session — enforces authentication.
|
||||
socket_path: Path to the fail2ban Unix domain socket.
|
||||
state: The jail service state holder.
|
||||
|
||||
Returns:
|
||||
:class:`~app.models.jail.JailListResponse` with all active jails.
|
||||
"""
|
||||
return await jail_service.list_jails(socket_path)
|
||||
return await jail_service.list_jails(socket_path, state)
|
||||
|
||||
|
||||
@router.get(
|
||||
|
||||
Reference in New Issue
Block a user