Task 11: Remove direct API calls from components

This commit is contained in:
2026-04-18 21:20:45 +02:00
parent 3f197b1ad7
commit 2105f8b435
21 changed files with 712 additions and 266 deletions

View File

@@ -0,0 +1,123 @@
/**
* 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 } 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<void>;
deleteJailLocalOverride: (name: string) => Promise<void>;
validateJailConfig: (name: string) => Promise<JailValidationResult>;
activateJail: (name: string, payload: ActivateJailRequest) => Promise<JailActivationResponse>;
createJailConfigFile: (payload: ConfFileCreateRequest) => Promise<void>;
}
/**
* Load inactive fail2ban jails and expose the admin actions used by the
* jail configuration tab.
*/
export function useJailAdmin(): UseJailAdminResult {
const [inactiveJails, setInactiveJails] = useState<InactiveJail[]>([]);
const [inactiveLoading, setInactiveLoading] = useState(false);
const [inactiveError, setInactiveError] = useState<string | null>(null);
const abortRef = useRef<AbortController | null>(null);
const refreshInactiveJails = useCallback((): void => {
abortRef.current?.abort();
const ctrl = new AbortController();
abortRef.current = ctrl;
setInactiveLoading(true);
setInactiveError(null);
fetchInactiveJails()
.then((resp) => {
if (!ctrl.signal.aborted) {
setInactiveJails(resp.jails);
}
})
.catch((err: unknown) => {
if (!ctrl.signal.aborted) {
handleFetchError(err, 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<void> => {
await deactivateJail(name);
},
[],
);
const handleDeleteLocalOverride = useCallback(
async (name: string): Promise<void> => {
await deleteJailLocalOverride(name);
},
[],
);
const handleValidateJailConfig = useCallback(
async (name: string): Promise<JailValidationResult> => {
return await validateJailConfig(name);
},
[],
);
const handleActivateJail = useCallback(
async (name: string, payload: ActivateJailRequest): Promise<JailActivationResponse> => {
return await activateJail(name, payload);
},
[],
);
const handleCreateJailConfigFile = useCallback(
async (payload: ConfFileCreateRequest): Promise<void> => {
await createJailConfigFile(payload);
},
[],
);
return {
inactiveJails,
inactiveLoading,
inactiveError,
refreshInactiveJails,
deactivateJail: handleDeactivateJail,
deleteJailLocalOverride: handleDeleteLocalOverride,
validateJailConfig: handleValidateJailConfig,
activateJail: handleActivateJail,
createJailConfigFile: handleCreateJailConfigFile,
};
}