Files
BanGUI/Docs/Tasks.md
Lukas 4be2469f92 Implement tasks 1-3: sidebar order, jail activation rollback, pie chart colors
Task 1: Move Configuration to last position in sidebar NAV_ITEMS

Task 2: Add automatic rollback when jail activation fails
- Back up .local override file before writing
- Restore original file (or delete) on reload failure, health-check
  failure, or jail not appearing post-reload
- Return recovered=True/False in JailActivationResponse
- Show warning/critical banner in ActivateJailDialog based on recovery
- Add _restore_local_file_sync and _rollback_activation_async helpers
- Add 3 new tests: rollback on reload failure, health-check failure,
  and double failure (recovered=False)

Task 3: Color pie chart legend labels to match their slice color
- legendFormatter now returns ReactNode with span style={{ color }}
- Import LegendPayload from recharts/types/component/DefaultLegendContent
2026-03-14 21:16:58 +01:00

6.7 KiB

BanGUI — Task List

This document breaks the entire BanGUI project into development stages, ordered so that each stage builds on the previous one. Every task is described in prose with enough detail for a developer to begin work. References point to the relevant documentation.


Task 1 — Move "Configuration" to the Last Position in the Sidebar DONE

Summary: Moved the Configuration entry in NAV_ITEMS to the last position in frontend/src/layouts/MainLayout.tsx.

File: frontend/src/layouts/MainLayout.tsx

The NAV_ITEMS array (around line 183) defines the sidebar menu order. Currently the order is: Dashboard, World Map, Jails, Configuration, History, Blocklists. Move the Configuration entry so it is the last element in the array. The resulting order must be:

  1. Dashboard
  2. World Map
  3. Jails
  4. History
  5. Blocklists
  6. Configuration

Only the position in the array changes. Do not modify the label, path, or icon of any item.


Task 2 — Auto-Recovery When Jail Activation Fails DONE

Summary: Added recovered: bool | None field to JailActivationResponse model. Implemented _restore_local_file_sync and _rollback_activation_async helpers. Updated activate_jail to back up the original .local file, roll back on any post-write failure (reload error, health-check failure, or jail not starting), and return recovered=True/False. Updated ActivateJailDialog.tsx to show warning/critical banners based on the recovered field. Added 3 new backend tests covering all rollback scenarios.

Context: When a user activates a jail via POST /api/config/jails/{name}/activate, the backend writes enabled = true to jail.d/{name}.local and then reloads fail2ban. If the new configuration is invalid or the server crashes after reload, fail2ban stays broken and all jails go offline. The system must automatically recover by rolling back the change and restarting fail2ban.

Backend Changes

File: backend/app/services/config_file_service.pyactivate_jail() method (around line 1086)

Wrap the reload-and-verify sequence in error handling that performs a rollback on failure:

  1. Before writing the .local override file, check whether a .local file for that jail already exists. If it does, read and keep its content in memory as a backup. If it does not exist, remember that no file existed.
  2. Write the override file with enabled = true (existing logic).
  3. Reload fail2ban via jail_service.reload_all() (existing logic).
  4. Health-check / verify that fail2ban is responsive and the jail appears in the active list (existing logic).
  5. If any step after the write fails (reload error, health-check timeout, jail not appearing):
    • Rollback the config: restore the original .local file content (or delete the file if it did not exist before).
    • Restart fail2ban: call jail_service.reload_all() again so fail2ban recovers with the old configuration.
    • Health-check again to confirm fail2ban is back.
    • Return an appropriate error response (HTTP 502 or 500) with a message that explains the activation failed and the system was recovered. Include a field recovered: true in the JSON body so the frontend can display a recovery notice.
  6. If rollback itself fails, return an error with recovered: false so the frontend can display a critical alert.

File: backend/app/routers/config.pyactivate_jail endpoint (around line 584)

Propagate the recovered field in the error response. No extra logic is needed in the router if the service already raises an appropriate exception or returns a result object with the recovery status.

Frontend Changes

File: frontend/src/components/config/JailsTab.tsx (or wherever the activate mutation result is handled)

When the activation API call returns an error:

  • If recovered is true, show a warning banner/toast: "Activation of jail '{name}' failed. The server has been automatically recovered."
  • If recovered is false, show a critical error banner/toast: "Activation of jail '{name}' failed and automatic recovery was unsuccessful. Manual intervention is required."

Tests

Add or extend tests in backend/tests/test_services/test_config_file_service.py:

  • test_activate_jail_rollback_on_reload_failure: Mock jail_service.reload_all() to raise on the first call (activation reload) and succeed on the second call (recovery reload). Assert the .local file is restored to its original content and the response indicates recovered: true.
  • test_activate_jail_rollback_on_health_check_failure: Mock the health check to fail after reload. Assert rollback and recovery.
  • test_activate_jail_rollback_failure: Mock both the activation reload and the recovery reload to fail. Assert the response indicates recovered: false.

Task 3 — Match Pie Chart Slice Colors to Country Label Font Colors DONE

Summary: Updated legendFormatter in TopCountriesPieChart.tsx to return React.ReactNode instead of string, using <span style={{ color: entry.color }}> to colour each legend label to match its pie slice. Imported LegendPayload from recharts/types/component/DefaultLegendContent.

Context: The dashboard's Top Countries pie chart (frontend/src/components/TopCountriesPieChart.tsx) uses a color palette from frontend/src/utils/chartTheme.ts for the pie slices. The country names displayed in the legend next to the chart currently use the default text color. They should instead use the same color as their corresponding pie slice.

Changes

File: frontend/src/components/TopCountriesPieChart.tsx

In the <Legend> component (rendered by Recharts), the formatter prop already receives the legend entry value. Apply a custom renderer so each country name is rendered with its matching slice color as the font color. The Recharts <Legend> accepts a formatter function whose second argument is the entry object containing the color property. Use that color to wrap the text in a <span> with style={{ color: entry.color }}. Example:

formatter={(value: string, entry: LegendPayload) => {
  const slice = slices.find((s) => s.name === value);
  if (slice == null || total === 0) return value;
  const pct = ((slice.value / total) * 100).toFixed(1);
  return (
    <span style={{ color: entry.color }}>
      {value} ({pct}%)
    </span>
  );
}}

Make sure the formatter return type is ReactNode (not string). Import the Recharts Payload type if needed: import type { Payload } from "recharts/types/component/DefaultLegendContent" . Adjust the import path to match the Recharts version in the project.

Do not change the pie slice colors themselves — only the country label font color must match the slice it corresponds to.