Memoize per-Geography style objects with useMemo so React.memo can
skip re-renders when only the tooltip position changes. Stabilize
mouse event handlers with useCallback using data-* attributes instead
of per-Geography closures. This eliminates the state-update race
condition that caused hover fill colors to flash back to defaults.
HistoryPage no longer requires Apply/Clear; filter state auto-syncs with query. Added guard to avoid redundant state updates. Updated task list in Docs/Tasks.md to mark completion.
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.
Extract jail, filter, and action configuration management into separate
domain-focused service modules:
- jail_config_service.py: Jail activation, deactivation, validation, rollback
- filter_config_service.py: Filter discovery, CRUD, assignment to jails
- action_config_service.py: Action discovery, CRUD, assignment to jails
Benefits:
- Reduces monolithic 3100-line module into three focused modules
- Improves readability and maintainability per domain
- Clearer separation of concerns following single responsibility principle
- Easier to test domain-specific functionality in isolation
- Reduces coupling - each service only depends on its needed utilities
Changes:
- Create three new service modules under backend/app/services/
- Update backend/app/routers/config.py to import from new modules
- Update exception and function imports to source from appropriate service
- Update Architecture.md to reflect new service organization
- All existing tests continue to pass with new module structure
Relates to Task 4 of refactoring backlog in Docs/Tasks.md
Task 2: rename 'Failures:' label to 'Failed Attempts:' and update tooltip
to 'Total failed authentication attempts currently tracked by fail2ban
across all active jails' — more accurate than 'Currently failing IPs'.
Task 3: move <ServerHealthSection /> to the top of ServerTab so users
see connectivity status before scrolling through all settings fields.
- Inject __APP_VERSION__ at build time via vite.config.ts define (reads
frontend/package.json#version); declare the global in vite-env.d.ts.
- Render 'BanGUI v{__APP_VERSION__}' in the sidebar footer (MainLayout)
when expanded; hidden when collapsed.
- Rename fail2ban version tooltip to 'fail2ban daemon version' in
ServerStatusBar so it is visually distinct from the app version.
- Sync frontend/package.json version (0.9.0 → 0.9.3) to match
Docker/VERSION; update release.sh to keep them in sync on every bump.
- Add vitest define stub for __APP_VERSION__ so tests compile cleanly.
- Add ServerStatusBar and MainLayout test suites (10 new test cases).
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.