74 lines
4.5 KiB
Markdown
74 lines
4.5 KiB
Markdown
# 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.
|
|
|
|
---
|