Allow useNavigationAbortSignal to opt out of navigation-based abort
for long-lived background tasks like polling. Set ignoreCancellation: true
to keep requests alive across route changes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add deprecation middleware for warning headers on sunset endpoints
- Add jails_v2 router for API v2 migration path
- Update CI workflow with new test coverage
- Update API versioning documentation
- Remove completed tasks from Tasks.md
- client.ts: store correlation ID in sessionStorage so HMR (module re-eval)
does not generate a new ID mid-session; add clearSessionCorrelationId()
- endpoints.ts: fix 3 template literal trailing-quote bugs (missing ')' chars);
replace template literals with string concat for encodeURIComponent calls
- AuthProvider.tsx: call clearSessionCorrelationId() on logout
- App.tsx: reorder ThemeProvider import before AuthProvider per PROVIDER_ORDER.md;
indent Routes inside AuthProvider to match expected tree structure
- Tasks.md: update task status
- providerTreeOrder.test.tsx: add integration tests for provider nesting order
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Issue #51: Enforce repository boundary at CI level using import-linter.
Contract 'forbid_router_db_import' checks that app.routers never imports
app.dependencies directly, keeping the DB access path through service
contexts only.
- Add import-linter>=2.0.0 to dev dependencies (backend/pyproject.toml)
- Configure [tool.importlinter] with package_root and root_packages
- Add [[tool.importlinter.contracts]] with type='forbidden', source
app.routers, forbidden app.dependencies
- Add 'Import Boundary' CI job (import-linter)
- Document import-linter in CONTRIBUTING.md code quality table
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Previously the AbortController was created inside useEffect, which runs
after render. This meant requests initiated during render received
the previous cycle's (possibly aborted) signal and were cancelled
immediately instead of completing.
Now the controller is created synchronously when pathname changes, before
any request can be initiated on the new route. The old controller is
aborted in the same conditional block, before the new one is created.
Side effect: removed resolved Issue #49 from Tasks.md.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move X-BanGUI-Request header name/value to backend/app/utils/constants.py as single source of truth. Add GET /api/v1/config/security-headers endpoint. Update csrf middleware, frontend api client, and docs to use shared constants.
- Add _acquire_readonly_connection() that applies PRAGMA query_only=ON after connect
- Verify PRAGMA value back to catch URI flag bypasses
- Wrap in async context manager _readonly_connection() used by all repo methods
- Replace hardcoded '_SESSION_COOKIE_NAME' in CSRF middleware with import from
app.utils.constants
- Remove completed Issues #45 and #46 from Docs/Tasks.md (Issue #46 now fixed,
#45 cache invalidation deferred to auth refactor branch)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Stale sessions from a stolen device could be reused up to the cache
TTL after a legitimate user re-logs in, because login never cleared
the existing cache entry.
Changes:
- Add invalidate_by_user(user_id) to SessionCache protocol
- InMemorySessionCache maintains a user_id -> set[token] index to
support O(1) invalidation of all sessions for a given user
- NoOpSessionCache stub updated for API compatibility
- auth_service.login() now returns the Session object alongside
signed_token and expires_at
- login router calls session_cache.invalidate_by_user(session.id)
immediately after successful authentication
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fail with RuntimeError when WEB_CONCURRENCY or BANGUI_WORKERS > 1.
In-memory session cache, rate-limit windows, and runtime state are
process-local. Multi-worker silently causes stale limits, ghost sessions,
inconsistent status.
Skipped when TESTING=1.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add GitHub Actions CI pipeline with pytest, ruff, mypy
- Expand Tasks.md with implementation tracking and testing criteria
- Update CONTRIBUTING.md with CI requirements
- Add Testing-Requirements.md with coverage thresholds and PR checks
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Extract jail status/processing to helper functions
- Add error_handling.py service for centralized error handling
- Update config.py with validation and defaults
- Update .env.example with all config options
- Remove obsolete Tasks.md, add Service-Development.md
- Minor fixes across routers and services
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace contextlib.suppress with try/except + warning log
- Add test for fail2ban client
- Remove stale Issue #21 from Tasks.md (indexes)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- New get_fail2ban_db_path() in setup_service resolves DB path from configured socket path
- New ensure_fail2ban_indexes() creates missing performance indexes on bans table
- Call ensure_fail2ban_indexes on every startup before first ban query
- Remove completed tasks from Docs/Tasks.md
- Update Docs/PERFORMANCE.md with index findings
- Move config loading to dedicated ConfigLoader class with validation
- Add DATABASE_MIGRATIONS.md content to TROUBLESHOOTING.md
- Add API_STATUS_CODES.md documenting all API response codes
- Update runner.csx to use new config structure
- Add check_responses.py validation script
- Update config tests for new structure
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add explicit HTTP status code documentation to every endpoint
across 15 router files. Each endpoint now declares all possible
response codes (200/201/204/400/401/404/409/429/502/503) with
descriptions so frontend can distinguish error types.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Establishes shared type conventions to prevent runtime type mismatches
between TS frontend and Python backend. Covers snake_case JSON field
names, null vs empty string handling, timestamp formats, and validation
patterns for country codes, bans, and jail configuration.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Detect catastrophic backtracking patterns before regex compilation
using regexploit library. Add ReDoSDetectedError exception and
_MINIMUM_STARRINESS threshold (>=3) to catch dangerous patterns
like (a+)+b. Update pyproject.toml deps, add tests for detection.
Replace check-then-insert race condition with INSERT ON CONFLICT.
- upsert_pending uses RETURNING id for atomic upsert
- UNIQUE(source_id, content_hash) constraint from migration 6
- blocklist_import_workflow updated to use upsert_pending
- test_import_source_success fixed for async mock patterns
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Enhance response model with additional fields and validation
- Update health and server router implementations
- Improve frontend type definitions and API integration
- Clean up documentation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- All backend routers moved to /api/v1/ prefix
- Frontend BASE_URL updated to /api/v1
- Setup redirect middleware updated to redirect to /api/v1/setup
- Health router path fixed: prefix=/api/v1/health, @router.get('')
- conftest.py: set server_status=online for test fixture
- Created Docs/API_VERSIONING.md with deprecation policy
- Updated Docs/Backend-Development.md with versioning section
- Updated Instructions.md curl examples
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add optional requestKey parameter to UseFetchDataOptions
- Implement module-level cache (inFlightRequests) to track in-flight requests
- When requestKey is provided, multiple hook instances with same key share in-flight requests
- Prevents duplicate API calls when multiple components fetch same data or rapid refresh calls
- Cache entries are automatically cleared when response arrives (success or error)
- Maintains backward compatibility: without requestKey, behaves as before
- Adds comprehensive tests for deduplication scenarios
This reduces bandwidth waste and prevents race conditions caused by concurrent requests for identical data.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add custom comparison function to React.memo for TopCountriesPieChart
- Add custom comparison function to React.memo for TopCountriesBarChart
- Use JSON.stringify for deep equality comparison of countries and countryNames
- Prevents unnecessary re-renders when parent updates with same data
- Avoids Recharts reprocessing 5000+ data points on each parent re-render
All tests passing. No linting issues.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>