list_bans() was calling geo_service.lookup() once per IP on the page (e.g. 100 sequential HTTP requests), hitting the ip-api.com free-tier single-IP limit of 45 req/min. IPs beyond the ~45th were added to the in-process negative cache (5 min TTL) and showed as no country until the TTL expired. The map endpoint never had this problem because it used lookup_batch (100 IPs per POST). Add http_session and app_db params to list_bans(). When http_session is provided (production path), the entire page is resolved in one lookup_batch() call instead of N individual ones. The legacy geo_enricher callback is kept for test compatibility. Update the dashboard router to use the batch path directly. Adds 3 tests covering the batch geo path, failure resilience, and http_session priority over geo_enricher.
1.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.
Completed
[DONE] Fix: Country column shows "—" for blocklist-import IPs in ban list
Root cause: ban_service.list_bans() resolved geo data one IP at a time via
geo_service.lookup(), which uses the ip-api.com single-IP endpoint (45 req/min
free tier limit). A page of 100 bans triggered 100 sequential HTTP requests;
after the ~45th request ip-api.com applied rate limiting, all remaining IPs were
added to the in-process negative cache (5 min TTL), and they showed "—" in the
country column permanently until the TTL expired. Because the map endpoint
(bans_by_country) used lookup_batch (100 IPs per POST), it never hit the
rate limit, which is why the map showed colours while the list did not.
Fix: Added http_session and app_db parameters to list_bans(). When
http_session is provided (production path via the dashboard router), the entire
page of IPs is resolved in a single geo_service.lookup_batch() call instead of
100 individual ones. The legacy geo_enricher callback is kept for backwards
compatibility in tests. Updated dashboard.py to pass http_session and db
instead of constructing a per-IP enricher closure. Added 3 new tests covering
the batch path, failure resilience, and priority over geo_enricher.