Files
BanGUI/frontend/src/api/jails.ts
Lukas 4773ae1c7a Add mass unban: DELETE /api/bans/all clears all active bans
- Send fail2ban's `unban --all` command via new `unban_all_ips()` service
  function; returns the count of unbanned IPs
- Add `UnbanAllResponse` Pydantic model (message + count)
- Add `DELETE /api/bans/all` router endpoint; handles 502 on socket error
- Frontend: `bansAll` endpoint constant, `unbanAllBans()` API call,
  `UnbanAllResponse` type, `unbanAll` action in `useActiveBans` hook
- JailsPage: "Clear All Bans" button (visible when bans > 0) with a
  Fluent UI confirmation Dialog before executing the operation
- 7 new tests (3 service, 4 router); 440 total pass, 82% coverage
2026-03-07 21:16:49 +01:00

227 lines
6.9 KiB
TypeScript

/**
* Jails API module.
*
* Wraps all backend endpoints under `/api/jails`, `/api/bans`, and
* `/api/geo` that relate to jail management.
*/
import { del, get, post } from "./client";
import { ENDPOINTS } from "./endpoints";
import type {
ActiveBanListResponse,
IpLookupResponse,
JailCommandResponse,
JailDetailResponse,
JailListResponse,
UnbanAllResponse,
} from "../types/jail";
// ---------------------------------------------------------------------------
// Jail overview
// ---------------------------------------------------------------------------
/**
* Fetch the list of all fail2ban jails.
*
* @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);
}
/**
* Fetch full detail for a single jail.
*
* @param name - Jail name (e.g. `"sshd"`).
* @returns A {@link JailDetailResponse} with config, ignore list, and status.
* @throws {ApiError} On non-2xx responses (404 if the jail does not exist).
*/
export async function fetchJail(name: string): Promise<JailDetailResponse> {
return get<JailDetailResponse>(ENDPOINTS.jail(name));
}
// ---------------------------------------------------------------------------
// Jail controls
// ---------------------------------------------------------------------------
/**
* Start a stopped jail.
*
* @param name - Jail name.
* @returns A {@link JailCommandResponse} confirming the operation.
* @throws {ApiError} On non-2xx responses.
*/
export async function startJail(name: string): Promise<JailCommandResponse> {
return post<JailCommandResponse>(ENDPOINTS.jailStart(name), {});
}
/**
* Stop a running jail.
*
* @param name - Jail name.
* @returns A {@link JailCommandResponse} confirming the operation.
* @throws {ApiError} On non-2xx responses.
*/
export async function stopJail(name: string): Promise<JailCommandResponse> {
return post<JailCommandResponse>(ENDPOINTS.jailStop(name), {});
}
/**
* Toggle idle mode for a jail.
*
* @param name - Jail name.
* @param on - `true` to enable idle mode, `false` to disable.
* @returns A {@link JailCommandResponse} confirming the toggle.
* @throws {ApiError} On non-2xx responses.
*/
export async function setJailIdle(
name: string,
on: boolean,
): Promise<JailCommandResponse> {
return post<JailCommandResponse>(ENDPOINTS.jailIdle(name), on);
}
/**
* Reload configuration for a single jail.
*
* @param name - Jail name.
* @returns A {@link JailCommandResponse} confirming the reload.
* @throws {ApiError} On non-2xx responses.
*/
export async function reloadJail(name: string): Promise<JailCommandResponse> {
return post<JailCommandResponse>(ENDPOINTS.jailReload(name), {});
}
/**
* Reload configuration for **all** jails at once.
*
* @returns A {@link JailCommandResponse} confirming the operation.
* @throws {ApiError} On non-2xx responses.
*/
export async function reloadAllJails(): Promise<JailCommandResponse> {
return post<JailCommandResponse>(ENDPOINTS.jailsReloadAll, {});
}
// ---------------------------------------------------------------------------
// Ignore list
// ---------------------------------------------------------------------------
/**
* Return the ignore list for a jail.
*
* @param name - Jail name.
* @returns Array of IP addresses / CIDR networks on the ignore list.
* @throws {ApiError} On non-2xx responses.
*/
export async function fetchIgnoreList(name: string): Promise<string[]> {
return get<string[]>(ENDPOINTS.jailIgnoreIp(name));
}
/**
* Add an IP or CIDR network to a jail's ignore list.
*
* @param name - Jail name.
* @param ip - IP address or CIDR network to add.
* @returns A {@link JailCommandResponse} confirming the addition.
* @throws {ApiError} On non-2xx responses.
*/
export async function addIgnoreIp(
name: string,
ip: string,
): Promise<JailCommandResponse> {
return post<JailCommandResponse>(ENDPOINTS.jailIgnoreIp(name), { ip });
}
/**
* Remove an IP or CIDR network from a jail's ignore list.
*
* @param name - Jail name.
* @param ip - IP address or CIDR network to remove.
* @returns A {@link JailCommandResponse} confirming the removal.
* @throws {ApiError} On non-2xx responses.
*/
export async function delIgnoreIp(
name: string,
ip: string,
): Promise<JailCommandResponse> {
return del<JailCommandResponse>(ENDPOINTS.jailIgnoreIp(name), { ip });
}
// ---------------------------------------------------------------------------
// Ban / unban
// ---------------------------------------------------------------------------
/**
* Manually ban an IP address in a specific jail.
*
* @param jail - Jail name.
* @param ip - IP address to ban.
* @returns A {@link JailCommandResponse} confirming the ban.
* @throws {ApiError} On non-2xx responses.
*/
export async function banIp(
jail: string,
ip: string,
): Promise<JailCommandResponse> {
return post<JailCommandResponse>(ENDPOINTS.bans, { jail, ip });
}
/**
* Unban an IP address from a specific jail or all jails.
*
* @param ip - IP address to unban.
* @param jail - Target jail name, or `undefined` to unban from all jails.
* @param unbanAll - When `true`, remove the IP from every jail.
* @returns A {@link JailCommandResponse} confirming the unban.
* @throws {ApiError} On non-2xx responses.
*/
export async function unbanIp(
ip: string,
jail?: string,
unbanAll = false,
): Promise<JailCommandResponse> {
return del<JailCommandResponse>(ENDPOINTS.bans, { ip, jail, unban_all: unbanAll });
}
// ---------------------------------------------------------------------------
// Active bans
// ---------------------------------------------------------------------------
/**
* Fetch all currently active bans across every jail.
*
* @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);
}
/**
* Unban every currently banned IP across all jails in a single operation.
*
* Uses fail2ban's global `unban --all` command.
*
* @returns An {@link UnbanAllResponse} with the count of unbanned IPs.
* @throws {ApiError} On non-2xx responses.
*/
export async function unbanAllBans(): Promise<UnbanAllResponse> {
return del<UnbanAllResponse>(ENDPOINTS.bansAll);
}
// ---------------------------------------------------------------------------
// Geo / IP lookup
// ---------------------------------------------------------------------------
/**
* Look up ban status and geo-location for an IP address.
*
* @param ip - IP address to look up.
* @returns An {@link IpLookupResponse} with ban history and geo info.
* @throws {ApiError} On non-2xx responses (400 for invalid IP).
*/
export async function lookupIp(ip: string): Promise<IpLookupResponse> {
return get<IpLookupResponse>(ENDPOINTS.geoLookup(ip));
}