/** * React hook for managing inactive jail operations and configuration actions. */ import { useCallback, useEffect, useRef, useState } from "react"; import { activateJail, deactivateJail, deleteJailLocalOverride, fetchInactiveJails, validateJailConfig, createJailConfigFile, } from "../api/config"; import { handleFetchError, createStringErrorAdapter } from "../utils/fetchError"; import type { ActivateJailRequest, ConfFileCreateRequest, InactiveJail, JailActivationResponse, JailValidationResult, } from "../types/config"; export interface UseJailAdminResult { inactiveJails: InactiveJail[]; inactiveLoading: boolean; inactiveError: string | null; refreshInactiveJails: () => void; deactivateJail: (name: string) => Promise; deleteJailLocalOverride: (name: string) => Promise; validateJailConfig: (name: string) => Promise; activateJail: (name: string, payload: ActivateJailRequest) => Promise; createJailConfigFile: (payload: ConfFileCreateRequest) => Promise; } /** * Load inactive fail2ban jails and expose the admin actions used by the * jail configuration tab. */ export function useJailAdmin(): UseJailAdminResult { const [inactiveJails, setInactiveJails] = useState([]); const [inactiveLoading, setInactiveLoading] = useState(false); const [inactiveError, setInactiveError] = useState(null); const abortRef = useRef(null); const refreshInactiveJails = useCallback((): void => { abortRef.current?.abort(); const ctrl = new AbortController(); abortRef.current = ctrl; setInactiveLoading(true); setInactiveError(null); fetchInactiveJails(ctrl.signal) .then((resp) => { if (!ctrl.signal.aborted) { setInactiveJails(resp.items); } }) .catch((err: unknown) => { if (!ctrl.signal.aborted) { handleFetchError(err, createStringErrorAdapter(setInactiveError), "Failed to load inactive jails"); } }) .finally(() => { if (!ctrl.signal.aborted) { setInactiveLoading(false); } }); }, []); useEffect(() => { refreshInactiveJails(); return (): void => { abortRef.current?.abort(); }; }, [refreshInactiveJails]); const handleDeactivateJail = useCallback( async (name: string): Promise => { await deactivateJail(name); }, [], ); const handleDeleteLocalOverride = useCallback( async (name: string): Promise => { await deleteJailLocalOverride(name); }, [], ); const handleValidateJailConfig = useCallback( async (name: string): Promise => { return await validateJailConfig(name); }, [], ); const handleActivateJail = useCallback( async (name: string, payload: ActivateJailRequest): Promise => { return await activateJail(name, payload); }, [], ); const handleCreateJailConfigFile = useCallback( async (payload: ConfFileCreateRequest): Promise => { await createJailConfigFile(payload); }, [], ); return { inactiveJails, inactiveLoading, inactiveError, refreshInactiveJails, deactivateJail: handleDeactivateJail, deleteJailLocalOverride: handleDeleteLocalOverride, validateJailConfig: handleValidateJailConfig, activateJail: handleActivateJail, createJailConfigFile: handleCreateJailConfigFile, }; }