diff --git a/Docs/Tasks.md b/Docs/Tasks.md index 210b525..449a0ef 100644 --- a/Docs/Tasks.md +++ b/Docs/Tasks.md @@ -144,6 +144,8 @@ Reference: `Docs/Refactoring.md` for full analysis of each issue. **Docs changes needed:** Update `Docs/Architekture.md` section 2.1 (Project Structure) and section 2.2 (Module Purposes) to reflect any new modules created. +**Status:** Completed. + **Why this is needed:** Large files slow down code review, increase merge conflict probability, and make it harder to find relevant code. Proactively identifying split points before the files grow further ensures the architecture stays navigable. This is a maintenance concern, not a correctness issue. --- @@ -162,6 +164,8 @@ Reference: `Docs/Refactoring.md` for full analysis of each issue. **Docs changes needed:** `Docs/Architekture.md` section 3.2 (API Layer table) already lists the target modules; no text changes needed beyond verifying the table matches once the split is done. +**Status:** Completed. + **Why this is needed:** `api/config.ts` currently serves five conceptually unrelated domains. Developers looking for server settings functions or health check calls have no obvious place to find them, and the file will keep growing as new endpoints are added. Splitting it into focused modules makes the API layer match the documented architecture and makes new additions predictable. --- diff --git a/frontend/src/api/config.ts b/frontend/src/api/config.ts index 2b7ef23..2845da0 100644 --- a/frontend/src/api/config.ts +++ b/frontend/src/api/config.ts @@ -1,5 +1,5 @@ /** - * API functions for the configuration and server settings endpoints. + * API functions for the configuration endpoints. */ import { del, get, post, put } from "./client"; @@ -41,8 +41,6 @@ import type { MapColorThresholdsUpdate, RegexTestRequest, RegexTestResponse, - ServerSettingsResponse, - ServerSettingsUpdate, JailFileConfig, JailFileConfigUpdate, ServiceStatusResponse, @@ -139,30 +137,6 @@ export async function previewLog( 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 // --------------------------------------------------------------------------- diff --git a/frontend/src/api/file_config.ts b/frontend/src/api/file_config.ts new file mode 100644 index 0000000..c971aed --- /dev/null +++ b/frontend/src/api/file_config.ts @@ -0,0 +1,102 @@ +import { get, post, put } from "./client"; +import { ENDPOINTS } from "./endpoints"; +import type { + ActionListResponse, + ConfFileContent, + ConfFileCreateRequest, + ConfFileUpdateRequest, + ConfFilesResponse, + FilterListResponse, + JailConfigFileContent, + JailConfigFilesResponse, +} from "../types/config"; + +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: { enabled: boolean }, +): Promise { + await put(ENDPOINTS.configJailFileEnabled(filename), update); +} + +export async function fetchFilterFiles(): Promise { + const result = await get(ENDPOINTS.configFilters); + return { + files: result.filters.map((filter) => ({ + name: filter.name, + filename: filter.filename, + })), + total: result.total, + }; +} + +export async function fetchFilterFile( + name: string, +): Promise { + return get(ENDPOINTS.configFilterRaw(name)); +} + +export async function updateFilterFile( + name: string, + req: ConfFileUpdateRequest, +): Promise { + await put(ENDPOINTS.configFilterRaw(name), req); +} + +export async function createFilterFile( + req: ConfFileCreateRequest, +): Promise { + return post(ENDPOINTS.configFiltersRaw, req); +} + +export async function fetchActionFiles(): Promise { + const result = await get(ENDPOINTS.configActions); + return { + files: result.actions.map((action) => ({ + name: action.name, + filename: action.filename, + })), + total: result.total, + }; +} + +export async function fetchActionFile( + name: string, +): Promise { + return get(ENDPOINTS.configActionRaw(name)); +} + +export async function updateActionFile( + name: string, + req: ConfFileUpdateRequest, +): Promise { + await put(ENDPOINTS.configActionRaw(name), req); +} + +export async function createActionFile( + req: ConfFileCreateRequest, +): Promise { + return post(ENDPOINTS.configActions, req); +} diff --git a/frontend/src/api/health.ts b/frontend/src/api/health.ts new file mode 100644 index 0000000..bf28a23 --- /dev/null +++ b/frontend/src/api/health.ts @@ -0,0 +1,7 @@ +import { get } from "./client"; +import { ENDPOINTS } from "./endpoints"; +import type { HealthResponse } from "../types/server"; + +export async function fetchHealth(): Promise { + return get(ENDPOINTS.health); +} diff --git a/frontend/src/api/server.ts b/frontend/src/api/server.ts new file mode 100644 index 0000000..d8f63e2 --- /dev/null +++ b/frontend/src/api/server.ts @@ -0,0 +1,24 @@ +import { get, post, put } from "./client"; +import { ENDPOINTS } from "./endpoints"; +import type { + ServerSettingsResponse, + ServerSettingsUpdate, +} from "../types/config"; + +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; +} diff --git a/frontend/src/components/__tests__/ConfigPageLogPath.test.tsx b/frontend/src/components/__tests__/ConfigPageLogPath.test.tsx index 99d95f2..932cffe 100644 --- a/frontend/src/components/__tests__/ConfigPageLogPath.test.tsx +++ b/frontend/src/components/__tests__/ConfigPageLogPath.test.tsx @@ -118,6 +118,26 @@ vi.mock("../../api/config", () => ({ updateParsedJailFile: vi.fn(), })); +vi.mock("../../api/file_config", () => ({ + fetchJailConfigFiles: mockFetchJailConfigFiles, + createJailConfigFile: vi.fn().mockResolvedValue({ + name: "new-jail", + filename: "new-jail.conf", + content: "# new-jail\n", + }), + fetchJailConfigFileContent: vi.fn(), + updateJailConfigFile: mockUpdateJailConfigFile, + setJailConfigFileEnabled: mockSetJailConfigFileEnabled, + fetchFilterFiles: mockFetchFilterFiles, + fetchFilterFile: vi.fn(), + updateFilterFile: vi.fn(), + createFilterFile: vi.fn(), + fetchActionFiles: mockFetchActionFiles, + fetchActionFile: vi.fn(), + updateActionFile: vi.fn(), + createActionFile: vi.fn(), +})); + vi.mock("../../api/jails", () => ({ fetchJails: vi.fn().mockResolvedValue({ jails: [], total: 0 }), })); diff --git a/frontend/src/components/config/ActionsTab.tsx b/frontend/src/components/config/ActionsTab.tsx index 749fd8b..297d0bf 100644 --- a/frontend/src/components/config/ActionsTab.tsx +++ b/frontend/src/components/config/ActionsTab.tsx @@ -24,10 +24,9 @@ import { import { Add24Regular, Delete24Regular, LinkEdit24Regular } from "@fluentui/react-icons"; import { fetchActionFile, - fetchActions, - removeActionFromJail, updateActionFile, -} from "../../api/config"; +} from "../../api/file_config"; +import { fetchActions, removeActionFromJail } from "../../api/config"; import type { ActionConfig, ConfFileUpdateRequest } from "../../types/config"; import { ActionForm } from "./ActionForm"; import { AssignActionDialog } from "./AssignActionDialog"; diff --git a/frontend/src/components/config/CreateJailDialog.tsx b/frontend/src/components/config/CreateJailDialog.tsx index 4849b37..455a8b0 100644 --- a/frontend/src/components/config/CreateJailDialog.tsx +++ b/frontend/src/components/config/CreateJailDialog.tsx @@ -22,7 +22,7 @@ import { Text, tokens, } from "@fluentui/react-components"; -import { createJailConfigFile } from "../../api/config"; +import { createJailConfigFile } from "../../api/file_config"; import type { ConfFileCreateRequest } from "../../types/config"; import { ApiError } from "../../api/client"; diff --git a/frontend/src/components/config/ExportTab.tsx b/frontend/src/components/config/ExportTab.tsx index 535e67d..f8a836d 100644 --- a/frontend/src/components/config/ExportTab.tsx +++ b/frontend/src/components/config/ExportTab.tsx @@ -20,7 +20,7 @@ import { createFilterFile, updateActionFile, updateFilterFile, -} from "../../api/config"; +} from "../../api/file_config"; import { JailFilesTab } from "./JailFilesTab"; import { ConfFilesTab } from "./ConfFilesTab"; diff --git a/frontend/src/components/config/FiltersTab.tsx b/frontend/src/components/config/FiltersTab.tsx index 6be4396..4316853 100644 --- a/frontend/src/components/config/FiltersTab.tsx +++ b/frontend/src/components/config/FiltersTab.tsx @@ -22,7 +22,8 @@ import { tokens, } from "@fluentui/react-components"; import { Add24Regular, LinkEdit24Regular } from "@fluentui/react-icons"; -import { fetchFilterFile, fetchFilters, updateFilterFile } from "../../api/config"; +import { fetchFilterFile, updateFilterFile } from "../../api/file_config"; +import { fetchFilters } from "../../api/config"; import type { ConfFileUpdateRequest, FilterConfig } from "../../types/config"; import { AssignFilterDialog } from "./AssignFilterDialog"; import { ConfigListDetail } from "./ConfigListDetail"; diff --git a/frontend/src/components/config/JailFilesTab.tsx b/frontend/src/components/config/JailFilesTab.tsx index 8c750be..58214a9 100644 --- a/frontend/src/components/config/JailFilesTab.tsx +++ b/frontend/src/components/config/JailFilesTab.tsx @@ -31,7 +31,7 @@ import { createJailConfigFile, fetchJailConfigFiles, setJailConfigFileEnabled, -} from "../../api/config"; +} from "../../api/file_config"; import type { JailConfigFile } from "../../types/config"; import { JailFileForm } from "./JailFileForm"; import { useConfigStyles } from "./configStyles"; diff --git a/frontend/src/components/config/__tests__/FiltersTab.test.tsx b/frontend/src/components/config/__tests__/FiltersTab.test.tsx index f7694b8..91e23bd 100644 --- a/frontend/src/components/config/__tests__/FiltersTab.test.tsx +++ b/frontend/src/components/config/__tests__/FiltersTab.test.tsx @@ -14,6 +14,9 @@ import type { FilterConfig, FilterListResponse } from "../../../types/config"; vi.mock("../../../api/config", () => ({ fetchFilters: vi.fn(), +})); + +vi.mock("../../../api/file_config", () => ({ fetchFilterFile: vi.fn(), updateFilterFile: vi.fn(), })); diff --git a/frontend/src/hooks/useConfig.ts b/frontend/src/hooks/useConfig.ts index f1fa12e..4890520 100644 --- a/frontend/src/hooks/useConfig.ts +++ b/frontend/src/hooks/useConfig.ts @@ -8,16 +8,18 @@ import { fetchGlobalConfig, fetchJailConfig, fetchJailConfigs, - fetchServerSettings, - flushLogs, previewLog, reloadConfig, restartFail2Ban, testRegex, updateGlobalConfig, updateJailConfig, - updateServerSettings, } from "../api/config"; +import { + fetchServerSettings, + flushLogs, + updateServerSettings, +} from "../api/server"; import { handleFetchError } from "../utils/fetchError"; import type { AddLogPathRequest, diff --git a/frontend/src/types/server.ts b/frontend/src/types/server.ts index 7c9525f..3ff076a 100644 --- a/frontend/src/types/server.ts +++ b/frontend/src/types/server.ts @@ -22,3 +22,9 @@ export interface ServerStatus { export interface ServerStatusResponse { status: ServerStatus; } + +/** Response shape for ``GET /api/health``. */ +export interface HealthResponse { + status: "ok"; + fail2ban: "online" | "offline"; +}