refactoring-backend #3

Merged
lukas.pupkalipinski merged 403 commits from refactoring-backend into main 2026-05-20 20:23:46 +02:00
14 changed files with 179 additions and 37 deletions
Showing only changes of commit d9550ae4aa - Show all commits

View File

@@ -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.
---

View File

@@ -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<LogPreviewResponse>(ENDPOINTS.configPreviewLog, req);
}
// ---------------------------------------------------------------------------
// Server settings
// ---------------------------------------------------------------------------
export async function fetchServerSettings(
): Promise<ServerSettingsResponse> {
return get<ServerSettingsResponse>(ENDPOINTS.serverSettings);
}
export async function updateServerSettings(
update: ServerSettingsUpdate
): Promise<void> {
await put<undefined>(ENDPOINTS.serverSettings, update);
}
export async function flushLogs(
): Promise<string> {
const resp = await post<{ message: string }>(
ENDPOINTS.serverFlushLogs,
undefined,
);
return resp.message;
}
// ---------------------------------------------------------------------------
// Map color thresholds
// ---------------------------------------------------------------------------

View File

@@ -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<JailConfigFilesResponse> {
return get<JailConfigFilesResponse>(ENDPOINTS.configJailFiles);
}
export async function createJailConfigFile(
req: ConfFileCreateRequest,
): Promise<ConfFileContent> {
return post<ConfFileContent>(ENDPOINTS.configJailFiles, req);
}
export async function fetchJailConfigFileContent(
filename: string,
): Promise<JailConfigFileContent> {
return get<JailConfigFileContent>(ENDPOINTS.configJailFile(filename));
}
export async function updateJailConfigFile(
filename: string,
req: ConfFileUpdateRequest,
): Promise<void> {
await put<undefined>(ENDPOINTS.configJailFile(filename), req);
}
export async function setJailConfigFileEnabled(
filename: string,
update: { enabled: boolean },
): Promise<void> {
await put<undefined>(ENDPOINTS.configJailFileEnabled(filename), update);
}
export async function fetchFilterFiles(): Promise<ConfFilesResponse> {
const result = await get<FilterListResponse>(ENDPOINTS.configFilters);
return {
files: result.filters.map((filter) => ({
name: filter.name,
filename: filter.filename,
})),
total: result.total,
};
}
export async function fetchFilterFile(
name: string,
): Promise<ConfFileContent> {
return get<ConfFileContent>(ENDPOINTS.configFilterRaw(name));
}
export async function updateFilterFile(
name: string,
req: ConfFileUpdateRequest,
): Promise<void> {
await put<undefined>(ENDPOINTS.configFilterRaw(name), req);
}
export async function createFilterFile(
req: ConfFileCreateRequest,
): Promise<ConfFileContent> {
return post<ConfFileContent>(ENDPOINTS.configFiltersRaw, req);
}
export async function fetchActionFiles(): Promise<ConfFilesResponse> {
const result = await get<ActionListResponse>(ENDPOINTS.configActions);
return {
files: result.actions.map((action) => ({
name: action.name,
filename: action.filename,
})),
total: result.total,
};
}
export async function fetchActionFile(
name: string,
): Promise<ConfFileContent> {
return get<ConfFileContent>(ENDPOINTS.configActionRaw(name));
}
export async function updateActionFile(
name: string,
req: ConfFileUpdateRequest,
): Promise<void> {
await put<undefined>(ENDPOINTS.configActionRaw(name), req);
}
export async function createActionFile(
req: ConfFileCreateRequest,
): Promise<ConfFileContent> {
return post<ConfFileContent>(ENDPOINTS.configActions, req);
}

View File

@@ -0,0 +1,7 @@
import { get } from "./client";
import { ENDPOINTS } from "./endpoints";
import type { HealthResponse } from "../types/server";
export async function fetchHealth(): Promise<HealthResponse> {
return get<HealthResponse>(ENDPOINTS.health);
}

View File

@@ -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<ServerSettingsResponse> {
return get<ServerSettingsResponse>(ENDPOINTS.serverSettings);
}
export async function updateServerSettings(
update: ServerSettingsUpdate,
): Promise<void> {
await put<undefined>(ENDPOINTS.serverSettings, update);
}
export async function flushLogs(): Promise<string> {
const resp = await post<{ message: string }>(
ENDPOINTS.serverFlushLogs,
undefined,
);
return resp.message;
}

View File

@@ -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 }),
}));

View File

@@ -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";

View File

@@ -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";

View File

@@ -20,7 +20,7 @@ import {
createFilterFile,
updateActionFile,
updateFilterFile,
} from "../../api/config";
} from "../../api/file_config";
import { JailFilesTab } from "./JailFilesTab";
import { ConfFilesTab } from "./ConfFilesTab";

View File

@@ -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";

View File

@@ -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";

View File

@@ -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(),
}));

View File

@@ -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,

View File

@@ -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";
}