Add origin field and filter for ban sources (Tasks 1 & 2)

- Task 1: Mark imported blocklist IP addresses
  - Add BanOrigin type and _derive_origin() to ban.py model
  - Populate origin field in ban_service list_bans() and bans_by_country()
  - BanTable and MapPage companion table show origin badge column
  - Tests: origin derivation in test_ban_service.py and test_dashboard.py

- Task 2: Add origin filter to dashboard and world map
  - ban_service: _origin_sql_filter() helper; origin param on list_bans()
    and bans_by_country()
  - dashboard router: optional origin query param forwarded to service
  - Frontend: BanOriginFilter type + BAN_ORIGIN_FILTER_LABELS in ban.ts
  - fetchBans / fetchBansByCountry forward origin to API
  - useBans / useMapData accept and pass origin; page resets on change
  - BanTable accepts origin prop; DashboardPage adds segmented filter
  - MapPage adds origin Select next to time-range picker
  - Tests: origin filter assertions in test_ban_service and test_dashboard
This commit is contained in:
2026-03-07 20:03:43 +01:00
parent 706d2e1df8
commit 53d664de4f
28 changed files with 1637 additions and 103 deletions

View File

@@ -5,6 +5,7 @@
import { useCallback, useEffect, useRef, useState } from "react";
import { fetchBansByCountry } from "../api/map";
import type { BansByCountryResponse, MapBanItem, TimeRange } from "../types/map";
import type { BanOriginFilter } from "../types/ban";
// ---------------------------------------------------------------------------
// Return type
@@ -31,7 +32,10 @@ export interface UseMapDataResult {
// Hook
// ---------------------------------------------------------------------------
export function useMapData(range: TimeRange = "24h"): UseMapDataResult {
export function useMapData(
range: TimeRange = "24h",
origin: BanOriginFilter = "all",
): UseMapDataResult {
const [data, setData] = useState<BansByCountryResponse | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
@@ -43,7 +47,7 @@ export function useMapData(range: TimeRange = "24h"): UseMapDataResult {
setLoading(true);
setError(null);
fetchBansByCountry(range)
fetchBansByCountry(range, origin)
.then((resp) => {
setData(resp);
})
@@ -55,7 +59,7 @@ export function useMapData(range: TimeRange = "24h"): UseMapDataResult {
.finally((): void => {
setLoading(false);
});
}, [range]);
}, [range, origin]);
useEffect((): (() => void) => {
load();