feat: frontend Actions Tab with structured API, assign/create/remove dialogs (Task 3.3)

- ActionsTab rewritten with master/detail layout (mirrors FiltersTab)
- New AssignActionDialog and CreateActionDialog components
- ActionConfig type extended with active, used_by_jails, source_file, has_local_override
- New API functions: fetchActions, fetchAction, updateAction, createAction, deleteAction, assignActionToJail, removeActionFromJail
- useActionConfig updated to use new structured endpoints
- index.ts barrel exports updated
This commit is contained in:
2026-03-13 19:21:58 +01:00
parent f7cc130432
commit 6e35c5d269
9 changed files with 861 additions and 77 deletions

View File

@@ -7,8 +7,12 @@ import { ENDPOINTS } from "./endpoints";
import type {
ActionConfig,
ActionConfigUpdate,
ActionCreateRequest,
ActionListResponse,
ActionUpdateRequest,
ActivateJailRequest,
AddLogPathRequest,
AssignActionRequest,
AssignFilterRequest,
ConfFileContent,
ConfFileCreateRequest,
@@ -386,6 +390,108 @@ export async function updateParsedAction(
await put<undefined>(ENDPOINTS.configActionParsed(name), update);
}
// ---------------------------------------------------------------------------
// Action discovery with active/inactive status (Task 3.1 / 3.2)
// ---------------------------------------------------------------------------
/**
* Fetch all actions from action.d/ with active/inactive status.
*
* Active actions (those referenced by running jails) are returned first,
* followed by inactive ones. Both groups are sorted alphabetically.
*
* @returns ActionListResponse with all discovered actions and status.
*/
export async function fetchActions(): Promise<ActionListResponse> {
return get<ActionListResponse>(ENDPOINTS.configActions);
}
/**
* Fetch full parsed detail for a single action.
*
* @param name - Action base name (e.g. "iptables" or "iptables.conf").
* @returns ActionConfig with active, used_by_jails, source_file populated.
*/
export async function fetchAction(name: string): Promise<ActionConfig> {
return get<ActionConfig>(ENDPOINTS.configAction(name));
}
/**
* Update an action's editable fields via the structured endpoint.
*
* Writes only the supplied fields to the ``.local`` override. Fields set
* to ``null`` are cleared; omitted fields are left unchanged.
*
* @param name - Action base name (e.g. ``"iptables"``)
* @param req - Partial update payload.
*/
export async function updateAction(
name: string,
req: ActionUpdateRequest
): Promise<void> {
await put<undefined>(ENDPOINTS.configAction(name), req);
}
/**
* Create a brand-new user-defined action in ``action.d/{name}.local``.
*
* @param req - Name and optional lifecycle commands.
* @returns The newly created ActionConfig.
*/
export async function createAction(
req: ActionCreateRequest
): Promise<ActionConfig> {
return post<ActionConfig>(ENDPOINTS.configActions, req);
}
/**
* Delete an action's ``.local`` override file.
*
* Only custom ``.local``-only actions can be deleted. Attempting to delete an
* action backed by a shipped ``.conf`` file returns 409.
*
* @param name - Action base name.
*/
export async function deleteAction(name: string): Promise<void> {
await del<undefined>(ENDPOINTS.configAction(name));
}
/**
* Assign an action to a jail by appending it to the jail's action list.
*
* @param jailName - Jail name.
* @param req - The action to assign with optional parameters.
* @param reload - When ``true``, trigger a fail2ban reload after writing.
*/
export async function assignActionToJail(
jailName: string,
req: AssignActionRequest,
reload = false
): Promise<void> {
const url = reload
? `${ENDPOINTS.configJailAction(jailName)}?reload=true`
: ENDPOINTS.configJailAction(jailName);
await post<undefined>(url, req);
}
/**
* Remove an action from a jail's action list.
*
* @param jailName - Jail name.
* @param actionName - Action base name to remove.
* @param reload - When ``true``, trigger a fail2ban reload after writing.
*/
export async function removeActionFromJail(
jailName: string,
actionName: string,
reload = false
): Promise<void> {
const url = reload
? `${ENDPOINTS.configJailActionName(jailName, actionName)}?reload=true`
: ENDPOINTS.configJailActionName(jailName, actionName);
await del<undefined>(url);
}
// ---------------------------------------------------------------------------
// Parsed jail file config (Task 6.1 / 6.2)
// ---------------------------------------------------------------------------