Commit Graph

84 Commits

Author SHA1 Message Date
3b3728c58d feat: implement request deduplication in useFetchData
- 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>
2026-05-01 18:44:46 +02:00
1af67eb0ce Add Application Performance Monitoring (APM) with Prometheus metrics
- Backend: Implement Prometheus metrics collection
  - Add prometheus-client dependency
  - Create metrics utility module with HTTP request tracking counters, histograms, gauges
  - Implement MetricsMiddleware to track request latency, count, and active requests
  - Add /metrics endpoint to expose metrics in Prometheus text format
  - Normalize paths to prevent cardinality explosion (e.g., /api/{id} for UUIDs)
  - Exclude /metrics and /health from detailed tracking

- Frontend: Add web vitals and API metrics collection
  - Install web-vitals library (v4.0.0) for Core Web Vitals tracking
  - Create metrics utility module for FCP, LCP, CLS, INP, TTFB collection
  - Implement useTrackedFetch hook for automatic API call metrics (method, endpoint, status, duration)
  - Initialize web vitals tracking in App component on mount
  - Provide exportMetrics() for sending metrics to backend

- Testing:
  - Add comprehensive backend metrics tests (9 tests, 100% coverage)
  - Add comprehensive frontend metrics tests (10 tests)
  - All tests passing

- Documentation:
  - Expand Docs/Observability.md with complete APM section
  - Include metrics reference, integration examples (Prometheus, Datadog, NewRelic)
  - Add troubleshooting guide and best practices for cardinality management
  - Update Tasks.md to mark APM task as complete

Metrics exposed:
- bangui_http_requests_total: HTTP request count by method, endpoint, status
- bangui_http_request_duration_seconds: Request latency histogram
- bangui_http_active_requests: Active request gauge
- Web Vitals: CLS, FCP, INP, LCP, TTFB with ratings
- API metrics: endpoint, method, status, duration, timestamp

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-01 18:33:14 +02:00
3bd2a71367 Refactor usePolledData hook and add comprehensive tests
- Renamed usePolledIntervalCheck to usePolledData for clarity
- Updated hook to properly manage interval cleanup on unmount
- Added comprehensive test suite covering normal operation, error handling, and cleanup
- Updated documentation to reflect new hook name
- Updated Tasks.md to track progress

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-30 20:24:47 +02:00
336242ad06 Implement visibility-aware polling to reduce background tab resource usage
- Add usePageVisibility hook to track page visibility state
- Add pauseWhenHidden option to usePolledData (defaults to false for backward compatibility)
- When enabled, polling pauses when page is hidden and resumes with immediate refresh when visible
- Refactor useBlocklistStatus to use usePolledData with pauseWhenHidden=true
- Add comprehensive tests for usePageVisibility hook
- Add polling lifecycle documentation to Web-Development.md

Fixes #36: Polling continues when tab is not visible

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-29 20:01:25 +02:00
b27765928a Standardize API response envelopes: use items for collection responses and update tests 2026-04-28 20:48:00 +02:00
1c673d600c Standardize API response envelope shapes across all endpoints
This commit standardizes how API responses are wrapped, solving issue #24.

Problem:
- Inconsistent response envelopes (jails vs items vs bans vs no wrapper)
- Frontend required multiple field name variants
- Integration bugs from branching logic
- No clear pattern for different response types

Solution:
- Created response.py with base classes: PaginatedListResponse,
  CollectionResponse, CommandResponse
- Standardized all list/collection responses to use 'items' field
- Domain-specific field names for detail and aggregation responses
- Updated all backends routers and mappers
- Updated frontend types and hooks to match

Changes:
Backend:
- backend/app/models/response.py (new): Base response models
- backend/app/models/ban.py: Updated responses to inherit from bases
- backend/app/models/jail.py: Updated JailListResponse, JailCommandResponse
- backend/app/models/config.py: Updated collection responses
- backend/app/services/jail_service.py: Updated return statements
- backend/app/mappers/ban_mappers.py: Updated 'bans' to 'items'
- backend/tests/test_mappers/test_ban_mappers.py: Updated tests

