/** * API functions for the configuration and server settings endpoints. */ import { del, get, post, put } from "./client"; import { ENDPOINTS } from "./endpoints"; import type { ActionConfig, ActionConfigUpdate, ActionCreateRequest, ActionListResponse, ActionUpdateRequest, ActivateJailRequest, AddLogPathRequest, AssignActionRequest, AssignFilterRequest, ConfFileContent, ConfFileCreateRequest, ConfFilesResponse, ConfFileUpdateRequest, Fail2BanLogResponse, FilterConfig, FilterConfigUpdate, FilterCreateRequest, FilterListResponse, FilterUpdateRequest, GlobalConfig, GlobalConfigUpdate, InactiveJailListResponse, JailActivationResponse, JailConfigFileContent, JailConfigFileEnabledUpdate, JailConfigFilesResponse, JailConfigListResponse, JailConfigResponse, JailConfigUpdate, JailValidationResult, LogPreviewRequest, LogPreviewResponse, MapColorThresholdsResponse, MapColorThresholdsUpdate, RegexTestRequest, RegexTestResponse, ServerSettingsResponse, ServerSettingsUpdate, JailFileConfig, JailFileConfigUpdate, ServiceStatusResponse, } from "../types/config"; // --------------------------------------------------------------------------- // Jail configuration // --------------------------------------------------------------------------- export async function fetchJailConfigs( ): Promise { return get(ENDPOINTS.configJails); } export async function fetchJailConfig( name: string ): Promise { return get(ENDPOINTS.configJail(name)); } export async function updateJailConfig( name: string, update: JailConfigUpdate ): Promise { await put(ENDPOINTS.configJail(name), update); } // --------------------------------------------------------------------------- // Global configuration // --------------------------------------------------------------------------- export async function fetchGlobalConfig( ): Promise { return get(ENDPOINTS.configGlobal); } export async function updateGlobalConfig( update: GlobalConfigUpdate ): Promise { await put(ENDPOINTS.configGlobal, update); } // --------------------------------------------------------------------------- // Reload and Restart // --------------------------------------------------------------------------- export async function reloadConfig( ): Promise { await post(ENDPOINTS.configReload, undefined); } export async function restartFail2Ban( ): Promise { await post(ENDPOINTS.configRestart, undefined); } // --------------------------------------------------------------------------- // Regex tester // --------------------------------------------------------------------------- export async function testRegex( req: RegexTestRequest ): Promise { return post(ENDPOINTS.configRegexTest, req); } // --------------------------------------------------------------------------- // Log path management // --------------------------------------------------------------------------- export async function addLogPath( jailName: string, req: AddLogPathRequest ): Promise { await post(ENDPOINTS.configJailLogPath(jailName), req); } export async function deleteLogPath( jailName: string, logPath: string ): Promise { await del( `${ENDPOINTS.configJailLogPath(jailName)}?log_path=${encodeURIComponent(logPath)}` ); } // --------------------------------------------------------------------------- // Log preview // --------------------------------------------------------------------------- export async function previewLog( req: LogPreviewRequest ): Promise { return post(ENDPOINTS.configPreviewLog, req); } // --------------------------------------------------------------------------- // Server settings // --------------------------------------------------------------------------- export async function fetchServerSettings( ): Promise { return get(ENDPOINTS.serverSettings); } export async function updateServerSettings( update: ServerSettingsUpdate ): Promise { await put(ENDPOINTS.serverSettings, update); } export async function flushLogs( ): Promise { const resp = await post<{ message: string }>( ENDPOINTS.serverFlushLogs, undefined, ); return resp.message; } // --------------------------------------------------------------------------- // Map color thresholds // --------------------------------------------------------------------------- export async function fetchMapColorThresholds( ): Promise { return get(ENDPOINTS.configMapColorThresholds); } export async function updateMapColorThresholds( update: MapColorThresholdsUpdate ): Promise { return put( ENDPOINTS.configMapColorThresholds, update, ); } // --------------------------------------------------------------------------- // Jail config files (Task 4a) // --------------------------------------------------------------------------- export async function fetchJailConfigFiles(): Promise { return get(ENDPOINTS.configJailFiles); } export async function createJailConfigFile( req: ConfFileCreateRequest ): Promise { return post(ENDPOINTS.configJailFiles, req); } export async function fetchJailConfigFileContent( filename: string ): Promise { return get(ENDPOINTS.configJailFile(filename)); } export async function updateJailConfigFile( filename: string, req: ConfFileUpdateRequest ): Promise { await put(ENDPOINTS.configJailFile(filename), req); } export async function setJailConfigFileEnabled( filename: string, update: JailConfigFileEnabledUpdate ): Promise { await put(ENDPOINTS.configJailFileEnabled(filename), update); } // --------------------------------------------------------------------------- // Filter files (Task 4d) — raw file management // --------------------------------------------------------------------------- /** * Return a lightweight name/filename list of all filter files. * * Internally calls the enriched ``GET /config/filters`` endpoint (which also * 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 { const result = await fetchFilters(); return { files: result.filters.map((f) => ({ name: f.name, filename: f.filename })), total: result.total, }; } /** Fetch the raw content of a filter definition file for the raw editor. */ export async function fetchFilterFile(name: string): Promise { return get(ENDPOINTS.configFilterRaw(name)); } /** Save raw content to a filter definition file (``PUT /filters/{name}/raw``). */ export async function updateFilterFile( name: string, req: ConfFileUpdateRequest ): Promise { await put(ENDPOINTS.configFilterRaw(name), req); } /** Create a new raw filter file (``POST /filters/raw``). */ export async function createFilterFile( req: ConfFileCreateRequest ): Promise { return post(ENDPOINTS.configFiltersRaw, req); } // --------------------------------------------------------------------------- // Action files (Task 4e) // --------------------------------------------------------------------------- export async function fetchActionFiles(): Promise { return get(ENDPOINTS.configActions); } export async function fetchActionFile(name: string): Promise { return get(ENDPOINTS.configAction(name)); } export async function updateActionFile( name: string, req: ConfFileUpdateRequest ): Promise { await put(ENDPOINTS.configAction(name), req); } export async function createActionFile( req: ConfFileCreateRequest ): Promise { return post(ENDPOINTS.configActions, req); } // --------------------------------------------------------------------------- // Parsed filter config (Task 2.2 / legacy /parsed endpoint) // --------------------------------------------------------------------------- export async function fetchParsedFilter(name: string): Promise { return get(ENDPOINTS.configFilterParsed(name)); } export async function updateParsedFilter( name: string, update: FilterConfigUpdate ): Promise { await put(ENDPOINTS.configFilterParsed(name), update); } // --------------------------------------------------------------------------- // Filter structured update / create / delete (Task 2.3) // --------------------------------------------------------------------------- /** * Update a filter'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 - Filter base name (e.g. ``"sshd"``) * @param req - Partial update payload. */ export async function updateFilter( name: string, req: FilterUpdateRequest ): Promise { await put(ENDPOINTS.configFilter(name), req); } /** * Create a brand-new user-defined filter in ``filter.d/{name}.local``. * * @param req - Name and optional regex patterns. * @returns The newly created FilterConfig. */ export async function createFilter( req: FilterCreateRequest ): Promise { return post(ENDPOINTS.configFilters, req); } /** * Delete a filter's ``.local`` override file. * * Only custom ``.local``-only filters can be deleted. Attempting to delete a * filter that is backed by a shipped ``.conf`` file returns 409. * * @param name - Filter base name. */ export async function deleteFilter(name: string): Promise { await del(ENDPOINTS.configFilter(name)); } /** * Assign a filter to a jail by writing ``filter = {filter_name}`` to the * jail's ``.local`` config file. * * @param jailName - Jail name. * @param req - The filter to assign. * @param reload - When ``true``, trigger a fail2ban reload after writing. */ export async function assignFilterToJail( jailName: string, req: AssignFilterRequest, reload = false ): Promise { const url = reload ? `${ENDPOINTS.configJailFilter(jailName)}?reload=true` : ENDPOINTS.configJailFilter(jailName); await post(url, req); } // --------------------------------------------------------------------------- // Filter discovery with active/inactive status (Task 2.1) // --------------------------------------------------------------------------- /** * Fetch all filters from filter.d/ with active/inactive status. * * Active filters (those referenced by running jails) are returned first, * followed by inactive ones. Both groups are sorted alphabetically. * * @returns FilterListResponse with all discovered filters and status. */ export async function fetchFilters(): Promise { return get(ENDPOINTS.configFilters); } /** * Fetch full parsed detail for a single filter with active/inactive status. * * @param name - Filter base name (e.g. "sshd" or "sshd.conf"). * @returns FilterConfig with active, used_by_jails, source_file populated. */ export async function fetchFilter(name: string): Promise { return get(ENDPOINTS.configFilter(name)); } // --------------------------------------------------------------------------- // Parsed action config (Task 3.2) // --------------------------------------------------------------------------- export async function fetchParsedAction(name: string): Promise { return get(ENDPOINTS.configActionParsed(name)); } export async function updateParsedAction( name: string, update: ActionConfigUpdate ): Promise { await put(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 { return get(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 { return get(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 { await put(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 { return post(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 { await del(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 { const url = reload ? `${ENDPOINTS.configJailAction(jailName)}?reload=true` : ENDPOINTS.configJailAction(jailName); await post(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 { const url = reload ? `${ENDPOINTS.configJailActionName(jailName, actionName)}?reload=true` : ENDPOINTS.configJailActionName(jailName, actionName); await del(url); } // --------------------------------------------------------------------------- // Parsed jail file config (Task 6.1 / 6.2) // --------------------------------------------------------------------------- export async function fetchParsedJailFile(filename: string): Promise { return get(ENDPOINTS.configJailFileParsed(filename)); } export async function updateParsedJailFile( filename: string, update: JailFileConfigUpdate ): Promise { await put(ENDPOINTS.configJailFileParsed(filename), update); } // --------------------------------------------------------------------------- // Inactive jails (Stage 1) // --------------------------------------------------------------------------- /** Fetch all inactive jails from config files. */ export async function fetchInactiveJails(): Promise { return get(ENDPOINTS.configJailsInactive); } /** * Activate an inactive jail, optionally providing override values. * * @param name - The jail name. * @param overrides - Optional parameter overrides (bantime, findtime, etc.). */ export async function activateJail( name: string, overrides?: ActivateJailRequest ): Promise { return post( ENDPOINTS.configJailActivate(name), overrides ?? {} ); } /** Deactivate an active jail. */ export async function deactivateJail( name: string ): Promise { return post( ENDPOINTS.configJailDeactivate(name), undefined ); } /** * Delete the ``jail.d/{name}.local`` override file for an inactive jail. * * Only valid when the jail is **not** currently active. Use this to clean up * leftover ``.local`` files after a jail has been fully deactivated. * * @param name - The jail name. */ export async function deleteJailLocalOverride(name: string): Promise { await del(ENDPOINTS.configJailLocalOverride(name)); } // --------------------------------------------------------------------------- // fail2ban log viewer (Task 2) // --------------------------------------------------------------------------- /** * Fetch the tail of the fail2ban daemon log file. * * @param lines - Number of tail lines to return (1–2000, default 200). * @param filter - Optional plain-text substring; only matching lines returned. */ export async function fetchFail2BanLog( lines?: number, filter?: string, ): Promise { const params = new URLSearchParams(); if (lines !== undefined) params.set("lines", String(lines)); if (filter !== undefined && filter !== "") params.set("filter", filter); const query = params.toString() ? `?${params.toString()}` : ""; return get(`${ENDPOINTS.configFail2BanLog}${query}`); } /** Fetch fail2ban service health status with current log configuration. */ export async function fetchServiceStatus(): Promise { return get(ENDPOINTS.configServiceStatus); } // --------------------------------------------------------------------------- // Jail config recovery (Task 3) // --------------------------------------------------------------------------- /** * Run pre-activation validation on a jail's config. * * Checks that referenced filter/action files exist, that all regex patterns * compile, and that log paths are accessible on the server. */ export async function validateJailConfig( name: string, ): Promise { return post(ENDPOINTS.configJailValidate(name), undefined); }