Add source=archive option for dashboard endpoints and history service; update Docs/Tasks.md; include archive branch for list_bans, bans_by_country, ban_trend, bans_by_jail; tests for archive paths.
1) Added _get_active_jail_names import in jail_config_service 2) Added _get_active_jail_names and _parse_jails_sync imports in filter_config_service and resolved constants/exceptions 3) Added bangui_version=__version__ in config_service.get_service_status and tests
- Add TYPE_CHECKING guards for runtime-expensive imports (aiohttp, aiosqlite)
- Reorganize imports to follow PEP 8 conventions
- Convert TypeAlias to modern PEP 695 type syntax (where appropriate)
- Use Sequence/Mapping from collections.abc for type hints (covariant)
- Replace string literals with cast() for improved type inference
- Fix casting of Fail2BanResponse and TypedDict patterns
- Add IpLookupResult TypedDict for precise return type annotation
- Reformat overlong lines for readability (120 char limit)
- Add asyncio_mode and filterwarnings to pytest config
- Update test fixtures with improved type hints
This improves mypy type checking and makes type relationships explicit.
On startup BanGUI now verifies that the four fail2ban jail config files
required by its two custom jails (manual-Jail and blocklist-import) are
present in `$fail2ban_config_dir/jail.d`. Any missing file is created
with the correct default content; existing files are never overwritten.
Files managed:
- manual-Jail.conf (enabled=false template)
- manual-Jail.local (enabled=true override)
- blocklist-import.conf (enabled=false template)
- blocklist-import.local (enabled=true override)
The check runs in the lifespan hook immediately after logging is
configured, before the database is opened.
Task 0.1: Create database parent directory before connecting
- main.py _lifespan now calls Path(database_path).parent.mkdir(parents=True,
exist_ok=True) before aiosqlite.connect() so the app starts cleanly on
a fresh Docker volume with a nested database path.
Task 0.2: SetupRedirectMiddleware redirects when db is None
- Guard now reads: if db is None or not is_setup_complete(db)
A missing database (startup still in progress) is treated as setup not
complete instead of silently allowing all API routes through.
Task 0.3: SetupGuard redirects to /setup on API failure
- .catch() handler now sets status to 'pending' instead of 'done'.
A crashed backend cannot serve protected routes; conservative fallback
is to redirect to /setup.
Task 0.4: SetupPage shows spinner while checking setup status
- Added 'checking' boolean state; full-screen Spinner is rendered until
getSetupStatus() resolves, preventing form flash before redirect.
- Added console.warn in catch block; cleanup return added to useEffect.
Also: remove unused type: ignore[call-arg] from config.py.
Tests: 18 backend tests pass; 117 frontend tests pass.
Rename GET/PUT /api/config/actions/{name} to /actions/{name}/raw in
file_config.py to eliminate the route-shadowing conflict with config.py,
which registers its own GET /actions/{name} returning ActionConfig.
Add configActionRaw endpoint helper in endpoints.ts and update
fetchActionFile/updateActionFile in config.ts to use it. Add
TestGetActionFileRaw and TestUpdateActionFileRaw test classes.
- Add has_local_override field to InactiveJail model
- Update _build_inactive_jail and list_inactive_jails to compute the field
- Add delete_jail_local_override() service function
- Add DELETE /api/config/jails/{name}/local router endpoint
- Surface has_local_override in frontend InactiveJail type
- Show Deactivate Jail button in JailsTab when has_local_override is true
- Add tests: TestBuildInactiveJail, TestListInactiveJails, TestDeleteJailLocalOverride
- jail_service.restart(): replace invalid ["restart"] socket command with
["stop"], matching fail2ban transmitter protocol. The daemon is now
stopped via socket; the caller starts it via subprocess.
- config_file_service: expose _start_daemon and _wait_for_fail2ban as
public start_daemon / wait_for_fail2ban functions.
- restart_fail2ban router: orchestrate stop (socket) → start (subprocess)
→ probe (socket). Returns 204 on success, 503 when fail2ban does not
come back within 10 s. Catches JailOperationError → 409.
- reload_fail2ban router: add JailOperationError catch → 409 Conflict,
consistent with other jail control endpoints.
- Tests: add TestJailControls.test_restart_* (3 cases), TestReloadFail2ban
502/409 cases, TestRestartFail2ban (5 cases), TestRollbackJail (6
integration tests verifying file-write, subprocess invocation, socket-
probe truthiness, active_jails count, and offline-at-call-time).
This commit implements fixes for three independent bugs in the fail2ban configuration and integration layer:
1. Task 1: Detect UnknownJailException and prevent silent failures
- Added JailNotFoundError detection in jail_service.reload_all()
- Enhanced error handling in config_file_service to catch JailNotFoundError
- Added specific error message with logpath validation hints
- Added rollback test for this scenario
2. Task 2: Fix iptables-allports exit code 4 (xtables lock contention)
- Added global banaction setting in jail.conf with -w 5 lockingopt
- Removed redundant per-jail banaction overrides from bangui-sim and blocklist-import
- Added production compose documentation note
3. Task 3: Suppress log noise from unsupported backend/idle commands
- Implemented capability detection to cache command support status
- Double-check locking to minimize lock contention
- Avoids sending unsupported get <jail> backend/idle commands
- Returns default values without socket calls when unsupported
All changes include comprehensive tests and maintain backward compatibility.
Task 1: Move Configuration to last position in sidebar NAV_ITEMS
Task 2: Add automatic rollback when jail activation fails
- Back up .local override file before writing
- Restore original file (or delete) on reload failure, health-check
failure, or jail not appearing post-reload
- Return recovered=True/False in JailActivationResponse
- Show warning/critical banner in ActivateJailDialog based on recovery
- Add _restore_local_file_sync and _rollback_activation_async helpers
- Add 3 new tests: rollback on reload failure, health-check failure,
and double failure (recovered=False)
Task 3: Color pie chart legend labels to match their slice color
- legendFormatter now returns ReactNode with span style={{ color }}
- Import LegendPayload from recharts/types/component/DefaultLegendContent
After deactivation the endpoint now calls _run_probe to flush the
cached server status immediately, matching the activate_jail behaviour
added in Task 5. Without this, the dashboard active-jail count could
remain stale for up to 30 s after a deactivation reload.
- config.py: capture result, await _run_probe, return result
- test_config.py: add test_deactivate_triggers_health_probe; fix 3
pre-existing UP017 ruff warnings (datetime.UTC alias)
- test_health.py: update test to assert the new fail2ban field
backend/tests/test_routers/test_file_config.py:
- TestListActionFiles.test_200_returns_files: GET /api/config/actions is
handled by config.router (registered before file_config.router), so mock
config_file_service.list_actions and assert on ActionListResponse.actions
- TestCreateActionFile.test_201_creates_file: same route conflict; mock
config_file_service.create_action and use ActionCreateRequest body format
frontend/src/components/__tests__/ConfigPageLogPath.test.tsx:
- Log paths are rendered as <Input value={path}>, not text nodes; replace
getByText() with getByDisplayValue() for both test assertions