Frontend:
- frontend/src/types/jail.ts: Updated response interfaces
- frontend/src/types/config.ts: Updated response interfaces
- frontend/src/hooks/useActiveBans.ts: Updated selector
- frontend/src/hooks/useJailList.ts: Updated selector
- frontend/src/hooks/useJailConfigs.ts: Updated selector
- frontend/src/hooks/useConfigActiveStatus.ts: Updated field access
- frontend/src/hooks/useJailAdmin.ts: Updated field access

Documentation:
- Docs/Backend-Development.md: Added § 4.1 API Response Envelope Policy

The policy defines:
1. Paginated lists use PaginatedListResponse (items, total, page, page_size)
2. Non-paginated collections use CollectionResponse (items, total)
3. Detail responses use entity-specific field names (jail, status, settings)
4. Command responses use CommandResponse (message, success, optional target)
5. Aggregations use domain-specific fields (jails, countries, buckets, bans)

All responses now follow one of these patterns, reducing frontend complexity.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-28 10:12:55 +02:00
7ba1cf7ca2 feat: Implement global request lifecycle cancellation on route transitions
Adds a navigation-aware request cancellation mechanism that automatically
aborts all route-specific API requests when the user navigates to a
different route. This prevents silent state-update errors from responses
arriving after component unmount and conserves bandwidth by cancelling
now-irrelevant requests.

Key additions:
- NavigationCancellationContext: Context for managing route-specific signals
- NavigationCancellationProvider: Provider that detects route changes and
  aborts all signals from the previous route
- useNavigationAbortSignal hook: Allows components to subscribe to
  navigation-aware cancellation signals
- Comprehensive tests for the cancellation lifecycle
- Documentation in Web-Development.md for request lifecycle policy

The provider is placed in the app hierarchy between BrowserRouter and
AuthProvider, ensuring consistent cancellation behavior across all routes.

Long-lived background tasks (polling, session validation) can opt-out by
managing their own AbortController lifecycle.

Closes #23 from Tasks.md: No global cancellation policy on route transitions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-28 09:58:59 +02:00
d10145e5d6 refactor(frontend): extract shared fetch lifecycle into useFetchData base hook
Eliminates ~100 lines of duplicated code across useListData and usePolledData
by creating a composable base hook that handles:
- Abort controller lifecycle and cancellation
- Loading/error state management
- Fetch error handling
- Unmount cleanup

Changes:
- Create hooks/useFetchData.ts with base fetch lifecycle (no effects on consumers)
- Refactor useListData to compose useFetchData, returns items array by default
- Refactor usePolledData to compose useFetchData, adds polling and focus-refetch
- Add comprehensive tests for useFetchData base hook
- Document hook architecture and composition pattern in Web-Development.md

Result: Both hooks now use shared primitives, reducing maintenance burden
and ensuring consistent cancellation/error handling across all data fetches.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-28 09:23:34 +02:00
5166789b68 feat: Implement typed error contracts in generic hooks
Introduce discriminated FetchError union type to replace weak string error
handling in API calls and hooks. Enables actionable error diagnostics.

Changes:
- Create types/api.ts with FetchError discriminated union (api_error,
  network_error, abort_error)
- Export type guards: isAuthError, isAbortError, isNetworkError, isApiError
- Update useListData and usePolledData to expose typed FetchError instead of
  string
- Add getErrorMessage() helper to extract displayable messages from FetchError
- Add createStringErrorAdapter() for backward compatibility with string error
  state
- Update handleFetchError() to work with both FetchError and string setters
- Update all consumer hooks to expose typed errors
- Update components to use getErrorMessage() when displaying errors
- Update tests to mock FetchError instead of strings
- Add comprehensive typed error model documentation to Web-Development.md

This enables better error handling patterns:
- Check error.type to distinguish between API, network, and abort errors
- Extract status codes for specific handling (401/403 auth, 50x server errors)
- Maintain backward compatibility with existing string-based error states

