Remove FastAPI dependency from jail config service signatures
This commit is contained in:
@@ -119,6 +119,8 @@ The inverted import creates a hidden circular-dependency risk: if `health_check`
|
|||||||
|
|
||||||
### TASK-04 — Remove `FastAPI` app instance from service signatures 🟠
|
### TASK-04 — Remove `FastAPI` app instance from service signatures 🟠
|
||||||
|
|
||||||
|
**Status:** Completed ✅
|
||||||
|
|
||||||
**Where:**
|
**Where:**
|
||||||
`backend/app/services/jail_config_service.py` — `activate_jail()` and `deactivate_jail()` function signatures accept `app: FastAPI` (guarded by `TYPE_CHECKING` on line 48, used at runtime on lines ~295, ~571).
|
`backend/app/services/jail_config_service.py` — `activate_jail()` and `deactivate_jail()` function signatures accept `app: FastAPI` (guarded by `TYPE_CHECKING` on line 48, used at runtime on lines ~295, ~571).
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,12 @@ from app.services import (
|
|||||||
jail_config_service,
|
jail_config_service,
|
||||||
)
|
)
|
||||||
from app.utils.fail2ban_client import Fail2BanConnectionError
|
from app.utils.fail2ban_client import Fail2BanConnectionError
|
||||||
|
from app.utils.runtime_state import (
|
||||||
|
clear_activation_record,
|
||||||
|
clear_pending_recovery,
|
||||||
|
create_pending_recovery,
|
||||||
|
record_activation,
|
||||||
|
)
|
||||||
|
|
||||||
router: APIRouter = APIRouter(prefix="/jails", tags=["Jail Config"])
|
router: APIRouter = APIRouter(prefix="/jails", tags=["Jail Config"])
|
||||||
|
|
||||||
@@ -376,8 +382,10 @@ async def activate_jail(
|
|||||||
"""
|
"""
|
||||||
req = body if body is not None else ActivateJailRequest()
|
req = body if body is not None else ActivateJailRequest()
|
||||||
|
|
||||||
|
activation_time = record_activation(app, name)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = await jail_config_service.activate_jail(app, config_dir, socket_path, name, req)
|
result = await jail_config_service.activate_jail(config_dir, socket_path, name, req)
|
||||||
except JailNameError as exc:
|
except JailNameError as exc:
|
||||||
raise _bad_request(str(exc)) from exc
|
raise _bad_request(str(exc)) from exc
|
||||||
except JailNotFoundInConfigError:
|
except JailNotFoundInConfigError:
|
||||||
@@ -406,7 +414,6 @@ async def activate_jail(
|
|||||||
summary="Deactivate an active jail",
|
summary="Deactivate an active jail",
|
||||||
)
|
)
|
||||||
async def deactivate_jail(
|
async def deactivate_jail(
|
||||||
app: AppDep,
|
|
||||||
_auth: AuthDep,
|
_auth: AuthDep,
|
||||||
config_dir: Fail2BanConfigDirDep,
|
config_dir: Fail2BanConfigDirDep,
|
||||||
socket_path: Fail2BanSocketDep,
|
socket_path: Fail2BanSocketDep,
|
||||||
@@ -418,7 +425,6 @@ async def deactivate_jail(
|
|||||||
full fail2ban reload so the jail stops immediately.
|
full fail2ban reload so the jail stops immediately.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
app: FastAPI application instance.
|
|
||||||
_auth: Validated session.
|
_auth: Validated session.
|
||||||
name: Name of the jail to deactivate.
|
name: Name of the jail to deactivate.
|
||||||
|
|
||||||
@@ -433,7 +439,7 @@ async def deactivate_jail(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = await jail_config_service.deactivate_jail(app, config_dir, socket_path, name)
|
result = await jail_config_service.deactivate_jail(config_dir, socket_path, name)
|
||||||
except JailNameError as exc:
|
except JailNameError as exc:
|
||||||
raise _bad_request(str(exc)) from exc
|
raise _bad_request(str(exc)) from exc
|
||||||
except JailNotFoundInConfigError:
|
except JailNotFoundInConfigError:
|
||||||
@@ -585,7 +591,7 @@ async def rollback_jail(
|
|||||||
start_cmd_parts: list[str] = start_cmd.split()
|
start_cmd_parts: list[str] = start_cmd.split()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = await jail_config_service.rollback_jail(app, config_dir, socket_path, name, start_cmd_parts)
|
result = await jail_config_service.rollback_jail(config_dir, socket_path, name, start_cmd_parts)
|
||||||
except JailNameError as exc:
|
except JailNameError as exc:
|
||||||
raise _bad_request(str(exc)) from exc
|
raise _bad_request(str(exc)) from exc
|
||||||
except ConfigWriteError as exc:
|
except ConfigWriteError as exc:
|
||||||
@@ -594,6 +600,10 @@ async def rollback_jail(
|
|||||||
detail=f"Failed to write config override: {exc}",
|
detail=f"Failed to write config override: {exc}",
|
||||||
) from exc
|
) from exc
|
||||||
|
|
||||||
|
if result.fail2ban_running:
|
||||||
|
clear_pending_recovery(app)
|
||||||
|
clear_activation_record(app)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import os
|
|||||||
import re
|
import re
|
||||||
import tempfile
|
import tempfile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TYPE_CHECKING, cast
|
from typing import cast
|
||||||
|
|
||||||
import structlog
|
import structlog
|
||||||
|
|
||||||
@@ -37,15 +37,6 @@ from app.models.config import (
|
|||||||
from app.services import health_service
|
from app.services import health_service
|
||||||
from app.utils.async_utils import run_blocking
|
from app.utils.async_utils import run_blocking
|
||||||
from app.utils.fail2ban_client import Fail2BanClient
|
from app.utils.fail2ban_client import Fail2BanClient
|
||||||
from app.utils.runtime_state import (
|
|
||||||
clear_activation_record,
|
|
||||||
clear_pending_recovery,
|
|
||||||
create_pending_recovery,
|
|
||||||
record_activation,
|
|
||||||
)
|
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
|
||||||
from fastapi import FastAPI
|
|
||||||
|
|
||||||
log: structlog.stdlib.BoundLogger = structlog.get_logger()
|
log: structlog.stdlib.BoundLogger = structlog.get_logger()
|
||||||
|
|
||||||
@@ -274,29 +265,18 @@ async def list_inactive_jails(
|
|||||||
|
|
||||||
|
|
||||||
async def activate_jail(
|
async def activate_jail(
|
||||||
app: FastAPI,
|
|
||||||
config_dir: str,
|
config_dir: str,
|
||||||
socket_path: str,
|
socket_path: str,
|
||||||
name: str,
|
name: str,
|
||||||
req: ActivateJailRequest,
|
req: ActivateJailRequest,
|
||||||
) -> JailActivationResponse:
|
) -> JailActivationResponse:
|
||||||
"""Activate a jail and manage crash recovery state.
|
"""Activate a jail and update the health-check cache.
|
||||||
|
|
||||||
This wrapper records the activation timestamp, delegates the actual
|
This wrapper delegates the file-based activation workflow to the
|
||||||
file-based activation workflow to the lower-level implementation, and
|
lower-level implementation and runs an immediate probe so the UI
|
||||||
updates the health-check cache immediately so the UI reflects the
|
reflects the current fail2ban state.
|
||||||
current fail2ban state.
|
|
||||||
"""
|
"""
|
||||||
activation_time = record_activation(app, name)
|
|
||||||
result = await _activate_jail(config_dir, socket_path, name, req)
|
result = await _activate_jail(config_dir, socket_path, name, req)
|
||||||
|
|
||||||
if not result.fail2ban_running:
|
|
||||||
create_pending_recovery(
|
|
||||||
app,
|
|
||||||
jail_name=name,
|
|
||||||
activated_at=activation_time,
|
|
||||||
)
|
|
||||||
|
|
||||||
await run_probe(socket_path)
|
await run_probe(socket_path)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@@ -561,7 +541,6 @@ async def _rollback_activation_async(
|
|||||||
|
|
||||||
|
|
||||||
async def deactivate_jail(
|
async def deactivate_jail(
|
||||||
app: FastAPI,
|
|
||||||
config_dir: str,
|
config_dir: str,
|
||||||
socket_path: str,
|
socket_path: str,
|
||||||
name: str,
|
name: str,
|
||||||
@@ -707,20 +686,17 @@ async def validate_jail_config(
|
|||||||
|
|
||||||
|
|
||||||
async def rollback_jail(
|
async def rollback_jail(
|
||||||
app: FastAPI,
|
|
||||||
config_dir: str,
|
config_dir: str,
|
||||||
socket_path: str,
|
socket_path: str,
|
||||||
name: str,
|
name: str,
|
||||||
start_cmd_parts: list[str],
|
start_cmd_parts: list[str],
|
||||||
) -> RollbackResponse:
|
) -> RollbackResponse:
|
||||||
"""Rollback a jail and clear pending recovery state on success."""
|
"""Rollback a jail and return the result.
|
||||||
result = await _rollback_jail(config_dir, socket_path, name, start_cmd_parts)
|
|
||||||
|
|
||||||
if result.fail2ban_running:
|
The caller is responsible for clearing pending recovery state when the
|
||||||
clear_pending_recovery(app)
|
operation succeeds.
|
||||||
clear_activation_record(app)
|
"""
|
||||||
|
return await _rollback_jail(config_dir, socket_path, name, start_cmd_parts)
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
async def _rollback_jail(
|
async def _rollback_jail(
|
||||||
|
|||||||
@@ -807,7 +807,7 @@ class TestActivateJail:
|
|||||||
|
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
# Verify the override values were passed to the service
|
# Verify the override values were passed to the service
|
||||||
called_req = mock_activate.call_args.args[4]
|
called_req = mock_activate.call_args.args[3]
|
||||||
assert called_req.bantime == "1h"
|
assert called_req.bantime == "1h"
|
||||||
assert called_req.maxretry == 3
|
assert called_req.maxretry == 3
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user