refactor: improve backend type safety and import organization

- Add TYPE_CHECKING guards for runtime-expensive imports (aiohttp, aiosqlite)
- Reorganize imports to follow PEP 8 conventions
- Convert TypeAlias to modern PEP 695 type syntax (where appropriate)
- Use Sequence/Mapping from collections.abc for type hints (covariant)
- Replace string literals with cast() for improved type inference
- Fix casting of Fail2BanResponse and TypedDict patterns
- Add IpLookupResult TypedDict for precise return type annotation
- Reformat overlong lines for readability (120 char limit)
- Add asyncio_mode and filterwarnings to pytest config
- Update test fixtures with improved type hints

This improves mypy type checking and makes type relationships explicit.
This commit is contained in:
2026-03-20 13:44:14 +01:00
parent 6515164d53
commit 250bb1a2e5
30 changed files with 431 additions and 644 deletions

View File

@@ -44,8 +44,6 @@ import structlog
from fastapi import APIRouter, HTTPException, Path, Query, Request, status
from app.dependencies import AuthDep
log: structlog.stdlib.BoundLogger = structlog.get_logger()
from app.models.config import (
ActionConfig,
ActionCreateRequest,
@@ -104,6 +102,8 @@ from app.services.jail_service import JailOperationError
from app.tasks.health_check import _run_probe
from app.utils.fail2ban_client import Fail2BanConnectionError
log: structlog.stdlib.BoundLogger = structlog.get_logger()
router: APIRouter = APIRouter(prefix="/api/config", tags=["Config"])
# ---------------------------------------------------------------------------
@@ -428,9 +428,7 @@ async def restart_fail2ban(
await config_file_service.start_daemon(start_cmd_parts)
# Step 3: probe the socket until fail2ban is responsive or the budget expires.
fail2ban_running: bool = await config_file_service.wait_for_fail2ban(
socket_path, max_wait_seconds=10.0
)
fail2ban_running: bool = await config_file_service.wait_for_fail2ban(socket_path, max_wait_seconds=10.0)
if not fail2ban_running:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
@@ -604,9 +602,7 @@ async def get_map_color_thresholds(
"""
from app.services import setup_service
high, medium, low = await setup_service.get_map_color_thresholds(
request.app.state.db
)
high, medium, low = await setup_service.get_map_color_thresholds(request.app.state.db)
return MapColorThresholdsResponse(
threshold_high=high,
threshold_medium=medium,
@@ -696,9 +692,7 @@ async def activate_jail(
req = body if body is not None else ActivateJailRequest()
try:
result = await config_file_service.activate_jail(
config_dir, socket_path, name, req
)
result = await config_file_service.activate_jail(config_dir, socket_path, name, req)
except JailNameError as exc:
raise _bad_request(str(exc)) from exc
except JailNotFoundInConfigError:
@@ -831,9 +825,7 @@ async def delete_jail_local_override(
socket_path: str = request.app.state.settings.fail2ban_socket
try:
await config_file_service.delete_jail_local_override(
config_dir, socket_path, name
)
await config_file_service.delete_jail_local_override(config_dir, socket_path, name)
except JailNameError as exc:
raise _bad_request(str(exc)) from exc
except JailNotFoundInConfigError:
@@ -952,9 +944,7 @@ async def rollback_jail(
start_cmd_parts: list[str] = start_cmd.split()
try:
result = await config_file_service.rollback_jail(
config_dir, socket_path, name, start_cmd_parts
)
result = await config_file_service.rollback_jail(config_dir, socket_path, name, start_cmd_parts)
except JailNameError as exc:
raise _bad_request(str(exc)) from exc
except ConfigWriteError as exc:
@@ -1107,9 +1097,7 @@ async def update_filter(
config_dir: str = request.app.state.settings.fail2ban_config_dir
socket_path: str = request.app.state.settings.fail2ban_socket
try:
return await config_file_service.update_filter(
config_dir, socket_path, name, body, do_reload=reload
)
return await config_file_service.update_filter(config_dir, socket_path, name, body, do_reload=reload)
except FilterNameError as exc:
raise _bad_request(str(exc)) from exc
except FilterNotFoundError:
@@ -1159,9 +1147,7 @@ async def create_filter(
config_dir: str = request.app.state.settings.fail2ban_config_dir
socket_path: str = request.app.state.settings.fail2ban_socket
try:
return await config_file_service.create_filter(
config_dir, socket_path, body, do_reload=reload
)
return await config_file_service.create_filter(config_dir, socket_path, body, do_reload=reload)
except FilterNameError as exc:
raise _bad_request(str(exc)) from exc
except FilterAlreadyExistsError as exc:
@@ -1257,9 +1243,7 @@ async def assign_filter_to_jail(
config_dir: str = request.app.state.settings.fail2ban_config_dir
socket_path: str = request.app.state.settings.fail2ban_socket
try:
await config_file_service.assign_filter_to_jail(
config_dir, socket_path, name, body, do_reload=reload
)
await config_file_service.assign_filter_to_jail(config_dir, socket_path, name, body, do_reload=reload)
except (JailNameError, FilterNameError) as exc:
raise _bad_request(str(exc)) from exc
except JailNotFoundInConfigError:
@@ -1403,9 +1387,7 @@ async def update_action(
config_dir: str = request.app.state.settings.fail2ban_config_dir
socket_path: str = request.app.state.settings.fail2ban_socket
try:
return await config_file_service.update_action(
config_dir, socket_path, name, body, do_reload=reload
)
return await config_file_service.update_action(config_dir, socket_path, name, body, do_reload=reload)
except ActionNameError as exc:
raise _bad_request(str(exc)) from exc
except ActionNotFoundError:
@@ -1451,9 +1433,7 @@ async def create_action(
config_dir: str = request.app.state.settings.fail2ban_config_dir
socket_path: str = request.app.state.settings.fail2ban_socket
try:
return await config_file_service.create_action(
config_dir, socket_path, body, do_reload=reload
)
return await config_file_service.create_action(config_dir, socket_path, body, do_reload=reload)
except ActionNameError as exc:
raise _bad_request(str(exc)) from exc
except ActionAlreadyExistsError as exc:
@@ -1546,9 +1526,7 @@ async def assign_action_to_jail(
config_dir: str = request.app.state.settings.fail2ban_config_dir
socket_path: str = request.app.state.settings.fail2ban_socket
try:
await config_file_service.assign_action_to_jail(
config_dir, socket_path, name, body, do_reload=reload
)
await config_file_service.assign_action_to_jail(config_dir, socket_path, name, body, do_reload=reload)
except (JailNameError, ActionNameError) as exc:
raise _bad_request(str(exc)) from exc
except JailNotFoundInConfigError:
@@ -1597,9 +1575,7 @@ async def remove_action_from_jail(
config_dir: str = request.app.state.settings.fail2ban_config_dir
socket_path: str = request.app.state.settings.fail2ban_socket
try:
await config_file_service.remove_action_from_jail(
config_dir, socket_path, name, action_name, do_reload=reload
)
await config_file_service.remove_action_from_jail(config_dir, socket_path, name, action_name, do_reload=reload)
except (JailNameError, ActionNameError) as exc:
raise _bad_request(str(exc)) from exc
except JailNotFoundInConfigError:
@@ -1689,4 +1665,3 @@ async def get_service_status(
return await config_service.get_service_status(socket_path)
except Fail2BanConnectionError as exc:
raise _bad_gateway(exc) from exc