All TypeScript compilation passes with no errors.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-28 09:13:47 +02:00
6c8e2b3423 fix(#16): Establish consistent API usage layering patterns
- Refactor useActiveBans to use useListData generic hook instead of inline state management
- Refactor useBans to use useListData generic hook for consistency
- Add comprehensive 'API Usage Layering' section to Web-Development.md documenting:
  - Tier 1: API Functions (pure wrappers around HTTP calls)
  - Tier 2: Reusable Generic Hooks (useListData, useConfigItem for common patterns)
  - Tier 3: Domain Hooks (compose Tier 2 with domain-specific logic)
  - Tier 4: Components (receive data/actions via props or context)
- Document pattern for action callbacks with automatic data refresh
- List anti-patterns to avoid for future consistency

These changes improve composability, testability, and reduce code duplication by
establishing a clear convention for data-fetching patterns across the frontend.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-28 08:53:36 +02:00
42beb9cf3b refactor: Decompose ConfigPage into focused routing and component layers
Split the over-centralized ConfigPage into focused, composable layers:

1. useTabRouter hook: Encapsulates tab state management and URL synchronization
   - Maintains selected tab and active item (e.g., jail name)
   - Syncs state to location.state for deep linking and browser history
   - Supports bookmarkable URLs and back/forward navigation

2. ConfigPageContainer: Orchestrates tab navigation
   - Manages TabList and routes tab selection events
   - Conditionally renders tab content panels
   - Delegates domain-specific logic to tab components

3. ConfigPage: Focused page layout component
   - Renders page structure (header, title, description)
   - Delegates tab orchestration to ConfigPageContainer
   - No routing or tab state logic

Benefits:
- Page is now 30 lines vs 125 lines (76% reduction)
- Tab state management is reusable for other multi-tab pages
- Each tab component remains focused on domain-specific UI
- Deep linking and browser history work out of the box
- Easier to test and maintain

Added comprehensive tests:
- useTabRouter: 6 tests covering state initialization, tab selection, and deep linking
- ConfigPageContainer: 8 tests covering tab rendering and navigation
- ConfigPage: 3 tests for page structure

Updated Web-Development.md with tab orchestration pattern documentation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-28 08:27:36 +02:00
3095fa3313 fix(frontend): deduplicate setup status API calls using shared hook
Implement request deduplication to prevent multiple duplicate calls to GET
/api/setup when multiple components mount simultaneously. The fix introduces:

1. New 'useSharedSetupStatus' hook with module-level caching
   - Shares a single in-flight request across all consumers
   - Implements 30-second cache TTL with cache invalidation
   - Notifies all subscribers when cache is invalidated

2. Refactored 'useSetup' hook to use shared cache
   - Internally uses useSharedSetupStatus for status checks
   - Calls invalidateSetupStatus() after successful setup submission
   - Maintains backward-compatible API

3. Updated components using setup status
   - SetupGuard and SetupPage automatically benefit from deduplication
   - No changes needed to consumer code

4. Updated tests
   - Mocked useSharedSetupStatus in component tests
   - Added comprehensive tests for cache behavior
   - All existing tests pass

5. Documentation updates
   - Added 'Request Deduplication & Shared Caching' section to Web-Development.md
   - Explains when and how to use shared hooks
   - Provides complete implementation example

This eliminates wasted resources from duplicate API calls and potential
race conditions where different requests return slightly different states.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-26 13:32:44 +02:00
29daaa9906 TASK-004: Bootstrap frontend auth state from backend session check
Validates session on app mount by calling GET /api/auth/session instead of relying
solely on cached sessionStorage. This ensures the UI state always reflects server
reality — expired or revoked sessions are detected immediately.

Changes:
- Backend: Add GET /api/auth/session endpoint (requires valid session, returns 200/401)
- Frontend: Add useSessionValidation hook for mount-time validation
- Frontend: Add SessionValidationLoading component for validation spinner
- Frontend: Update AuthProvider to call validation on mount with loading state
- Frontend: Add validateSession API function
- Docs: Update Features.md with session validation behavior
- Docs: Update Web-Development.md with session validation pattern

Handles three outcomes:
1. Valid session (200): Proceed with cached state
2. Invalid session (401): Clear sessionStorage and redirect to login
3. Network error: Don't logout (backend may be temporarily unreachable)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-26 12:00:21 +02:00
69a0296c47 T-18: Merge useDashboardCountryData and useMapData into shared base hook
Create useBansByCountry as the shared base hook containing all common
fetch logic, abort-controller pattern, and state management. Both
useDashboardCountryData and useMapData now wrap this base hook:

- useDashboardCountryData: Thin wrapper that calls base hook with autoFetch=true
- useMapData: Wraps base hook with 300ms debounce layer

Changes:
- Create useBansByCountry.ts (base hook with optional autoFetch parameter)
- Refactor useDashboardCountryData.ts to use base hook
- Refactor useMapData.ts to use base hook with debounce wrapper
- Add tests for all three hooks

Benefits:
- Single source of truth for ban-by-country logic
- Bug fixes in base hook apply to both consumers
- Eliminates code duplication (~80 lines reduced)
- Maintains backward compatibility: existing call sites work unchanged

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-25 19:39:51 +02:00
3b527244aa Update task documentation and test fixes
- Update Tasks.md with current progress and status
- Fix useListData hook tests

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-25 19:34:16 +02:00
6490e9d3df T-16: Centralize PAGE_SIZE frontend constants
- Add BAN_PAGE_SIZE (100) and HISTORY_PAGE_SIZE (50) to frontend/src/utils/constants.ts
- Replace local PAGE_SIZE definitions in useBans.ts and HistoryPage.tsx with imports
- Eliminates risk of pagination constants silently diverging from backend defaults
- Single source of truth for all pagination sizes

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-25 19:27:24 +02:00
8bd5713d38 Refactor jail detail hooks: split into useJailData and useJailCommands
- Split monolithic useJailDetail hook into separate concerns
- Created useJailData for fetching and managing jail data
- Created useJailCommands for jail operations (power, console, etc.)
- Updated JailDetailPage to use new hooks
- Updated tests to reflect new hook structure
- Removed old useJailDetail hook

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-25 19:14:16 +02:00
8d30a81346 feat(hooks): consolidate data-fetching patterns with useListData and usePolledData
- Refactor useJails (useJailList.ts) to use useListData with onSuccess for total
- Refactor useBanTrend to use useListData with onSuccess for bucket_size
- Refactor useDashboardCountryData to use useListData with onSuccess for aggregated data
- Refactor useHistory to use useListData with proper abort guard in finally()
- Create usePolledData for single-item endpoints with polling and window focus refetch
- Refactor useServerStatus to use usePolledData for 30s polling + window focus refetch
- Keep useIpHistory with manual pattern (single-item, no list semantics)
- Document deferred refactoring of useJailDetail (depends on T-13 for data/command split)

All data-fetching hooks now follow one of two consistent patterns:
1. useListData: for paginated/list endpoints with refresh semantics
2. usePolledData: for single-item endpoints with polling and focus-refetch

This eliminates code duplication, centralizes abort-guard logic, and enables
consistent fixes across all data-fetching hooks.

Resolves T-12.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-25 19:08:26 +02:00
ac44bab8e6 Update documentation and refactor useAutoSave hook
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-23 09:52:22 +02:00
9c5757eeb0 Refactor useHistory hook: replace HistoryQuery with explicit parameters and add documentation
- Split useHistory interface to accept explicit parameters (page, pageSize, range, origin, jail, ip, source) instead of HistoryQuery object
- Add comprehensive JSDoc for useHistory function
- Update HistoryPage and tests to use new parameter structure
- Move TaskList documentation from Tasks.md to Web-Development.md
- Improve type safety with explicit TimeRange and BanOriginFilter types

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-23 09:51:16 +02:00
8904e180d1 Fix: Prevent session-expiry errors from briefly showing in useConfigItem.save()
When save() encounters a 401 or 403 error, the HTTP client dispatches
SESSION_EXPIRED_EVENT which triggers auth handling and navigation to login.
However, setSaveError was called first, causing a brief flash of an
'Unauthorized' message before the redirect.

Now, isAuthError(err) checks if the error is a 401/403 before setting
saveError. Auth errors are rethrown without setting error state, allowing
the auth handler to deal with session expiry cleanly without UX confusion.

- Import isAuthError from api/client in useConfigItem hook
- Check for auth errors in the save() catch block before setSaveError
- Add tests for 401 and 403 error handling

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-23 09:45:11 +02:00
9375430e02 Refactor schedule functionality in frontend
- Extract schedule logic into custom useSchedule hook
- Update BlocklistScheduleSection to use the new hook
- Add tests for useSchedule hook
- Update documentation with task progress

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-23 09:40:19 +02:00
10c534d090 Stabilize function references in useJails with useCallback
Previously, the withRefresh helper and all operations (startJail, stopJail, setIdle, reloadJail, reloadAll) were recreated on every render because they were defined in the hook body without useCallback. This caused unnecessary re-renders of child components using React.memo when parent state changed.

Now each operation is wrapped in useCallback with [load] as its dependency. This ensures function references remain stable between renders, allowing React.memo optimizations to work correctly in JailOverviewSection.

Tests confirm that function references are now stable between consecutive renders.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-23 09:18:31 +02:00
1bcc336c9b Add tests and documentation updates for log preview and regex tester hooks
- Add useLogPreview.test.ts with comprehensive test coverage
- Add useRegexTester.test.ts with comprehensive test coverage
- Update Docs/Tasks.md and Docs/Web-Development.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-23 09:14:58 +02:00
584588e363 fix: add AbortController and unmount guard to useIpLookup hook
- Add AbortController to cancel pending IP lookups when component unmounts
- Prevent state updates on unmounted components by checking abort signal before setState calls
- Add useEffect cleanup that aborts pending requests on unmount
- Update lookupIp API function to accept optional AbortSignal parameter
- Converts callback-based fetch to async/await pattern for better control flow
- Aligns with other API functions that already support abort signals (fetchJails, fetchJailBannedIps)

Fixes TASK-ABORT-04

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-23 08:43:11 +02:00
4e3f2005f9 fix: capture AbortController in local variable to avoid race condition in three hooks
TASK-ABORT-03: Fix stale abortRef read in .finally() callbacks

In useGlobalConfig, useServerSettings, and useJailConfigDetail hooks, the
.finally() block was reading abortRef.current instead of using the locally
captured controller reference. If load() is called while a fetch is in flight,
the previous fetch's .finally() would read the new controller (not aborted)
and prematurely clear the loading state while the new fetch is still pending.

Changes:
- useGlobalConfig.ts: use locally-captured ctrl in .finally() (line 46)
- useServerSettings.ts: use locally-captured ctrl in .finally() (line 50)
- useJailConfigDetail.ts: use locally-captured ctrl in .finally() (line 47)

All three hooks already use ctrl correctly in .then() and .catch() callbacks.

Documentation:
- Add 'AbortController in Hooks' section to Web-Development.md
- Explains the pattern and shows incorrect vs correct examples
- Prevents future regressions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-23 08:40:03 +02:00
0223cb12a4 fix(hooks): Forward abort signal to fetchBansByCountry in useDashboardCountryData
The useDashboardCountryData hook was creating an AbortController and checking
signal.aborted in callbacks, but was not passing the signal to the fetchBansByCountry
API call. This meant the HTTP request itself was never actually aborted.

Now the signal is forwarded, allowing proper request cancellation when the hook
unmounts or dependencies change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-23 08:36:27 +02:00
5a6cb640d8 Add AbortSignal support to API functions for request cancellation
Add optional signal?: AbortSignal parameter to all API GET functions so they can be
cancelled when components unmount. This prevents state-update warnings and wasted
resources.

Changes:
- frontend/src/api/history.ts: fetchHistory, fetchIpHistory
- frontend/src/api/map.ts: fetchBansByCountry
- frontend/src/api/jails.ts: fetchJails, fetchActiveBans
- frontend/src/api/config.ts: fetchJailConfig, fetchInactiveJails, fetchJailConfigFiles,
  fetchFilterFiles (threads signal through fetchFilters), fetchFilterFile, fetchActionFiles,
  fetchActionFile
- frontend/src/api/blocklist.ts: fetchImportLog, previewBlocklist

Updated all calling hooks to pass the abort signal from their controllers:
- useHistory, useIpHistory
- useMapData
- useActiveBans
- useJails
- useConfigActiveStatus (fetchJails and fetchJailConfigs)
- useJailAdmin (fetchInactiveJails)
- useJailConfigDetail (fetchJailConfig)
- useImportLog (fetchImportLog)
- useBlocklists (previewBlocklist with AbortController)

Updated Docs/Web-Development.md to document the convention.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-23 08:33:28 +02:00
1d50bc1a73 fix: add validation error handling to InactiveJailDetail
- Add validationError state to show network/API failures to user
- Use handleFetchError to properly handle auth errors (suppress generic error banner, trigger session-expiry flow)
- Clear validationError when user clicks Validate again
- Ensure error MessageBar renders instead of success banner when validation fails
- Fix InactiveJailDetail onValidate to return Promise as expected by prop type
- Fix useJailConfigs test to use correct JailConfig interface

Fixes TASK-BUG-05: prevents silent validation failures where user cannot distinguish between clean 'no issues' result and server error.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-23 08:02:24 +02:00
0f261e31c2 Fix infinite re-fetch loop in useJailConfigs
The hook was passing an inline onSuccess callback to useListData, which
included onSuccess in its internal refresh function's dependency array.
This caused refresh to be recreated on each render, which triggered the
useEffect, which fired the fetch, which completed and caused a re-render,
creating an infinite loop.

Wrap onSuccess in useCallback with empty dependencies so it maintains a
stable reference across renders. This allows refresh to be stable when
its dependencies don't change, breaking the cycle.

Add documentation to Refactoring.md explaining the onSuccess stability
requirement for useListData callers.

Also add tests for useJailConfigs to verify it doesn't trigger infinite
refetches with stable onSuccess callback.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-22 21:16:42 +02:00
b6d9c649ca Delete hook barrel files and switch to direct hook imports 2026-04-21 20:02:50 +02:00
b3eb5dc6ec Standardise loading state naming across dashboard hooks 2026-04-21 19:12:43 +02:00
e244a85291 Extract generic useListData hook for shared list fetching 2026-04-21 17:53:58 +02:00
e683108965 Standardise AbortController cancellation in setup and server health hooks
Add abortable API signals for setup status and server health/log fetches, document hook cancellation patterns, and cover stale refresh cancellation with tests.
2026-04-21 17:38:35 +02:00
cf5a000bf5 Add AbortSignal support to dashboard/blocklist APIs and hooks 2026-04-21 17:29:05 +02:00
69d5cffabd Remove duplicate api/file_config.ts and consolidate raw file APIs into api/config.ts 2026-04-20 20:19:20 +02:00
8b4a2f0b71 Fix useMapData debounce loading state 2026-04-20 20:10:48 +02:00
5446f6c3e1 Fix jail banned IP loading race with AbortController 2026-04-19 19:31:03 +02:00
7fb0cc727f Surface setup error state instead of console.warn in useSetup 2026-04-19 18:53:02 +02:00
b6303cff72 Remove production test scaffolding from useMapData and update MapPage tests 2026-04-19 18:47:29 +02:00
d44a667592 Fix unsafe frontend casts and mark Task 18 done 2026-04-19 18:25:32 +02:00
6c053cdaee Add AbortController cleanup to async frontend effects 2026-04-18 21:30:57 +02:00
2105f8b435 Task 11: Remove direct API calls from components 2026-04-18 21:20:45 +02:00
3f197b1ad7 Split multi-hook frontend modules into single-hook files 2026-04-18 20:47:44 +02:00
fba7675eb8 Move auth and timezone hooks into dedicated hook files 2026-04-18 20:35:28 +02:00
d9550ae4aa Split frontend config API into file_config, server, and health modules 2026-04-18 20:32:38 +02:00
c51858ec71 Add country-specific companion table filtering for map page 2026-04-05 22:12:06 +02:00
ffaa14f864 Switch dashboard/map/history views to archive source for long-term data
Update fail2ban dbpurgeage to 648000 and history sync backfill/pagination for archive-based 7.5 day history.
2026-04-05 20:21:54 +02:00
6f15e1fa24 fix(map): add test-only useMapData exports for MapPage mock 2026-03-29 21:20:54 +02:00
9e43282bbc fix(config): stabilize config hook callbacks to prevent action/filter flicker 2026-03-24 20:13:23 +01:00