Task 7 complete: move config operational orchestration from routers into service/task layer

This commit is contained in:
2026-04-10 21:24:54 +02:00
parent 91e5792caf
commit 952469e667
5 changed files with 87 additions and 44 deletions

View File

@@ -16,7 +16,7 @@ import os
import re
import tempfile
from pathlib import Path
from typing import cast
from typing import TYPE_CHECKING, cast
import structlog
@@ -36,7 +36,17 @@ from app.models.config import (
JailValidationResult,
RollbackResponse,
)
from app.tasks.health_check import run_probe
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()
@@ -517,6 +527,34 @@ async def list_inactive_jails(
async def activate_jail(
app: FastAPI,
config_dir: str,
socket_path: str,
name: str,
req: ActivateJailRequest,
) -> JailActivationResponse:
"""Activate a jail and manage crash recovery state.
This wrapper records the activation timestamp, delegates the actual
file-based activation workflow to the lower-level implementation, and
updates the health-check cache immediately so the UI reflects the
current fail2ban state.
"""
activation_time = record_activation(app, name)
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(app)
return result
async def _activate_jail(
config_dir: str,
socket_path: str,
name: str,
@@ -782,6 +820,23 @@ async def _rollback_activation_async(
async def deactivate_jail(
app: FastAPI,
config_dir: str,
socket_path: str,
name: str,
) -> JailActivationResponse:
"""Deactivate a jail and update the health-check cache.
This wrapper disables the jail in the config, reloads fail2ban, and then
forces an immediate health probe so any cached dashboard status reflects
the current daemon state.
"""
result = await _deactivate_jail(config_dir, socket_path, name)
await run_probe(app)
return result
async def _deactivate_jail(
config_dir: str,
socket_path: str,
name: str,
@@ -918,6 +973,23 @@ async def validate_jail_config(
async def rollback_jail(
app: FastAPI,
config_dir: str,
socket_path: str,
name: str,
start_cmd_parts: list[str],
) -> RollbackResponse:
"""Rollback a jail and clear pending recovery state on success."""
result = await _rollback_jail(config_dir, socket_path, name, start_cmd_parts)
if result.fail2ban_running:
clear_pending_recovery(app)
clear_activation_record(app)
return result
async def _rollback_jail(
config_dir: str,
socket_path: str,
name: str,