- LoginPage now shows a loading spinner while validating the session
- Redirect to dashboard automatically once validation completes and session is valid
- Expose isValidating state through AuthProvider for components to track validation status
- Update useAuth hook to return isValidating along with isAuthenticated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add automated type synchronization from backend OpenAPI schema to frontend TypeScript types to prevent type drift and ensure runtime safety.
Changes:
- Add openapi-typescript as dev dependency
- Create npm scripts for type generation (generate:types) and validation (validate:types)
- Integrate type generation into build pipeline (runs before TypeScript compilation)
- Generate frontend/src/types/generated.ts from backend OpenAPI schema
- Add frontend/scripts/validate-types.sh for CI/CD validation
- Update Web-Development.md with type generation workflow documentation
- Update Backend-Development.md with OpenAPI schema sync requirements
Workflow:
1. Backend automatically exposes OpenAPI schema at /api/openapi.json (FastAPI built-in)
2. Frontend build runs 'npm run generate:types' to generate types from schema
3. Generated types are committed to version control
4. CI can run 'npm run validate:types' to fail builds if types drift
Fixes critical type safety issue where frontend types were manually maintained
and could become out of sync with backend Pydantic models.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- 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>
- Add BroadcastChannel API for real-time logout synchronization across tabs
- Implement storage event listener as fallback for older browsers
- When a user logs out in one tab, all other tabs immediately reflect the logout state
- Update tests to verify storage event and BroadcastChannel behavior
- Update Architecture.md to document cross-tab synchronization
- Update Web-Development.md with authentication state management notes
The provider now broadcasts logout messages to other tabs so they immediately
reflect the logout state without requiring a page refresh or additional API calls.
The implementation uses BroadcastChannel as the primary sync mechanism with
storage events as a fallback for older browsers.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Align frontend and backend error observability with correlation IDs and
structured telemetry for distributed tracing across systems.
Backend changes:
- Add CorrelationIdMiddleware to generate/extract correlation IDs
- Include correlation_id in all ErrorResponse objects
- Store correlation ID in structlog contextvars for automatic inclusion in logs
- Add correlation ID to response headers (X-Correlation-ID)
Frontend changes:
- API client automatically generates session-scoped UUID4 and includes
X-Correlation-ID header in all requests
- Extract correlation ID from API error responses
- Update error handlers to use telemetry with correlation IDs
- Add telemetry logging to ErrorBoundary, PageErrorBoundary, SectionErrorBoundary
- Implement redaction utilities for privacy-safe logging of sensitive data
Documentation:
- Add observability guidelines to Web-Development.md
* Correlation ID usage patterns
* Privacy & security best practices
* Telemetry event structure
* Redaction utilities for sensitive data
- Add distributed tracing architecture section to Architecture.md
* Correlation ID flow across frontend/backend
* Example troubleshooting scenario
* Implementation details for future enhancements
Testing:
- Add comprehensive tests for correlation middleware
- Update error boundary tests to verify telemetry integration
- Verify TypeScript and ESLint pass with no warnings
Fixes: Issue #40 - Frontend and backend observability are not aligned
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- 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>
- Only set Content-Type header for requests with a body (POST, PUT, DELETE with body)
- Only set X-BanGUI-Request CSRF header for mutating methods (POST, PUT, DELETE, PATCH)
- GET, HEAD, OPTIONS requests no longer include unnecessary headers, reducing CORS preflights
- Update Web-Development.md to clarify conditional header behavior
- Add comprehensive tests for header behavior by HTTP method
This reduces unnecessary CORS preflight requests on GET endpoints while maintaining
CSRF protection on state-mutating requests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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>
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>
- Move magic strings from AuthProvider, MainLayout, and ThemeProvider to
frontend/src/utils/constants.ts
- Add STORAGE_KEY_AUTHENTICATED, STORAGE_KEY_SIDEBAR_COLLAPSED, and
STORAGE_KEY_THEME constants with JSDoc descriptions
- Update all three files to import and use centralized keys
- Prevents key drift and typo regressions across the frontend
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add setAuthErrorHandler() registration mechanism to utils/fetchError.ts
- Implement fallback logging when auth errors (401/403) occur without registered handler
- Update AuthProvider to register both API client and fetch error handlers
- Ensure auth errors are handled deterministically at multiple layers
- Add comprehensive tests for auth error handler registration and fallback logging
- Update Web-Development.md documentation with auth error handling contract
Fixes issue #21: Silent auth errors are now caught and logged if the handler is not
registered, preventing actionable errors from being silently swallowed.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Implement standardized skeleton loading placeholders to reduce perceived
loading time and prevent layout shift during data fetches. These components
match actual content dimensions exactly, improving perceived responsiveness.
New skeleton components in src/components/skeletons/:
- SkeletonTable: Table/grid loading with customizable rows and cells
- SkeletonTableRow: Individual animated skeleton row
- SkeletonChart: Chart/graph loading with bars matching dimensions
- SkeletonStat: Stat card loading with label and value
- SkeletonFormField: Form input loading placeholder
- PageLoadingSkeleton: Convenience wrapper for page-level loading states
Implementation details:
- All skeletons use global 'skeleton-pulse' animation (2s cycle)
- Dimensions match real content to prevent layout shift on arrival
- Marked with aria-hidden and role=presentation for accessibility
- Theme-aware colors using Fluent UI tokens
- Respects prefers-reduced-motion setting
Updates:
- ChartStateWrapper: Uses SkeletonChart instead of spinner
- PageFeedback: Added PageLoadingSkeleton component
- App.tsx: Injects skeleton styles at startup
- Web-Design.md: Added § 8a with loading UX guidance and usage examples
All components tested (22 tests, 100% passing) and linted.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This addresses issue #19 by making the implicit provider dependency order
explicit and order-sensitive.
Changes:
1. Created PROVIDER_ORDER.md - comprehensive documentation explaining:
- The provider hierarchy from outermost to innermost
- Why each provider must be at its position
- Order-sensitive pitfalls and what would break
- Guidelines for adding new providers in the future
2. Added provider composition tests (providerComposition.test.tsx):
- 13 comprehensive tests validating provider order and dependencies
- Tests verify all providers mount correctly
- Tests check that hooks only work inside correct providers
- Tests validate async initialization (AuthProvider, TimezoneProvider)
- Tests verify theme persistence and notification propagation
3. Updated App.tsx with inline documentation:
- Added detailed provider order contract in JSDoc header
- Inline comments explaining each provider's position
- Reference to PROVIDER_ORDER.md for detailed rationale
4. Updated Web-Development.md:
- Added new section 5.5 'Provider Order Contract'
- Documents provider hierarchy and rationale
- Links to comprehensive provider documentation
- References regression test suite
All tests pass. TypeScript compilation succeeds. Build succeeds.
The provider order is now explicit and future refactors can validate
compliance through the regression test suite.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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>
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>
- 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>
Include NotificationContainer in test setup so notification messages
appear in the DOM and can be found by tests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Create NotificationService with context provider for centralized error/success messaging
- Add NotificationContainer component to render notification stack
- Integrate NotificationProvider into App root
- Refactor BanUnbanForm to use notification service instead of local error state
- Update fetchError utility to optionally use notification callbacks
- Add comprehensive error handling guidelines to Web-Development.md
- Prevent duplicate notifications with deduplication logic
- Support auto-dismiss with configurable TTL per notification type
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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>
Replace multi-hop prop forwarding with a dedicated JailContext that manages
jail state and actions. This reduces coupling, simplifies the component hierarchy,
and makes the data flow more explicit.
Changes:
- Create JailContext.tsx with JailProvider and useJailContext hook
- Wrap JailsPage content with JailProvider to expose jail state
- Refactor JailOverviewSection to use useJailContext instead of props
- Remove 10 props from JailOverviewSection component signature
- Add comprehensive documentation on state ownership and prop drilling
Benefits:
- Eliminates unnecessary prop chains through intermediate components
- Makes component contracts clearer (no longer need to pass unrelated props)
- Simplifies future refactoring of jail-related functionality
- Sets a pattern for other page-scoped state management
Testing:
- TypeScript type check passes (tsc --noEmit)
- Frontend builds successfully
- Existing JailsPage tests pass with new context structure
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fixes a critical security vulnerability where the session token was
being returned in the JSON response body of POST /api/auth/login.
This exposed the token to JavaScript, allowing malicious scripts to
steal it and bypass the HttpOnly cookie protection.
Changes:
- Backend: Remove 'token' field from LoginResponse model (auth.py)
- Backend: Update login() endpoint to return only 'expires_at'
- Frontend: Update LoginResponse type to exclude 'token' field
- Backend: Update test helper _login() to extract token from cookie
- Backend: Update test cases to verify token is NOT in response body
- Documentation: Add section 'Authentication Endpoints' in Backend-Development.md
- Documentation: Update Web-Development.md to explain HttpOnly cookie benefits
Security benefit: Session tokens are now only accessible via HttpOnly
cookies, protected from JavaScript access, XSS attacks, and malicious
third-party scripts. The frontend continues to use only the cookie for
authentication.
All auth tests pass (23 tests). Type checking and linting pass with
zero errors.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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>
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>
Moved all static layout properties (display, gap, margin, padding, colour)
from inline style props to makeStyles classes in:
- MapBansTable.tsx: Pagination row flexbox layout
- JailDetailPage.tsx: Link styling for textDecoration
- HistoryPage.tsx: Summary text styling
- IpDetailView.tsx: Loading container and text formatting
Kept inline styles only for genuinely dynamic values:
- WorldMap.tsx: Tooltip position (follows mouse)
- TopCountriesPieChart.tsx: Legend color (from recharts data)
- TopCountriesBarChart.tsx: Chart height (derives from data length)
This change improves performance by leveraging Griffel's atomic CSS cache
and ensures consistency with the established Fluent UI pattern.
Updated Docs/Web-Development.md with explicit rule: inline styles only
for runtime-dynamic values, all static properties go in makeStyles.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Move DashboardFilterProvider component and tests from providers/ to pages/
- Update DashboardPage imports to reflect new structure
- Update documentation with latest task progress
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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>
- 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>
- Update AuthProvider with improved error handling and token management
- Enhance API client with better request/response handling
- Add comprehensive test coverage for auth flows
- Update documentation with current tasks
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move reusable UI section components (JailInfoSection, PatternsSection,
BantimeEscalationSection, IgnoreListSection, CodeList) from pages/jail/
to components/jail/, aligning with the project convention that pages/
contains only route-level entry points while components/ contains reusable
UI building blocks.
Changes:
- Move 5 section components + jailDetailPageStyles.ts to components/jail/
- Update import paths in moved components (relative paths to commonStyles)
- Update JailDetailPage.tsx imports to reference components/jail/
- Delete empty pages/jail/ directory
- Document pages/ vs components/ distinction in Web-Development.md
All components use standard import structure and TypeScript passes type
checking. BannedIpsSection was already correctly placed in components/jail/.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- 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>
- 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>
- Replace console.warn with visible MessageBar warning when map color thresholds fail to load
- Add DismissRegular icon button to allow users to dismiss the warning
- Add dismissedThresholdWarning state to manage warning visibility
- Add mock and test for useMapColorThresholds hook
- Add test case verifying warning displays and can be dismissed
- Remove TASK-QUALITY-04 from Tasks.md (completed)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- 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>
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>
Replace the flawed join-based comparison (entryKeys.join(',')) with
JSON.stringify() to properly handle keys containing commas. The previous
implementation could produce false equality when different key sets
shared the same comma-separated representation (e.g., 'a,b' key vs
separate 'a' and 'b' keys).
This ensures the effect fires correctly when keys change, fixing silent
failures to update derived state.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- 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>
- Remove JWT token and expires_at from sessionStorage
- Simplify AuthProvider to use boolean isAuthenticated flag
- Persist only isAuthenticated boolean for page-reload continuity
- Update AuthProvider test to verify new auth model
- Add comprehensive auth documentation to Web-Development.md explaining:
- Cookie-based authentication model
- How frontend auth state persists
- Why tokens are no longer stored
- Error handling flow for 401/403 responses
The authentication model is cookie-based: the backend sets bangui_session
cookie on login, frontend automatically includes it via credentials:
'include', and the backend is the sole authority on session validity.
Previously stored tokens were never actually used and made the auth model
misleading during development.
Fixes TASK-STATE-04.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- BanTable now requires all filter props (timeRange, origin, source)
- Removed useDashboardFilters() hook dependency from BanTable
- Eliminated context fallback chain using ?? operator
- Component now exclusively reads from props, never from context
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- DashboardFilterBar now requires all filter props (timeRange, onTimeRangeChange, originFilter, onOriginFilterChange) instead of falling back to context
- Removed useDashboardFilters() hook dependency from DashboardFilterBar, BanTrendChart, and JailDistributionChart
- Updated DashboardPage to explicitly pass all filter values and callbacks from context to components
- Made props required on BanTrendChart and JailDistributionChart
- Updated all tests to reflect new prop requirements
- This eliminates the silent dual-source behavior that could lead to subtle bugs when components are used with different data sources
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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>
- 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>