Files
BanGUI/Docs/Tasks.md
2026-04-05 22:45:41 +02:00

4.5 KiB

BanGUI — Task List

This document breaks the entire BanGUI project into development stages, ordered so that each stage builds on the previous one. Every task is described in prose with enough detail for a developer to begin work. References point to the relevant documentation.

Reference: Docs/Refactoring.md for full analysis of each issue.


Open Issues


TASK-001 — WorldMap: filter companion table by selected country (server-side)

Status: Done
Priority: Medium
Domain: Full-stack (backend + frontend)
References: Docs/Features.md §4, Docs/Web-Development.md

Background

The GET /api/dashboard/bans/by-country endpoint always returns the 200 most recent ban rows in bans (constant _MAX_COMPANION_BANS = 200 in backend/app/services/ban_service.py). MapPage.tsx stores a selectedCountry state and filters the returned rows client-side via visibleBans. This means the companion table can only show the fraction of a country's bans that fall within the global top-200 window. If the selected time range has, say, 1 500 bans and 300 are from China, but China's bans are not all in the top 200 overall, the table will silently display fewer than 300 rows.

When a country is selected the companion table must return the complete set of bans for that country so the user sees an accurate picture.

Desired behaviour

  • No country selected → companion table shows the 200 most recent bans across all countries (existing behaviour, no change).
  • Country selected → the server returns all ban entries for that country in the selected time window; no client-side row-count cap applies.
  • Deselecting a country (clicking the same country again, or the "Clear filter" button) reverts to the default 200-row unfiltered view.
  • The existing visibleBans client-side filter in MapPage.tsx can remain as a defensive guard but must not be the only filter.

Implementation steps

  1. Backend — router (backend/app/routers/dashboard.py)

    • Add country_code: str | None = Query(default=None, description="ISO alpha-2 country code to filter companion rows.") to get_bans_by_country.
    • Pass it to ban_service.bans_by_country(..., country_code=country_code).
  2. Backend — service (backend/app/services/ban_service.py)

    • Add country_code: str | None = None keyword argument to bans_by_country.
    • After geo_map is built (existing geo-resolution step), collect IPs whose resolved country matches country_code.
    • For the fail2ban source: call fail2ban_db_repo.get_currently_banned with ip_filter=matched_ips and no limit (remove the _MAX_COMPANION_BANS cap for filtered queries).
    • For the archive source: filter all_rows to those whose IP is in matched_ips and return all of them (skip the page_size=_MAX_COMPANION_BANS call).
    • When country_code is None, behaviour is identical to today.
  3. Backend — repository (backend/app/repositories/fail2ban_db_repo.py)

    • Add ip_filter: list[str] | None = None to get_currently_banned.
    • When provided and non-empty, append AND ip IN ({placeholders}) to the SQL WHERE clause, parameterised safely (never interpolated as a string).
  4. Backend — repository (archive) (backend/app/repositories/history_archive_repo.py)

    • Similarly add optional ip_filter to the archive companion-rows query used from bans_by_country.
  5. Frontend — API client (frontend/src/api/map.ts)

    • Add optional countryCode?: string parameter to fetchBansByCountry.
    • When set, append country_code=<value> to the query string.
  6. Frontend — hook (frontend/src/hooks/useMapData.ts)

    • Add countryCode?: string to the function signature.
    • Include it in the useCallback dependency array and pass it to fetchBansByCountry.
  7. Frontend — page (frontend/src/pages/MapPage.tsx)

    • Pass selectedCountry ?? undefined as countryCode to useMapData.
    • The hook's effect will re-fetch automatically when selectedCountry changes; the existing useEffect that resets page to 1 already covers this.

Testing guidance

  • Select a country that has > 200 bans in the chosen time window; confirm the companion table shows more than the previous cap would allow.
  • With no country selected, confirm only 200 rows are returned (no regression).
  • Deselect the country; confirm the unfiltered 200-row view is restored.
  • Test with the archive source as well as the fail2ban live source.
  • Verify the ip_filter SQL clause is parameterised and cannot be injected.