Add ignore-self toggle to Jail Detail page
Implements the missing UI control for POST /api/jails/{name}/ignoreself:
- Add jailIgnoreSelf endpoint constant to endpoints.ts
- Add toggleIgnoreSelf(name, on) API function to jails.ts
- Expose toggleIgnoreSelf action from useJailDetail hook
- Replace read-only 'ignore self' badge with a Fluent Switch in
IgnoreListSection to allow enabling/disabling the flag per jail
- Add 5 vitest tests for checked/unchecked state and toggle behaviour
This commit is contained in:
132
Docs/Tasks.md
132
Docs/Tasks.md
@@ -468,3 +468,135 @@ Write tests covering:
|
||||
.venv/bin/mypy backend/app/ --strict
|
||||
```
|
||||
Zero errors.
|
||||
|
||||
---
|
||||
|
||||
## Task 8 — Add "ignore self" toggle to Jail Detail page
|
||||
|
||||
**Status:** done
|
||||
|
||||
**Summary:** Added `jailIgnoreSelf` endpoint constant to `endpoints.ts`; added `toggleIgnoreSelf(name, on)` API function to `jails.ts`; extended `useJailDetail` return type and hook implementation to expose `toggleIgnoreSelf`; replaced the read-only "ignore self" badge in `IgnoreListSection` (`JailDetailPage.tsx`) with a Fluent UI `Switch` that calls the toggle action and surfaces any error in the existing `opError` message bar; added 5 new tests in `JailDetailIgnoreSelf.test.tsx` covering checked/unchecked rendering, toggle-on, toggle-off, and error display.
|
||||
|
||||
**Page:** `/jails/:name` — rendered by `frontend/src/pages/JailDetailPage.tsx`
|
||||
|
||||
### Problem description
|
||||
|
||||
`Features.md` §5 (Jail Management / IP Whitelist) requires: "Toggle the 'ignore self' option per jail, which automatically excludes the server's own IP addresses."
|
||||
|
||||
The backend already exposes `POST /api/jails/{name}/ignoreself` (accepts a JSON boolean `on`). The `useJailDetail` hook reads `ignore_self` from the `GET /api/jails/{name}` response and exposes it as `ignoreSelf: boolean`. However:
|
||||
|
||||
1. **No API wrapper** — `frontend/src/api/jails.ts` has no `toggleIgnoreSelf` function, and `frontend/src/api/endpoints.ts` has no `jailIgnoreSelf` entry.
|
||||
2. **No hook action** — `UseJailDetailResult` in `useJails.ts` does not expose a `toggleIgnoreSelf` helper.
|
||||
3. **Read-only UI** — `IgnoreListSection` in `JailDetailPage.tsx` shows an "ignore self" badge when enabled but has no control to change the setting.
|
||||
|
||||
As a result, users must use the fail2ban CLI to manage this flag even though the backend is ready.
|
||||
|
||||
### What to do
|
||||
|
||||
#### Part A — Add endpoint constant (frontend)
|
||||
|
||||
**File:** `frontend/src/api/endpoints.ts`
|
||||
|
||||
Inside the Jails block, after `jailIgnoreIp`, add:
|
||||
|
||||
```ts
|
||||
jailIgnoreSelf: (name: string): string => `/jails/${encodeURIComponent(name)}/ignoreself`,
|
||||
```
|
||||
|
||||
#### Part B — Add API wrapper function (frontend)
|
||||
|
||||
**File:** `frontend/src/api/jails.ts`
|
||||
|
||||
After the `delIgnoreIp` function in the "Ignore list" section, add:
|
||||
|
||||
```ts
|
||||
/**
|
||||
* Enable or disable the `ignoreself` flag for a jail.
|
||||
*
|
||||
* When enabled, fail2ban automatically adds the server's own IP addresses to
|
||||
* the ignore list so the host can never ban itself.
|
||||
*
|
||||
* @param name - Jail name.
|
||||
* @param on - `true` to enable, `false` to disable.
|
||||
* @returns A {@link JailCommandResponse} confirming the change.
|
||||
* @throws {ApiError} On non-2xx responses.
|
||||
*/
|
||||
export async function toggleIgnoreSelf(
|
||||
name: string,
|
||||
on: boolean,
|
||||
): Promise<JailCommandResponse> {
|
||||
return post<JailCommandResponse>(ENDPOINTS.jailIgnoreSelf(name), on);
|
||||
}
|
||||
```
|
||||
|
||||
#### Part C — Expose toggle action from hook (frontend)
|
||||
|
||||
**File:** `frontend/src/hooks/useJails.ts`
|
||||
|
||||
1. Import `toggleIgnoreSelf` at the top (alongside the other API imports).
|
||||
2. Add `toggleIgnoreSelf: (on: boolean) => Promise<void>` to the `UseJailDetailResult` interface with a JSDoc comment: `/** Enable or disable the ignoreself option for this jail. */`.
|
||||
3. Inside `useJailDetail`, add the implementation:
|
||||
|
||||
```ts
|
||||
const toggleIgnoreSelf = async (on: boolean): Promise<void> => {
|
||||
await toggleIgnoreSelfApi(name, on);
|
||||
load();
|
||||
};
|
||||
```
|
||||
|
||||
Alias the import as `toggleIgnoreSelfApi` to avoid shadowing the local function name.
|
||||
|
||||
4. Add the function to the returned object.
|
||||
|
||||
#### Part D — Add toggle control to UI (frontend)
|
||||
|
||||
**File:** `frontend/src/pages/JailDetailPage.tsx`
|
||||
|
||||
1. Accept `toggleIgnoreSelf: (on: boolean) => Promise<void>` in the `IgnoreListSectionProps` interface.
|
||||
2. Pass the function from `useJailDetail` down via the existing destructuring and JSX prop.
|
||||
3. Inside `IgnoreListSection`, render a `<Switch>` next to (or in place of) the read-only badge:
|
||||
|
||||
```tsx
|
||||
<Switch
|
||||
label="Ignore self (exclude this server's own IPs)"
|
||||
checked={ignoreSelf}
|
||||
onChange={(_e, data): void => {
|
||||
toggleIgnoreSelf(data.checked).catch((err: unknown) => {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
setOpError(msg);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
Import `Switch` from `"@fluentui/react-components"`. Remove the existing read-only badge (it is replaced by the labelled switch, which is self-explanatory). Keep the existing `opError` state and `<MessageBar>` for error display.
|
||||
|
||||
### Tests to add
|
||||
|
||||
**File:** `frontend/src/pages/__tests__/JailDetailIgnoreSelf.test.tsx` (new file)
|
||||
|
||||
Write tests that render the `IgnoreListSection` component (or the full `JailDetailPage` via a shallow-enough render) and cover:
|
||||
|
||||
1. **`test_ignore_self_switch_is_checked_when_ignore_self_true`** — when `ignoreSelf=true`, the switch is checked.
|
||||
2. **`test_ignore_self_switch_is_unchecked_when_ignore_self_false`** — when `ignoreSelf=false`, the switch is unchecked.
|
||||
3. **`test_toggling_switch_calls_toggle_ignore_self`** — clicking the switch calls `toggleIgnoreSelf` with `false` (when it was `true`).
|
||||
4. **`test_toggle_error_shows_message_bar`** — when `toggleIgnoreSelf` rejects, the error message bar is rendered.
|
||||
|
||||
### Verification
|
||||
|
||||
1. Run frontend type check and lint:
|
||||
```bash
|
||||
cd frontend && npx tsc --noEmit && npx eslint src/api/jails.ts src/api/endpoints.ts src/hooks/useJails.ts src/pages/JailDetailPage.tsx
|
||||
```
|
||||
Zero errors and zero warnings.
|
||||
|
||||
2. Run frontend tests:
|
||||
```bash
|
||||
cd frontend && npx vitest run src/pages/__tests__/JailDetailIgnoreSelf
|
||||
```
|
||||
All 4 new tests pass.
|
||||
|
||||
3. **Manual test with running server:**
|
||||
- Go to `/jails`, click a running jail.
|
||||
- On the Jail Detail page, scroll to "Ignore List (IP Whitelist)".
|
||||
- Toggle the "Ignore self" switch on and off — the switch should reflect the live state and the change should survive a page refresh.
|
||||
|
||||
Reference in New Issue
Block a user