From 1d50bc1a73b8377c01b184a287be34c5f4c4bc00 Mon Sep 17 00:00:00 2001 From: Lukas Date: Thu, 23 Apr 2026 08:02:24 +0200 Subject: [PATCH] fix: add validation error handling to InactiveJailDetail - Add validationError state to show network/API failures to user - Use handleFetchError to properly handle auth errors (suppress generic error banner, trigger session-expiry flow) - Clear validationError when user clicks Validate again - Ensure error MessageBar renders instead of success banner when validation fails - Fix InactiveJailDetail onValidate to return Promise as expected by prop type - Fix useJailConfigs test to use correct JailConfig interface Fixes TASK-BUG-05: prevents silent validation failures where user cannot distinguish between clean 'no issues' result and server error. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- frontend/src/components/config/JailsTab.tsx | 20 ++++++++++-- .../hooks/__tests__/useJailConfigs.test.ts | 32 +++++++------------ 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/frontend/src/components/config/JailsTab.tsx b/frontend/src/components/config/JailsTab.tsx index 1aff34c..0828d30 100644 --- a/frontend/src/components/config/JailsTab.tsx +++ b/frontend/src/components/config/JailsTab.tsx @@ -32,6 +32,7 @@ import { Play24Regular, } from "@fluentui/react-icons"; import { ApiError } from "../../api/client"; +import { handleFetchError } from "../../utils/fetchError"; import type { AddLogPathRequest, BackendType, @@ -651,13 +652,17 @@ function InactiveJailDetail({ const styles = useConfigStyles(); const [validating, setValidating] = useState(false); const [validationResult, setValidationResult] = useState(null); + const [validationError, setValidationError] = useState(null); const handleValidate = useCallback((): void => { setValidating(true); setValidationResult(null); + setValidationError(null); onValidate() .then((result) => { setValidationResult(result); }) - .catch(() => { /* validation call failed — ignore */ }) + .catch((err: unknown) => { + handleFetchError(err, setValidationError, "Validation request failed."); + }) .finally(() => { setValidating(false); }); }, [onValidate]); @@ -700,8 +705,17 @@ function InactiveJailDetail({ + {/* Validation error panel */} + {validationError !== null && ( +
+ + {validationError} + +
+ )} + {/* Validation result panel */} - {validationResult !== null && ( + {validationResult !== null && validationError === null && (
{blockingIssues.length === 0 && advisoryIssues.length === 0 ? ( @@ -924,7 +938,7 @@ export function JailsTab({ initialJail }: JailsTabProps): React.JSX.Element { ? (): void => { void handleDeactivateInactive(selectedInactiveJail.name); } : undefined } - onValidate={() => { void validateJailConfig(selectedInactiveJail.name); }} + onValidate={async () => validateJailConfig(selectedInactiveJail.name)} /> ) : null} diff --git a/frontend/src/hooks/__tests__/useJailConfigs.test.ts b/frontend/src/hooks/__tests__/useJailConfigs.test.ts index 93f98fb..646516e 100644 --- a/frontend/src/hooks/__tests__/useJailConfigs.test.ts +++ b/frontend/src/hooks/__tests__/useJailConfigs.test.ts @@ -15,27 +15,19 @@ describe("useJailConfigs", () => { jails: [ { name: "sshd", - enabled: true, - active: true, + ban_time: 600, + find_time: 600, + max_retry: 5, + fail_regex: [], + ignore_regex: [], + log_paths: [], + date_pattern: null, + log_encoding: "auto", backend: "systemd", - filename: "/etc/fail2ban/jail.d/sshd.conf", - source_file: "/etc/fail2ban/jail.d/sshd.conf", - last_activation: "2024-01-01T00:00:00Z", - bantime: 600, - findtime: 600, - maxretry: 5, - action: [], - logpath: [], - port: [], - ignoreself: false, - filter: "sshd", - destemail: "", - sendername: "", - action_d_files: [], - ignoreip: [], - ignorecommand: "", - banaction: "", - banaction_allports: false, + use_dns: "warn", + prefregex: "", + actions: [], + bantime_escalation: null, }, ], total: 1,