Add better jail configuration: file CRUD, enable/disable, log paths

Task 4 (Better Jail Configuration) implementation:
- Add fail2ban_config_dir setting to app/config.py
- New file_config_service: list/view/edit/create jail.d, filter.d, action.d files
  with path-traversal prevention and 512 KB content size limit
- New file_config router: GET/PUT/POST endpoints for jail files, filter files,
  and action files; PUT .../enabled for toggle on/off
- Extend config_service with delete_log_path() and add_log_path()
- Add DELETE /api/config/jails/{name}/logpath and POST /api/config/jails/{name}/logpath
- Extend geo router with re-resolve endpoint; add geo_re_resolve background task
- Update blocklist_service with revised scheduling helpers
- Update Docker compose files with BANGUI_FAIL2BAN_CONFIG_DIR env var and
  rw volume mount for the fail2ban config directory
- Frontend: new Jail Files, Filters, Actions tabs in ConfigPage; file editor
  with accordion-per-file, editable textarea, save/create; add/delete log paths
- Frontend: types in types/config.ts; API calls in api/config.ts and api/endpoints.ts
- 63 new backend tests (test_file_config_service, test_file_config, test_geo_re_resolve)
- 6 new frontend tests in ConfigPageLogPath.test.tsx
- ruff, mypy --strict, tsc --noEmit, eslint: all clean; 617 backend tests pass
This commit is contained in:
2026-03-12 20:08:33 +01:00
parent 59464a1592
commit ea35695221
23 changed files with 2911 additions and 91 deletions

View File

@@ -2,12 +2,19 @@
* API functions for the configuration and server settings endpoints.
*/
import { get, post, put } from "./client";
import { del, get, post, put } from "./client";
import { ENDPOINTS } from "./endpoints";
import type {
AddLogPathRequest,
ConfFileContent,
ConfFileCreateRequest,
ConfFilesResponse,
ConfFileUpdateRequest,
GlobalConfig,
GlobalConfigUpdate,
JailConfigFileContent,
JailConfigFileEnabledUpdate,
JailConfigFilesResponse,
JailConfigListResponse,
JailConfigResponse,
JailConfigUpdate,
@@ -88,6 +95,15 @@ export async function addLogPath(
await post<undefined>(ENDPOINTS.configJailLogPath(jailName), req);
}
export async function deleteLogPath(
jailName: string,
logPath: string
): Promise<void> {
await del<undefined>(
`${ENDPOINTS.configJailLogPath(jailName)}?log_path=${encodeURIComponent(logPath)}`
);
}
// ---------------------------------------------------------------------------
// Log preview
// ---------------------------------------------------------------------------
@@ -139,3 +155,74 @@ export async function updateMapColorThresholds(
update,
);
}
// ---------------------------------------------------------------------------
// Jail config files (Task 4a)
// ---------------------------------------------------------------------------
export async function fetchJailConfigFiles(): Promise<JailConfigFilesResponse> {
return get<JailConfigFilesResponse>(ENDPOINTS.configJailFiles);
}
export async function fetchJailConfigFileContent(
filename: string
): Promise<JailConfigFileContent> {
return get<JailConfigFileContent>(ENDPOINTS.configJailFile(filename));
}
export async function setJailConfigFileEnabled(
filename: string,
update: JailConfigFileEnabledUpdate
): Promise<void> {
await put<undefined>(ENDPOINTS.configJailFileEnabled(filename), update);
}
// ---------------------------------------------------------------------------
// Filter files (Task 4d)
// ---------------------------------------------------------------------------
export async function fetchFilterFiles(): Promise<ConfFilesResponse> {
return get<ConfFilesResponse>(ENDPOINTS.configFilters);
}
export async function fetchFilterFile(name: string): Promise<ConfFileContent> {
return get<ConfFileContent>(ENDPOINTS.configFilter(name));
}
export async function updateFilterFile(
name: string,
req: ConfFileUpdateRequest
): Promise<void> {
await put<undefined>(ENDPOINTS.configFilter(name), req);
}
export async function createFilterFile(
req: ConfFileCreateRequest
): Promise<ConfFileContent> {
return post<ConfFileContent>(ENDPOINTS.configFilters, req);
}
// ---------------------------------------------------------------------------
// Action files (Task 4e)
// ---------------------------------------------------------------------------
export async function fetchActionFiles(): Promise<ConfFilesResponse> {
return get<ConfFilesResponse>(ENDPOINTS.configActions);
}
export async function fetchActionFile(name: string): Promise<ConfFileContent> {
return get<ConfFileContent>(ENDPOINTS.configAction(name));
}
export async function updateActionFile(
name: string,
req: ConfFileUpdateRequest
): Promise<void> {
await put<undefined>(ENDPOINTS.configAction(name), req);
}
export async function createActionFile(
req: ConfFileCreateRequest
): Promise<ConfFileContent> {
return post<ConfFileContent>(ENDPOINTS.configActions, req);
}