Stage 9: ban history — backend service, router, frontend history page
- history.py models: HistoryBanItem, HistoryListResponse, IpTimelineEvent, IpDetailResponse
- history_service.py: list_history() with dynamic WHERE clauses (range/jail/ip
prefix/all-time), get_ip_detail() with timeline aggregation
- history.py router: GET /api/history + GET /api/history/{ip} (404 for unknown)
- Fixed latent bug in ban_service._parse_data_json: json.loads('null') -> None
-> AttributeError; now checks isinstance(parsed, dict) before assigning obj
- 317 tests pass (27 new), ruff + mypy clean (46 files)
- types/history.ts, api/history.ts, hooks/useHistory.ts created
- HistoryPage.tsx: filter bar (time range/jail/IP), DataGrid table,
high-ban-count row highlighting, per-IP IpDetailView with timeline,
pagination
- Frontend tsc + ESLint clean (0 errors/warnings)
- Tasks.md Stage 9 marked done
This commit is contained in:
59
frontend/src/types/history.ts
Normal file
59
frontend/src/types/history.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* TypeScript types for the ban history API.
|
||||
*/
|
||||
|
||||
/** Optional time-range filter for history queries. */
|
||||
export type TimeRange = "24h" | "7d" | "30d" | "365d";
|
||||
|
||||
/** A single row in the history ban-list table. */
|
||||
export interface HistoryBanItem {
|
||||
ip: string;
|
||||
jail: string;
|
||||
banned_at: string;
|
||||
ban_count: number;
|
||||
failures: number;
|
||||
matches: string[];
|
||||
country_code: string | null;
|
||||
country_name: string | null;
|
||||
asn: string | null;
|
||||
org: string | null;
|
||||
}
|
||||
|
||||
/** Paginated response from GET /api/history */
|
||||
export interface HistoryListResponse {
|
||||
items: HistoryBanItem[];
|
||||
total: number;
|
||||
page: number;
|
||||
page_size: number;
|
||||
}
|
||||
|
||||
/** A single ban event in a per-IP timeline. */
|
||||
export interface IpTimelineEvent {
|
||||
jail: string;
|
||||
banned_at: string;
|
||||
ban_count: number;
|
||||
failures: number;
|
||||
matches: string[];
|
||||
}
|
||||
|
||||
/** Full historical record for a single IP address. */
|
||||
export interface IpDetailResponse {
|
||||
ip: string;
|
||||
total_bans: number;
|
||||
total_failures: number;
|
||||
last_ban_at: string | null;
|
||||
country_code: string | null;
|
||||
country_name: string | null;
|
||||
asn: string | null;
|
||||
org: string | null;
|
||||
timeline: IpTimelineEvent[];
|
||||
}
|
||||
|
||||
/** Query parameters supported by GET /api/history */
|
||||
export interface HistoryQuery {
|
||||
range?: TimeRange;
|
||||
jail?: string;
|
||||
ip?: string;
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
}
|
||||
Reference in New Issue
Block a user