Move session cache initialization from per-request _build_app_context to
startup lifespan handler. The session cache type is now decided once at app
startup based on settings, making _build_app_context pure (read-only).
Changes:
- Move cache initialization logic to new _update_session_cache() in main.py
- Call _update_session_cache() during lifespan startup to initialize cache
- Remove three if/elif/elif branches mutating state.session_cache from _build_app_context
- Add cache swap logic to set_runtime_settings() in runtime_state.py to handle
runtime settings changes (e.g., setup wizard updates)
- Keep app.state.session_cache initialization in create_app() for test compatibility
This ensures:
- _build_app_context is pure and doesn't mutate app state on each request
- Session cache configuration decisions are centralized at startup
- Settings changes during runtime (via setup wizard) also trigger cache swap
- Cache initialization logic is isolated in one place
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Reorganized dashboard router with improved structure
- Enhanced ban_service with better separation of concerns
- Updated history service with cleaner logic
- Improved constants and configuration handling
- Updated documentation of completed tasks
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Consolidate duplicate _ok(), _to_dict(), ensure_list(), and is_not_found_error()
functions from 6 service modules into a single canonical implementation at
backend/app/utils/fail2ban_response.py.
Changes:
- Create fail2ban_response.py with canonical implementations
- Remove local duplicates from: ban_service, jail_service, config_service,
health_service, server_service, config_file_utils
- Update all imports to use shared module
- Add comprehensive docstrings and examples
- Update Architecture.md and Backend-Development.md documentation
Benefits:
- Single source of truth for response parsing logic
- Eliminates code duplication across service layer
- Improves maintainability and consistency
- Enables centralized bug fixes and improvements
Tests: All 228 service tests passing, no regressions
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use NoOpSessionCache in backend/app/main.py and dynamically switch cache implementation in backend/app/dependencies.py so disabled cache mode remains safe while get_session_cache always returns a valid object.
Wire DEFAULT_BLOCKING_EXECUTOR as the default executor in backend/app/utils/async_utils.py, preserving custom executors and marking Task 22 completed in Docs/Tasks.md.
- 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.
Stage 1.1-1.3: reload_all include/exclude_jails params already implemented;
added keyword-arg assertions in router and service tests.
Stage 2.1/6.1: _send_command_sync retry loop (3 attempts, 150ms exp backoff)
retrying on EAGAIN/ECONNREFUSED/ENOBUFS; immediate raise on all other errors.
Stage 2.2: asyncio.Lock at module level in jail_service.reload_all to
serialize concurrent reload--all commands.
Stage 3.1: activate_jail re-queries _get_active_jail_names after reload;
returns active=False with descriptive message if jail did not start.
Stage 4.1/6.2: asyncio.Semaphore (max 10) in Fail2BanClient.send, lazy-
initialized; logs fail2ban_command_waiting_semaphore at debug when waiting.
Stage 5.1/5.2: unit tests asserting reload_all is called with include_jails
and exclude_jails; activation verification happy/sad path tests.
Stage 6.3: TestSendCommandSyncRetry (5 cases) + TestFail2BanClientSemaphore
concurrency test.
Stage 7.1-7.3: _since_unix uses time.time(); bans_by_jail debug logging with
since_iso; diagnostic warning when total==0 despite table rows; unit test
verifying the warning fires for stale data.