feat: Task 3 — invalid jail config recovery (pre-validation, crash detection, rollback)

- Backend: extend activate_jail() with pre-validation and 4-attempt post-reload
  health probe; add validate_jail_config() and rollback_jail() service functions
- Backend: new endpoints POST /api/config/jails/{name}/validate,
  GET /api/config/pending-recovery, POST /api/config/jails/{name}/rollback
- Backend: extend JailActivationResponse with fail2ban_running + validation_warnings;
  add JailValidationIssue, JailValidationResult, PendingRecovery, RollbackResponse models
- Backend: health_check task tracks last_activation and creates PendingRecovery
  record when fail2ban goes offline within 60 s of an activation
- Backend: add fail2ban_start_command setting (configurable start cmd for rollback)
- Frontend: ActivateJailDialog — pre-validation on open, crash-detected callback,
  extended spinner text during activation+verify
- Frontend: JailsTab — Validate Config button for inactive jails, validation
  result panels (blocking errors + advisory warnings)
- Frontend: RecoveryBanner component — polls pending-recovery, shows full-width
  alert with Disable & Restart / View Logs buttons
- Frontend: MainLayout — mount RecoveryBanner at layout level
- Tests: 19 new backend service tests (validate, rollback, filter/action parsing)
  + 6 health_check crash-detection tests + 11 router tests; 5 RecoveryBanner
  frontend tests; fix mock setup in existing activate_jail tests
This commit is contained in:
2026-03-14 14:13:07 +01:00
parent ab11ece001
commit 0966f347c4
17 changed files with 1862 additions and 26 deletions

View File

@@ -34,12 +34,15 @@ import type {
JailConfigListResponse,
JailConfigResponse,
JailConfigUpdate,
JailValidationResult,
LogPreviewRequest,
LogPreviewResponse,
MapColorThresholdsResponse,
MapColorThresholdsUpdate,
PendingRecovery,
RegexTestRequest,
RegexTestResponse,
RollbackResponse,
ServerSettingsResponse,
ServerSettingsUpdate,
JailFileConfig,
@@ -569,3 +572,37 @@ export async function fetchFail2BanLog(
export async function fetchServiceStatus(): Promise<ServiceStatusResponse> {
return get<ServiceStatusResponse>(ENDPOINTS.configServiceStatus);
}
// ---------------------------------------------------------------------------
// Jail config recovery (Task 3)
// ---------------------------------------------------------------------------
/**
* Run pre-activation validation on a jail's config.
*
* Checks that referenced filter/action files exist, that all regex patterns
* compile, and that log paths are accessible on the server.
*/
export async function validateJailConfig(
name: string,
): Promise<JailValidationResult> {
return post<JailValidationResult>(ENDPOINTS.configJailValidate(name), undefined);
}
/**
* Fetch the pending crash-recovery record, if any.
*
* Returns null when fail2ban is healthy and no recovery is pending.
*/
export async function fetchPendingRecovery(): Promise<PendingRecovery | null> {
return get<PendingRecovery | null>(ENDPOINTS.configPendingRecovery);
}
/**
* Rollback a bad jail — disables it and attempts to restart fail2ban.
*
* @param name - Name of the jail to disable.
*/
export async function rollbackJail(name: string): Promise<RollbackResponse> {
return post<RollbackResponse>(ENDPOINTS.configJailRollback(name), undefined);
}