feat: implement dashboard ban overview (Stage 5)
- Add ban_service reading fail2ban SQLite DB via read-only aiosqlite - Add geo_service resolving IPs via ip-api.com with 10k in-memory cache - Add GET /api/dashboard/bans and GET /api/dashboard/accesses endpoints - Add TimeRange, DashboardBanItem, DashboardBanListResponse, AccessListItem, AccessListResponse models in models/ban.py - Build BanTable component (Fluent UI DataGrid) with bans/accesses modes, pagination, loading/error/empty states, and ban-count badges - Build useBans hook managing time-range and pagination state - Update DashboardPage: status bar + time-range toolbar + tab switcher - Add 37 new backend tests (ban service, geo service, dashboard router) - All 141 tests pass; ruff/mypy --strict/tsc --noEmit clean
This commit is contained in:
@@ -1,20 +1,68 @@
|
||||
/**
|
||||
* Dashboard API module.
|
||||
*
|
||||
* Wraps the `GET /api/dashboard/status` endpoint.
|
||||
* Wraps `GET /api/dashboard/status`, `GET /api/dashboard/bans`, and
|
||||
* `GET /api/dashboard/accesses`.
|
||||
*/
|
||||
|
||||
import { get } from "./client";
|
||||
import { ENDPOINTS } from "./endpoints";
|
||||
import type { AccessListResponse, DashboardBanListResponse, TimeRange } from "../types/ban";
|
||||
import type { ServerStatusResponse } from "../types/server";
|
||||
|
||||
/**
|
||||
* Fetch the cached fail2ban server status from the backend.
|
||||
*
|
||||
* @returns The server status response containing ``online``, ``version``,
|
||||
* ``active_jails``, ``total_bans``, and ``total_failures``.
|
||||
* @returns The server status response containing `online`, `version`,
|
||||
* `active_jails`, `total_bans`, and `total_failures`.
|
||||
* @throws {ApiError} When the server returns a non-2xx status.
|
||||
*/
|
||||
export async function fetchServerStatus(): Promise<ServerStatusResponse> {
|
||||
return get<ServerStatusResponse>(ENDPOINTS.dashboardStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a paginated ban list for the selected time window.
|
||||
*
|
||||
* @param range - Time-range preset: `"24h"`, `"7d"`, `"30d"`, or `"365d"`.
|
||||
* @param page - 1-based page number (default `1`).
|
||||
* @param pageSize - Items per page (default `100`).
|
||||
* @returns Paginated {@link DashboardBanListResponse}.
|
||||
* @throws {ApiError} When the server returns a non-2xx status.
|
||||
*/
|
||||
export async function fetchBans(
|
||||
range: TimeRange,
|
||||
page = 1,
|
||||
pageSize = 100,
|
||||
): Promise<DashboardBanListResponse> {
|
||||
const params = new URLSearchParams({
|
||||
range,
|
||||
page: String(page),
|
||||
page_size: String(pageSize),
|
||||
});
|
||||
return get<DashboardBanListResponse>(`${ENDPOINTS.dashboardBans}?${params.toString()}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a paginated access list (individual matched log lines) for the
|
||||
* selected time window.
|
||||
*
|
||||
* @param range - Time-range preset.
|
||||
* @param page - 1-based page number (default `1`).
|
||||
* @param pageSize - Items per page (default `100`).
|
||||
* @returns Paginated {@link AccessListResponse}.
|
||||
* @throws {ApiError} When the server returns a non-2xx status.
|
||||
*/
|
||||
export async function fetchAccesses(
|
||||
range: TimeRange,
|
||||
page = 1,
|
||||
pageSize = 100,
|
||||
): Promise<AccessListResponse> {
|
||||
const params = new URLSearchParams({
|
||||
range,
|
||||
page: String(page),
|
||||
page_size: String(pageSize),
|
||||
});
|
||||
return get<AccessListResponse>(`/api/dashboard/accesses?${params.toString()}`);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user