- .github/agents/ProcessTasks.agent.md: Copilot agent definition - eslint.config.ts: minor lint rule adjustment - Docs/Tasks.md: update completed and in-progress task status
221 lines
12 KiB
Markdown
221 lines
12 KiB
Markdown
# 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.
|
|
|
|
---
|
|
|
|
## Config View Redesign — List/Detail Layout with Active Status and Raw Export
|
|
|
|
### Overview
|
|
|
|
Redesign the Jails, Filters, and Actions tabs on the Configuration page (`frontend/src/pages/ConfigPage.tsx`) to use a **master/detail list layout** instead of the current accordion pattern. The left pane shows a scrollable list of config items (jail names, filter names, action names). Each item displays an active/inactive badge. Active items sort to the top. Clicking an item shows its structured form editor in the right pane, with a collapsible raw-text export section appended at the bottom.
|
|
|
|
Use only **Fluent UI React v9** (`@fluentui/react-components`, `@fluentui/react-icons`) components as specified in [Web-Design.md](Web-Design.md) and [Web-Development.md](Web-Development.md). No additional UI libraries.
|
|
|
|
### References
|
|
|
|
- [Web-Design.md](Web-Design.md) — Design rules: Fluent UI components, tokens, spacing, accessibility.
|
|
- [Web-Development.md](Web-Development.md) — Code rules: TypeScript strict, `makeStyles`, component structure, hooks, API layer.
|
|
- Fluent UI v9 docs: https://github.com/microsoft/fluentui — components reference.
|
|
|
|
---
|
|
|
|
### Task A — Shared List/Detail Layout Component
|
|
|
|
**File:** `frontend/src/components/config/ConfigListDetail.tsx`
|
|
|
|
Create a reusable layout component that renders a two-pane master/detail view.
|
|
|
|
**Left pane (list):**
|
|
- Fixed width ~280 px, full height of the tab content area, with its own vertical scroll.
|
|
- Use a vertical stack of clickable items. Each item is a Fluent UI `Card` (or a simple styled `div` with `tokens.colorNeutralBackground2` on hover) displaying:
|
|
- The config **name** (e.g. `sshd`, `iptables-multiport`), truncated with ellipsis + tooltip for long names.
|
|
- A Fluent UI `Badge` to the right of the name: **"Active"** (`appearance="filled"`, `color="success"`) or **"Inactive"** (`appearance="outline"`, `color="informative"`).
|
|
- The selected item gets a left border accent (`tokens.colorBrandBackground`) and a highlighted background (`tokens.colorNeutralBackground1Selected`).
|
|
- Items are sorted: **active items first**, then inactive, alphabetical within each group.
|
|
- Keyboard navigable (arrow keys, Enter to select). Follow the accessibility rules from Web-Design.md §15.
|
|
|
|
**Right pane (detail):**
|
|
- Takes remaining width. Renders whatever `children` or render-prop content the parent tab passes for the currently selected item.
|
|
- Displays an empty state (`"Select an item from the list"`) when nothing is selected.
|
|
- Uses `Skeleton` / `Spinner` while the detail is loading.
|
|
|
|
**Props interface (suggestion):**
|
|
|
|
```typescript
|
|
interface ConfigListDetailProps<T extends { name: string }> {
|
|
items: T[];
|
|
isActive: (item: T) => boolean;
|
|
selectedName: string | null;
|
|
onSelect: (name: string) => void;
|
|
loading: boolean;
|
|
error: string | null;
|
|
children: React.ReactNode; // detail content for selected item
|
|
}
|
|
```
|
|
|
|
**Styles:** Add new style slots to `frontend/src/components/config/configStyles.ts`:
|
|
- `listDetailRoot` — flex row, gap `tokens.spacingHorizontalM`.
|
|
- `listPane` — fixed width 280 px, overflow-y auto, border-right `tokens.colorNeutralStroke2`.
|
|
- `listItem` — padding, hover background, cursor pointer.
|
|
- `listItemSelected` — left brand-colour border, selected background.
|
|
- `detailPane` — flex 1, overflow-y auto, padding.
|
|
|
|
Responsive: On screens < 900 px, collapse the list pane into a `Dropdown` / `Select` above the detail pane so it doesn't take too much horizontal space.
|
|
|
|
---
|
|
|
|
### Task B — Active Status for Jails, Filters, and Actions
|
|
|
|
Determine "active" status for each config type so it can be shown in the list.
|
|
|
|
**Jails:**
|
|
- The existing `JailConfig` type does not carry an `enabled` flag directly. The jails API (`GET /api/jails`) returns `JailSummary` objects which contain `enabled: boolean`. To get the active status, fetch the jails list from `fetchJails()` (in `api/jails.ts`) alongside the jail configs from `fetchJailConfigs()`. Merge the two by name: a jail is "active" if `enabled === true` in the jails list.
|
|
- Alternatively, check the `JailConfigFile` entries from the jail files API which have `enabled: boolean` — determine which data source is more reliable for showing runtime active state.
|
|
|
|
**Filters:**
|
|
- A filter is "active" if it is referenced by at least one active jail. After fetching jail configs (`JailConfig[]`), collect all unique filter names used by enabled jails. Cross-reference with the filter files list (`ConfFileEntry[]`). Mark used ones as active.
|
|
- This requires correlating data: jail config has a `name` field that often matches the filter name (convention in fail2ban: jail name = filter name unless overridden). For accuracy, the filter name a jail uses can be inferred from the jail name or, if the backend provides a `filter` field on `JailConfig`, from that.
|
|
|
|
**Actions:**
|
|
- An action is "active" if it is referenced by at least one active jail's `actions` array. After fetching jail configs, collect all unique action names from `JailConfig.actions[]` arrays of enabled jails. Cross-reference with action files.
|
|
|
|
**Implementation:**
|
|
- Create a new hook `frontend/src/hooks/useConfigActiveStatus.ts` that:
|
|
1. Fetches jails list (`fetchJails`), jail configs (`fetchJailConfigs`), filter files (`fetchFilterFiles`), and action files (`fetchActionFiles`) in parallel.
|
|
2. Computes and returns `{ activeJails: Set<string>, activeFilters: Set<string>, activeActions: Set<string>, loading: boolean, error: string | null }`.
|
|
3. Cache results and re-fetch on demand (expose a `refresh` function).
|
|
- The hook is consumed by the three tabs so each tab knows which items are active.
|
|
|
|
---
|
|
|
|
### Task C — Redesign JailsTab to List/Detail Layout
|
|
|
|
**File:** `frontend/src/components/config/JailsTab.tsx`
|
|
|
|
Replace the current `Accordion`-based layout with the `ConfigListDetail` component from Task A.
|
|
|
|
1. Fetch jail configs via `useJailConfigs` (already exists in `hooks/useConfig.ts`).
|
|
2. Use the active-status hook from Task B to get `activeJails`.
|
|
3. Render `ConfigListDetail` with:
|
|
- `items` = jail config list.
|
|
- `isActive` = `(jail) => activeJails.has(jail.name)`.
|
|
- `onSelect` updates `selectedName` state.
|
|
4. The right-pane detail content is the **existing `JailAccordionPanel` logic** (the form fields for ban_time, find_time, max_retry, regex patterns, log paths, escalation, etc.) — extract it into a standalone `JailConfigDetail` component if not already. Remove the accordion wrapper; it is just the form body now.
|
|
5. **Export section** (new, at the bottom of the detail pane):
|
|
- A collapsible section (use `Accordion` with a single item, or a `Button` toggle) titled **"Raw Configuration"**.
|
|
- Contains a Fluent UI `Textarea` (monospace font, full width, ~20 rows) pre-filled with the raw plain-text representation of the jail config. The raw text is fetched via the existing `fetchJailConfig` or `fetchJailConfigFile` endpoint that returns the file content as a string.
|
|
- The textarea is **editable**: the user can modify the raw text and click a "Save Raw" button to push the changes back via the existing `updateJailConfigFile` PUT endpoint.
|
|
- Show an `AutoSaveIndicator` or manual save button + success/error `MessageBar`.
|
|
|
|
---
|
|
|
|
### Task D — Redesign FiltersTab to List/Detail Layout
|
|
|
|
**File:** `frontend/src/components/config/FiltersTab.tsx`
|
|
|
|
Replace the current `Accordion`-based layout with `ConfigListDetail`.
|
|
|
|
1. Fetch filter file list via `fetchFilterFiles` (already used).
|
|
2. Use `activeFilters` from the active-status hook (Task B).
|
|
3. Render `ConfigListDetail` with:
|
|
- `items` = filter file entries.
|
|
- `isActive` = `(f) => activeFilters.has(f.name)`.
|
|
4. On item select, lazily load the parsed filter via `useFilterConfig` (already exists in `hooks/useFilterConfig.ts`).
|
|
5. Right pane renders the **existing `FilterForm`** component with the loaded config.
|
|
6. **Export section** at the bottom of the detail pane:
|
|
- Collapsible "Raw Configuration" section.
|
|
- `Textarea` (monospace) pre-filled with the raw file content fetched via `fetchFilterFile(name)` (returns `ConfFileContent` with a `content: string` field).
|
|
- Editable with a "Save Raw" `Button` that calls `updateFilterFile(name, { content })`.
|
|
- Success/error feedback via `MessageBar`.
|
|
|
|
---
|
|
|
|
### Task E — Redesign ActionsTab to List/Detail Layout
|
|
|
|
**File:** `frontend/src/components/config/ActionsTab.tsx`
|
|
|
|
Same pattern as Task D but for actions.
|
|
|
|
1. Fetch action file list via `fetchActionFiles`.
|
|
2. Use `activeActions` from the active-status hook (Task B).
|
|
3. Render `ConfigListDetail` with:
|
|
- `items` = action file entries.
|
|
- `isActive` = `(a) => activeActions.has(a.name)`.
|
|
4. On item select, lazily load the parsed action via `useActionConfig` (already exists).
|
|
5. Right pane renders the **existing `ActionForm`** component.
|
|
6. **Export section** at the bottom:
|
|
- Collapsible "Raw Configuration" section.
|
|
- `Textarea` (monospace) with raw file content from `fetchActionFile(name)`.
|
|
- "Save Raw" button calling `updateActionFile(name, { content })`.
|
|
- Feedback messages.
|
|
|
|
---
|
|
|
|
### Task F — Raw Export Section Component
|
|
|
|
**File:** `frontend/src/components/config/RawConfigSection.tsx`
|
|
|
|
Extract the raw-export pattern into a reusable component so Jails, Filters, and Actions tabs don't duplicate logic.
|
|
|
|
**Props:**
|
|
|
|
```typescript
|
|
interface RawConfigSectionProps {
|
|
/** Async function that returns the raw file content string. */
|
|
fetchContent: () => Promise<string>;
|
|
/** Async function that saves updated raw content. */
|
|
saveContent: (content: string) => Promise<void>;
|
|
/** Label shown in the collapsible header, e.g. "Raw Jail Configuration". */
|
|
label?: string;
|
|
}
|
|
```
|
|
|
|
**Behaviour:**
|
|
- Renders a collapsible section (single `AccordionItem` or a disclosure `Button`).
|
|
- When expanded for the first time, calls `fetchContent()` and fills the `Textarea`.
|
|
- Uses monospace font (`fontFamily: "monospace"`) and a left brand-colour accent border (reuse `styles.codeInput` from `configStyles.ts`).
|
|
- "Save Raw" `Button` with `appearance="primary"` calls `saveContent(text)`.
|
|
- Shows `AutoSaveIndicator`-style feedback (idle → saving → saved / error).
|
|
- The `Textarea` is resizable vertically, minimum 15 rows.
|
|
|
|
---
|
|
|
|
### Task G — Update Barrel Exports and ConfigPage
|
|
|
|
1. **`frontend/src/components/config/index.ts`** — Add exports for `ConfigListDetail` and `RawConfigSection`.
|
|
2. **`frontend/src/pages/ConfigPage.tsx`** — No structural changes needed if the tabs internally switch to the new layout. Verify the page still renders all tabs correctly.
|
|
3. **`frontend/src/components/config/configStyles.ts`** — Add the new style slots described in Task A. Do not remove existing styles that may still be used by other tabs (Global, Server, Map, Regex Tester, Export).
|
|
|
|
---
|
|
|
|
### Task H — Testing and Validation
|
|
|
|
1. **Type-check:** Run `npx tsc --noEmit` — zero errors.
|
|
2. **Lint:** Run `npm run lint` — zero warnings.
|
|
3. **Existing tests:** Run `npx vitest run` — all existing tests pass.
|
|
4. **Manual verification:**
|
|
- Navigate to Config → Jails tab. List pane shows jail names with active badges. Active jails appear at the top. Click a jail → right pane shows configuration form + collapsible raw editor.
|
|
- Navigate to Config → Filters tab. Same list/detail pattern. Active filters (used by running jails) show "Active" badge.
|
|
- Navigate to Config → Actions tab. Same pattern.
|
|
- Resize window below 900 px — list collapses to a dropdown selector above the detail.
|
|
- Keyboard: Tab into the list, arrow-key navigate, Enter to select.
|
|
5. **New tests (optional but recommended):**
|
|
- Unit test `ConfigListDetail` rendering with mock items, verifying sort order (active first) and selection callback.
|
|
- Unit test `RawConfigSection` with mocked fetch/save functions.
|
|
|
|
---
|
|
|
|
### Implementation Order
|
|
|
|
1. Task F (RawConfigSection) — standalone, no dependencies.
|
|
2. Task A (ConfigListDetail layout) — standalone component.
|
|
3. Task B (useConfigActiveStatus hook) — needs only the existing API layer.
|
|
4. Task C (JailsTab redesign) — depends on A, B, F.
|
|
5. Task D (FiltersTab redesign) — depends on A, B, F.
|
|
6. Task E (ActionsTab redesign) — depends on A, B, F.
|
|
7. Task G (exports and wiring) — after C/D/E.
|
|
8. Task H (testing) — last.
|
|
|
|
---
|