Add AbortSignal support to API functions for request cancellation
Add optional signal?: AbortSignal parameter to all API GET functions so they can be cancelled when components unmount. This prevents state-update warnings and wasted resources. Changes: - frontend/src/api/history.ts: fetchHistory, fetchIpHistory - frontend/src/api/map.ts: fetchBansByCountry - frontend/src/api/jails.ts: fetchJails, fetchActiveBans - frontend/src/api/config.ts: fetchJailConfig, fetchInactiveJails, fetchJailConfigFiles, fetchFilterFiles (threads signal through fetchFilters), fetchFilterFile, fetchActionFiles, fetchActionFile - frontend/src/api/blocklist.ts: fetchImportLog, previewBlocklist Updated all calling hooks to pass the abort signal from their controllers: - useHistory, useIpHistory - useMapData - useActiveBans - useJails - useConfigActiveStatus (fetchJails and fetchJailConfigs) - useJailAdmin (fetchInactiveJails) - useJailConfigDetail (fetchJailConfig) - useImportLog (fetchImportLog) - useBlocklists (previewBlocklist with AbortController) Updated Docs/Web-Development.md to document the convention. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -50,8 +50,8 @@ export async function deleteBlocklist(id: number): Promise<void> {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Preview the contents of a blocklist source URL. */
|
||||
export async function previewBlocklist(id: number): Promise<PreviewResponse> {
|
||||
return get<PreviewResponse>(ENDPOINTS.blocklistPreview(id));
|
||||
export async function previewBlocklist(id: number, signal?: AbortSignal): Promise<PreviewResponse> {
|
||||
return get<PreviewResponse>(ENDPOINTS.blocklistPreview(id), signal);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -86,6 +86,7 @@ export async function fetchImportLog(
|
||||
page = 1,
|
||||
pageSize = 50,
|
||||
sourceId?: number,
|
||||
signal?: AbortSignal,
|
||||
): Promise<ImportLogListResponse> {
|
||||
const params = new URLSearchParams();
|
||||
params.set("page", String(page));
|
||||
@@ -93,5 +94,6 @@ export async function fetchImportLog(
|
||||
if (sourceId !== undefined) params.set("source_id", String(sourceId));
|
||||
return get<ImportLogListResponse>(
|
||||
`${ENDPOINTS.blocklistsLog}?${params.toString()}`,
|
||||
signal,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -57,9 +57,10 @@ signal?: AbortSignal,
|
||||
}
|
||||
|
||||
export async function fetchJailConfig(
|
||||
name: string
|
||||
name: string,
|
||||
signal?: AbortSignal,
|
||||
): Promise<JailConfigResponse> {
|
||||
return get<JailConfigResponse>(ENDPOINTS.configJail(name));
|
||||
return get<JailConfigResponse>(ENDPOINTS.configJail(name), signal);
|
||||
}
|
||||
|
||||
export async function updateJailConfig(
|
||||
@@ -161,8 +162,8 @@ export async function updateMapColorThresholds(
|
||||
// Jail config files (Task 4a)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export async function fetchJailConfigFiles(): Promise<JailConfigFilesResponse> {
|
||||
return get<JailConfigFilesResponse>(ENDPOINTS.configJailFiles);
|
||||
export async function fetchJailConfigFiles(signal?: AbortSignal): Promise<JailConfigFilesResponse> {
|
||||
return get<JailConfigFilesResponse>(ENDPOINTS.configJailFiles, signal);
|
||||
}
|
||||
|
||||
export async function createJailConfigFile(
|
||||
@@ -202,8 +203,8 @@ export async function setJailConfigFileEnabled(
|
||||
* returns active-status data) and maps the result down to the simpler
|
||||
* ``ConfFilesResponse`` shape expected by the raw-file editor and export tab.
|
||||
*/
|
||||
export async function fetchFilterFiles(): Promise<ConfFilesResponse> {
|
||||
const result = await fetchFilters();
|
||||
export async function fetchFilterFiles(signal?: AbortSignal): Promise<ConfFilesResponse> {
|
||||
const result = await fetchFilters(signal);
|
||||
return {
|
||||
files: result.filters.map((f) => ({ name: f.name, filename: f.filename })),
|
||||
total: result.total,
|
||||
@@ -211,8 +212,8 @@ export async function fetchFilterFiles(): Promise<ConfFilesResponse> {
|
||||
}
|
||||
|
||||
/** Fetch the raw content of a filter definition file for the raw editor. */
|
||||
export async function fetchFilterFile(name: string): Promise<ConfFileContent> {
|
||||
return get<ConfFileContent>(ENDPOINTS.configFilterRaw(name));
|
||||
export async function fetchFilterFile(name: string, signal?: AbortSignal): Promise<ConfFileContent> {
|
||||
return get<ConfFileContent>(ENDPOINTS.configFilterRaw(name), signal);
|
||||
}
|
||||
|
||||
/** Save raw content to a filter definition file (``PUT /filters/{name}/raw``). */
|
||||
@@ -234,12 +235,12 @@ export async function createFilterFile(
|
||||
// Action files (Task 4e)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export async function fetchActionFiles(): Promise<ConfFilesResponse> {
|
||||
return get<ConfFilesResponse>(ENDPOINTS.configActions);
|
||||
export async function fetchActionFiles(signal?: AbortSignal): Promise<ConfFilesResponse> {
|
||||
return get<ConfFilesResponse>(ENDPOINTS.configActions, signal);
|
||||
}
|
||||
|
||||
export async function fetchActionFile(name: string): Promise<ConfFileContent> {
|
||||
return get<ConfFileContent>(ENDPOINTS.configActionRaw(name));
|
||||
export async function fetchActionFile(name: string, signal?: AbortSignal): Promise<ConfFileContent> {
|
||||
return get<ConfFileContent>(ENDPOINTS.configActionRaw(name), signal);
|
||||
}
|
||||
|
||||
export async function updateActionFile(
|
||||
@@ -495,9 +496,8 @@ export async function updateParsedJailFile(
|
||||
// Inactive jails (Stage 1)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Fetch all inactive jails from config files. */
|
||||
export async function fetchInactiveJails(): Promise<InactiveJailListResponse> {
|
||||
return get<InactiveJailListResponse>(ENDPOINTS.configJailsInactive);
|
||||
export async function fetchInactiveJails(signal?: AbortSignal): Promise<InactiveJailListResponse> {
|
||||
return get<InactiveJailListResponse>(ENDPOINTS.configJailsInactive, signal);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,7 @@ import type {
|
||||
*/
|
||||
export async function fetchHistory(
|
||||
query: HistoryQuery = {},
|
||||
signal?: AbortSignal,
|
||||
): Promise<HistoryListResponse> {
|
||||
const params = new URLSearchParams();
|
||||
if (query.range) params.set("range", query.range);
|
||||
@@ -30,7 +31,7 @@ export async function fetchHistory(
|
||||
const url = qs
|
||||
? `${ENDPOINTS.history}?${qs}`
|
||||
: ENDPOINTS.history;
|
||||
return get<HistoryListResponse>(url);
|
||||
return get<HistoryListResponse>(url, signal);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,9 +39,9 @@ export async function fetchHistory(
|
||||
*
|
||||
* @returns null when the server returns 404 (no history for this IP).
|
||||
*/
|
||||
export async function fetchIpHistory(ip: string): Promise<IpDetailResponse | null> {
|
||||
export async function fetchIpHistory(ip: string, signal?: AbortSignal): Promise<IpDetailResponse | null> {
|
||||
try {
|
||||
return await get<IpDetailResponse>(ENDPOINTS.historyIp(ip));
|
||||
return await get<IpDetailResponse>(ENDPOINTS.historyIp(ip), signal);
|
||||
} catch (err: unknown) {
|
||||
if (
|
||||
typeof err === "object" &&
|
||||
|
||||
@@ -27,8 +27,8 @@ import type {
|
||||
* @returns A {@link JailListResponse} containing summary info for each jail.
|
||||
* @throws {ApiError} On non-2xx responses.
|
||||
*/
|
||||
export async function fetchJails(): Promise<JailListResponse> {
|
||||
return get<JailListResponse>(ENDPOINTS.jails);
|
||||
export async function fetchJails(signal?: AbortSignal): Promise<JailListResponse> {
|
||||
return get<JailListResponse>(ENDPOINTS.jails, signal);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,8 +213,8 @@ export async function unbanIp(
|
||||
* @returns An {@link ActiveBanListResponse} with geo-enriched entries.
|
||||
* @throws {ApiError} On non-2xx responses.
|
||||
*/
|
||||
export async function fetchActiveBans(): Promise<ActiveBanListResponse> {
|
||||
return get<ActiveBanListResponse>(ENDPOINTS.bansActive);
|
||||
export async function fetchActiveBans(signal?: AbortSignal): Promise<ActiveBanListResponse> {
|
||||
return get<ActiveBanListResponse>(ENDPOINTS.bansActive, signal);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,7 @@ export async function fetchBansByCountry(
|
||||
origin: BanOriginFilter = "all",
|
||||
source: "fail2ban" | "archive" = "fail2ban",
|
||||
countryCode?: string,
|
||||
signal?: AbortSignal,
|
||||
): Promise<BansByCountryResponse> {
|
||||
const params = new URLSearchParams({ range });
|
||||
if (origin !== "all") {
|
||||
@@ -30,5 +31,5 @@ export async function fetchBansByCountry(
|
||||
if (countryCode) {
|
||||
params.set("country_code", countryCode);
|
||||
}
|
||||
return get<BansByCountryResponse>(`${ENDPOINTS.dashboardBansByCountry}?${params.toString()}`);
|
||||
return get<BansByCountryResponse>(`${ENDPOINTS.dashboardBansByCountry}?${params.toString()}`, signal);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user