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:
113
frontend/src/types/ban.ts
Normal file
113
frontend/src/types/ban.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* TypeScript interfaces mirroring the backend ban Pydantic models.
|
||||
*
|
||||
* `backend/app/models/ban.py` — dashboard dashboard sections.
|
||||
*/
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Time-range selector
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** The four supported time-range presets for dashboard views. */
|
||||
export type TimeRange = "24h" | "7d" | "30d" | "365d";
|
||||
|
||||
/** Human-readable labels for each time-range preset. */
|
||||
export const TIME_RANGE_LABELS: Record<TimeRange, string> = {
|
||||
"24h": "Last 24 h",
|
||||
"7d": "Last 7 days",
|
||||
"30d": "Last 30 days",
|
||||
"365d": "Last 365 days",
|
||||
} as const;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Ban-list table item
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A single row in the dashboard ban-list table.
|
||||
*
|
||||
* Mirrors `DashboardBanItem` from `backend/app/models/ban.py`.
|
||||
*/
|
||||
export interface DashboardBanItem {
|
||||
/** Banned IP address. */
|
||||
ip: string;
|
||||
/** Jail that issued the ban. */
|
||||
jail: string;
|
||||
/** ISO 8601 UTC timestamp of the ban. */
|
||||
banned_at: string;
|
||||
/** First matched log line (context for the ban), or null. */
|
||||
service: string | null;
|
||||
/** ISO 3166-1 alpha-2 country code, or null if unknown. */
|
||||
country_code: string | null;
|
||||
/** Human-readable country name, or null if unknown. */
|
||||
country_name: string | null;
|
||||
/** Autonomous System Number string, e.g. "AS3320", or null. */
|
||||
asn: string | null;
|
||||
/** Organisation name associated with the IP, or null. */
|
||||
org: string | null;
|
||||
/** How many times this IP was banned. */
|
||||
ban_count: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Paginated ban-list response from `GET /api/dashboard/bans`.
|
||||
*
|
||||
* Mirrors `DashboardBanListResponse` from `backend/app/models/ban.py`.
|
||||
*/
|
||||
export interface DashboardBanListResponse {
|
||||
/** Ban items for the current page. */
|
||||
items: DashboardBanItem[];
|
||||
/** Total number of bans in the selected time window. */
|
||||
total: number;
|
||||
/** Current 1-based page number. */
|
||||
page: number;
|
||||
/** Maximum items per page. */
|
||||
page_size: number;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Access-list table item
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A single row in the dashboard access-list table.
|
||||
*
|
||||
* Each row represents one matched log line (failure attempt) that
|
||||
* contributed to a ban.
|
||||
*
|
||||
* Mirrors `AccessListItem` from `backend/app/models/ban.py`.
|
||||
*/
|
||||
export interface AccessListItem {
|
||||
/** IP address of the access event. */
|
||||
ip: string;
|
||||
/** Jail that recorded the access. */
|
||||
jail: string;
|
||||
/** ISO 8601 UTC timestamp of the ban that captured this access. */
|
||||
timestamp: string;
|
||||
/** Raw matched log line. */
|
||||
line: string;
|
||||
/** ISO 3166-1 alpha-2 country code, or null. */
|
||||
country_code: string | null;
|
||||
/** Human-readable country name, or null. */
|
||||
country_name: string | null;
|
||||
/** ASN string, or null. */
|
||||
asn: string | null;
|
||||
/** Organisation name, or null. */
|
||||
org: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Paginated access-list response from `GET /api/dashboard/accesses`.
|
||||
*
|
||||
* Mirrors `AccessListResponse` from `backend/app/models/ban.py`.
|
||||
*/
|
||||
export interface AccessListResponse {
|
||||
/** Access items for the current page. */
|
||||
items: AccessListItem[];
|
||||
/** Total number of access events in the selected window. */
|
||||
total: number;
|
||||
/** Current 1-based page number. */
|
||||
page: number;
|
||||
/** Maximum items per page. */
|
||||
page_size: number;
|
||||
}
|
||||
Reference in New Issue
Block a user