Compare commits
3 Commits
v0.9.17
...
fix/worldm
| Author | SHA1 | Date | |
|---|---|---|---|
| c03a5c1cbc | |||
| eb983799cd | |||
| d3f564d66f |
@@ -1 +1 @@
|
|||||||
v0.9.17
|
v0.9.18
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ The main landing page after login. Shows recent ban activity at a glance.
|
|||||||
- Last 7 days (week)
|
- Last 7 days (week)
|
||||||
- Last 30 days (month)
|
- Last 30 days (month)
|
||||||
- Last 365 days (year)
|
- Last 365 days (year)
|
||||||
|
- **Data source selection:** The "Last 24 hours" preset queries fail2ban's live database directly for real-time accuracy. All longer presets (7 days, 30 days, 365 days) query the BanGUI long-term archive, because fail2ban's own database only retains the last 24 hours by default.
|
||||||
|
- A **data-source badge** next to the time-range selector indicates whether the current view is showing **Live (fail2ban DB)** or **Archive (BanGUI DB)** data.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -70,14 +72,16 @@ A geographical overview of ban activity.
|
|||||||
- Colors are smoothly interpolated between the thresholds (e.g., 35 bans shows a yellow-green blend)
|
- Colors are smoothly interpolated between the thresholds (e.g., 35 bans shows a yellow-green blend)
|
||||||
- The color threshold values are configurable through the application settings
|
- The color threshold values are configurable through the application settings
|
||||||
- **Interactive zoom and pan:** Users can zoom in/out using mouse wheel or touch gestures, and pan by clicking and dragging. This allows detailed inspection of densely-affected regions. Zoom controls (zoom in, zoom out, reset view) are provided as overlay buttons in the top-right corner.
|
- **Interactive zoom and pan:** Users can zoom in/out using mouse wheel or touch gestures, and pan by clicking and dragging. This allows detailed inspection of densely-affected regions. Zoom controls (zoom in, zoom out, reset view) are provided as overlay buttons in the top-right corner.
|
||||||
- For every country that has bans, the total count is displayed centred inside that country's borders in the selected time range.
|
- For every country that has bans, the total count is shown only in the country tooltip, not rendered on the map itself.
|
||||||
- Countries with zero banned IPs show no number and no label — they remain blank and transparent.
|
- Countries with zero banned IPs show no tooltip and remain blank and transparent.
|
||||||
- Clicking a country filters the companion table below to show only bans from that country.
|
- Clicking a country filters the companion table below to show only bans from that country.
|
||||||
- Time-range selector with the same quick presets:
|
- Time-range selector with the same quick presets:
|
||||||
- Last 24 hours
|
- Last 24 hours
|
||||||
- Last 7 days
|
- Last 7 days
|
||||||
- Last 30 days
|
- Last 30 days
|
||||||
- Last 365 days
|
- Last 365 days
|
||||||
|
- **Data source selection:** Same rule as the Dashboard — "Last 24 hours" uses the live fail2ban database; all other ranges use the BanGUI archive.
|
||||||
|
- A **data-source badge** is displayed alongside the time-range selector indicating **Live (fail2ban DB)** or **Archive (BanGUI DB)**.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -245,13 +249,15 @@ A page to inspect and modify the fail2ban configuration without leaving the web
|
|||||||
|
|
||||||
## 7. Ban History
|
## 7. Ban History
|
||||||
|
|
||||||
A view for exploring historical ban data stored in the fail2ban database.
|
A view for exploring historical ban data stored in the BanGUI long-term archive.
|
||||||
|
|
||||||
### History Table
|
### History Table
|
||||||
|
|
||||||
- Browse all past bans across all jails, not just the currently active ones.
|
- Browse all past bans across all jails, not just the currently active ones.
|
||||||
- **Columns:** Time of ban, IP address, jail, ban duration, ban count (how many times this IP was banned), country.
|
- **Columns:** Time of ban, IP address, jail, ban duration, ban count (how many times this IP was banned), country.
|
||||||
- Filter by jail, by IP address, or by time range.
|
- Filter by jail, by IP address, or by time range.
|
||||||
|
- The default time range on first load is **Last 7 days** and the data source is always the **BanGUI archive**, ensuring the full retention window is visible regardless of fail2ban's `dbpurgeage` setting.
|
||||||
|
- A **data-source badge** is displayed indicating **Archive (BanGUI DB)**.
|
||||||
- See at a glance which IPs are repeat offenders (high ban count).
|
- See at a glance which IPs are repeat offenders (high ban count).
|
||||||
|
|
||||||
### Per-IP History
|
### Per-IP History
|
||||||
@@ -265,7 +271,7 @@ A view for exploring historical ban data stored in the fail2ban database.
|
|||||||
- On each configured sync cycle (default every 5 minutes), BanGUI reads latest entries from fail2ban `bans` table and appends any new events to BanGUI history storage.
|
- On each configured sync cycle (default every 5 minutes), BanGUI reads latest entries from fail2ban `bans` table and appends any new events to BanGUI history storage.
|
||||||
- Supports both `ban` and `unban` events; audit record includes: `timestamp`, `ip`, `jail`, `action`, `duration`, `origin` (manual, auto, blocklist, etc.), `failures`, `matches`, and optional `country` / `ASN` enrichment.
|
- Supports both `ban` and `unban` events; audit record includes: `timestamp`, `ip`, `jail`, `action`, `duration`, `origin` (manual, auto, blocklist, etc.), `failures`, `matches`, and optional `country` / `ASN` enrichment.
|
||||||
- Includes incremental import logic with dedupe: using unique constraint on (ip, jail, action, timeofban) to prevent duplication across sync cycles.
|
- Includes incremental import logic with dedupe: using unique constraint on (ip, jail, action, timeofban) to prevent duplication across sync cycles.
|
||||||
- Provides backfill mode for initial startup: import last N days (configurable, default 7 days) of existing fail2ban history into BanGUI to avoid dark gaps after restart.
|
- Provides backfill mode for initial startup: import the last 7.5 days of existing fail2ban history into BanGUI to avoid dark gaps after restart. Requires fail2ban's `dbpurgeage` to be set to at least `648000` (7.5 days) — BanGUI ships with this value pre-configured in its Docker setup.
|
||||||
- Includes configurable archive purge policy in BanGUI (default 365 days), separate from fail2ban `dbpurgeage`, to keep app storage bounded while preserving audit data.
|
- Includes configurable archive purge policy in BanGUI (default 365 days), separate from fail2ban `dbpurgeage`, to keep app storage bounded while preserving audit data.
|
||||||
- Expose API endpoints for querying persistent history, with filters for timeframe, jail, origin, IP, and current ban status.
|
- Expose API endpoints for querying persistent history, with filters for timeframe, jail, origin, IP, and current ban status.
|
||||||
- On fail2ban connectivity failure, BanGUI continues serving historical data; next successful sync resumes ingestion without data loss.
|
- On fail2ban connectivity failure, BanGUI continues serving historical data; next successful sync resumes ingestion without data loss.
|
||||||
|
|||||||
161
Docs/Tasks.md
161
Docs/Tasks.md
@@ -8,128 +8,71 @@ Reference: `Docs/Refactoring.md` for full analysis of each issue.
|
|||||||
|
|
||||||
## Open Issues
|
## Open Issues
|
||||||
|
|
||||||
### Replace `react-simple-maps` with `d3-geo` in WorldMap
|
### Backend Architecture
|
||||||
|
|
||||||
The current `WorldMap` component (`frontend/src/components/WorldMap.tsx`) uses the `react-simple-maps` library (`ComposableMap`, `ZoomableGroup`, `Geography`, `useGeographies`). This library wraps d3-geo but adds a heavy abstraction layer and fetches the TopoJSON geography file from a remote CDN at runtime. Replace it with direct d3-geo rendering, following the pattern demonstrated in the reference project at `/media/lukas/Volume/repo/worldmaptest/`.
|
- **Replace the single shared SQLite connection.**
|
||||||
|
- Current startup code opens one `aiosqlite.Connection` and reuses it for every request.
|
||||||
|
- This should be replaced with either a connection pool or request-scoped connections to avoid concurrency and locking issues.
|
||||||
|
- Update request dependencies, application lifecycle, and tests to use the new pattern.
|
||||||
|
|
||||||
Reference: `Docs/Features.md` §4 (World Map View) for the full feature specification.
|
- **Refactor dependency wiring and shared resource management.**
|
||||||
|
- Remove hidden module-level import coupling between routers, services, and shared utilities.
|
||||||
|
- Introduce explicit factories or providers for shared resources such as DB, HTTP client session, scheduler, and settings.
|
||||||
|
- Ensure routers depend on injected providers rather than global state or dynamic imports.
|
||||||
|
|
||||||
**All existing features must be preserved.** The component's public API (`WorldMapProps`) and behaviour must remain identical so that `MapPage.tsx`, `HistoryPage.tsx`, and the existing unit test continue to work after the migration.
|
- **Harden fail2ban integration.**
|
||||||
|
- Remove the `sys.path` hack that locates `fail2ban-master` at runtime.
|
||||||
|
- Replace it with a deterministic packaging or configuration model so the backend does not depend on repository layout.
|
||||||
|
- Refactor `Fail2BanClient` so concurrency control is instance-based and not backed by hidden module globals.
|
||||||
|
|
||||||
---
|
- **Improve startup / setup guard behavior.**
|
||||||
|
- Convert `SetupRedirectMiddleware` from an on-demand DB check into a startup/initialisation guard where possible.
|
||||||
|
- Cache setup completion in a safe way and provide an explicit invalidation path if the application state changes.
|
||||||
|
- Reduce middleware responsibility and avoid DB access during normal request dispatch.
|
||||||
|
|
||||||
#### Task 1 — Swap npm dependencies [DONE]
|
- **Make deployment configuration explicit.**
|
||||||
|
- Move hard-coded environment assumptions such as CORS origins into settings.
|
||||||
|
- Ensure `fail2ban_socket`, `fail2ban_config_dir`, and startup commands are fully configurable via `Settings`.
|
||||||
|
- Document production-ready defaults separately from development defaults.
|
||||||
|
|
||||||
Remove `react-simple-maps` and `@types/react-simple-maps` from `frontend/package.json`. Add the following packages that the new implementation requires:
|
### Reliability and Resilience
|
||||||
|
|
||||||
- `d3-geo` — geographic projection and SVG path generation.
|
- **Add backend lifecycle tests for resource cleanup.**
|
||||||
- `@types/d3-geo` — TypeScript definitions for d3-geo.
|
- Verify startup opens and initialises DB, HTTP session, scheduler, and geo cache correctly.
|
||||||
- `topojson-client` — converts TopoJSON to GeoJSON `FeatureCollection`.
|
- Verify shutdown closes those resources cleanly.
|
||||||
- `@types/topojson-client` — TypeScript definitions for topojson-client.
|
|
||||||
- `world-atlas` — provides the `countries-110m.json` TopoJSON file as a local npm asset (no more CDN fetch at runtime).
|
|
||||||
|
|
||||||
Run `npm install` and verify the lock file updates cleanly.
|
- **Add concurrency/regression coverage for DB and fail2ban socket use.**
|
||||||
|
- Add tests that simulate multiple concurrent requests using the same DB dependency.
|
||||||
|
- Add tests around fail2ban socket retries, protocol errors, and rate limiting.
|
||||||
|
|
||||||
---
|
- **Improve state caching and invalidation.**
|
||||||
|
- Add tests for session cache invalidation on logout.
|
||||||
|
- Add tests for setup completion caching so stale state is never served.
|
||||||
|
|
||||||
#### Task 2 — Rewrite `WorldMap.tsx` to use d3-geo directly [DONE]
|
### Backend Feature Work
|
||||||
|
|
||||||
Rewrite the component so that it renders a plain `<svg>` with `<path>` elements generated by d3-geo instead of the react-simple-maps wrappers. The implementation should follow this approach (as seen in the reference project):
|
- **Document and implement backend-safe environment-driven CORS.**
|
||||||
|
- Add support for production and local development origins through configuration.
|
||||||
|
- Avoid a hardcoded Vite origin in the core app factory.
|
||||||
|
|
||||||
1. **Import the TopoJSON locally** — `import worldData from "world-atlas/countries-110m.json"` instead of fetching from a CDN URL. Use `topojson-client`'s `feature()` to extract the GeoJSON `FeatureCollection` once (memoised).
|
- **Centralise scheduler job registration.**
|
||||||
|
- Refactor APScheduler registration so background tasks are registered through a common lifecycle helper.
|
||||||
|
- Ensure jobs can be discovered, replaced, and tested without requiring implicit `app.state` side effects.
|
||||||
|
|
||||||
2. **Create a projection** — Use `geoMercator()` from d3-geo (matching the current Mercator projection) with `.fitSize([width, height], featureCollection)` to auto-scale. Memoise the projection so it is only recomputed when the geometry changes.
|
- **Strengthen fail2ban error handling and reporting.**
|
||||||
|
- Standardise `502` responses for connection/protocol failures across all endpoints.
|
||||||
|
- Add structured logging for retries and fatal socket failures.
|
||||||
|
- Ensure the UI can distinguish offline fail2ban from internal backend failures.
|
||||||
|
|
||||||
3. **Create a path generator** — `geoPath().projection(projection)`. Memoise.
|
- **Improve documentation of backend responsibilities.**
|
||||||
|
- Keep `Docs/Tasks.md` aligned with the backend architecture review.
|
||||||
|
- Add references to the backend modules, resource lifecycle, and dependency model in the documentation.
|
||||||
|
|
||||||
4. **Render countries** — Map over the GeoJSON features and render a `<path>` element for each country. Use the `ISO_NUMERIC_TO_ALPHA2` lookup (already exists in `frontend/src/data/isoNumericToAlpha2.ts`) to translate the numeric feature id to the alpha-2 code expected by the `countries` prop.
|
### Priority Execution Plan
|
||||||
|
|
||||||
5. **Preserve colour coding** — Continue using `getBanCountColor()` from `frontend/src/utils/mapColors.ts` to compute each country's fill colour based on its ban count and the three threshold props.
|
1. Fix the global SQLite connection pattern and tests.
|
||||||
|
2. Refactor dependency injection / explicit shared resources.
|
||||||
6. **Preserve ban-count labels** — For every country with `count > 0`, compute the centroid with `pathGenerator.centroid(feature)` and render a `<text>` element at that position showing the count. Countries with zero bans must remain blank and transparent (no fill, no label).
|
3. Harden fail2ban client concurrency and packaging.
|
||||||
|
4. Convert setup guard to a safer startup-driven model.
|
||||||
7. **Preserve country selection** — Clicking a country calls `onSelectCountry` with the alpha-2 code (or `null` to deselect). The selected country must receive a distinct brand fill colour, matching the current behaviour.
|
5. Add deployment-safe configuration and production-ready CORS.
|
||||||
|
6. Add lifecycle and concurrency regression tests.
|
||||||
8. **Preserve hover tooltip** — On `mouseenter` / `mousemove` / `mouseleave`, show/hide a tooltip portal (`createPortal` into `document.body`) displaying the country name and ban count. Use the same Fluent UI styled tooltip div that the current implementation uses.
|
|
||||||
|
|
||||||
9. **Preserve keyboard accessibility** — Each country with a known alpha-2 code must have `role="button"`, `tabIndex={0}`, an `aria-label` (`"CC: N ban(s)"`), and `aria-pressed` when selected. `Enter` and `Space` must trigger selection/deselection.
|
|
||||||
|
|
||||||
10. **Use a `viewBox`-based responsive SVG** — Set `viewBox="0 0 {width} {height}"` and `style={{ width: "100%", height: "auto" }}` so the map scales with its container, matching the reference project's approach.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### Task 3 — Implement zoom and pan without `react-simple-maps` [DONE]
|
|
||||||
|
|
||||||
The current implementation relies on `ZoomableGroup` from react-simple-maps for zoom/pan. Reimplement this using a `<g>` wrapper with an SVG `transform` attribute driven by React state:
|
|
||||||
|
|
||||||
1. **State:** Track `zoom` (number, 1–8) and `center` (translate offset `[x, y]`).
|
|
||||||
|
|
||||||
2. **Zoom controls:** Keep the three overlay buttons (Zoom In `+`, Zoom Out `−`, Reset `⟲`) in the top-right corner. Each button adjusts the `zoom` state by ±0.5, clamped to `[1, 8]`. Reset sets zoom to 1 and center to `[0, 0]`.
|
|
||||||
|
|
||||||
3. **Mouse-wheel zoom:** Attach a `wheel` event handler to the SVG that increments/decrements zoom on scroll, zooming toward the cursor position.
|
|
||||||
|
|
||||||
4. **Click-and-drag pan:** Track `mousedown` → `mousemove` → `mouseup` on the SVG to translate the `center` offset. Only pan when the drag exceeds a small threshold (e.g. 3 px) to avoid conflicting with country click events.
|
|
||||||
|
|
||||||
5. **Touch support (stretch goal):** Optionally support pinch-to-zoom and touch-drag for tablet users.
|
|
||||||
|
|
||||||
6. **Apply transform:** Wrap all `<path>` and `<text>` elements in a `<g transform="translate(tx, ty) scale(zoom)">` group. Alternatively, use `d3-zoom` if a more robust implementation is preferred, but keep React as the rendering layer (no d3 DOM manipulation).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### Task 4 — Update hover and selection styles to use CSS transitions [DONE]
|
|
||||||
|
|
||||||
The reference project applies hover highlights via CSS classes (`.country`, `.country.hovered`) with CSS `transition` instead of the react-simple-maps `style={{ default, hover, pressed }}` object. Adopt the same approach:
|
|
||||||
|
|
||||||
- Define CSS classes (or Fluent UI `makeStyles` rules) for default, hovered, and selected states.
|
|
||||||
- Apply the correct class based on component state (`isSelected`, `isHovered`).
|
|
||||||
- Use a CSS `transition` on `fill` and `stroke` for a smooth 150 ms highlight effect.
|
|
||||||
- This avoids the react-simple-maps per-geography style object entirely.
|
|
||||||
|
|
||||||
Ensure the selected state still uses `tokens.colorBrandBackground` / `tokens.colorBrandBackgroundHover` / `tokens.colorBrandBackgroundPressed` from Fluent UI so the map integrates visually with the rest of the application.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### Task 5 — Update the WorldMap unit test [DONE]
|
|
||||||
|
|
||||||
The existing test at `frontend/src/components/__tests__/WorldMap.test.tsx` mocks `react-simple-maps`. After the migration those mocks are invalid. Update the test:
|
|
||||||
|
|
||||||
1. **Remove the `vi.mock("react-simple-maps", ...)` block.**
|
|
||||||
|
|
||||||
2. **Mock the TopoJSON data instead.** Since the new implementation imports `world-atlas/countries-110m.json` directly, mock that module to return a minimal TopoJSON object containing a single country feature (e.g. id `"840"` for the US). Use `topojson-client`'s `feature()` to verify the mock produces a valid GeoJSON feature.
|
|
||||||
|
|
||||||
3. **Keep the same assertions:** tooltip appears on hover with country name and ban count, tooltip disappears on mouse leave, country element has correct ARIA attributes (`role="button"`, `aria-label`, `aria-pressed`).
|
|
||||||
|
|
||||||
4. **Verify zoom controls render:** assert that the three zoom buttons (Zoom In, Zoom Out, Reset) are present and have the correct `aria-label` values.
|
|
||||||
|
|
||||||
5. Also verify that tests in `MapPage.test.tsx` and `HistoryPage.test.tsx` still pass (they mock `WorldMap` at the component level so they should be unaffected, but confirm).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### Task 6 — Remove CDN dependency and verify offline capability [DONE]
|
|
||||||
|
|
||||||
The old implementation fetched geography data from `https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json` at runtime. The new implementation bundles the data via the `world-atlas` npm package, so:
|
|
||||||
|
|
||||||
1. Delete the `GEO_URL` constant.
|
|
||||||
2. Confirm the TopoJSON file is included in the Vite bundle (imported as a JSON module).
|
|
||||||
3. Verify the map renders correctly without any network request for geography data (check the browser network tab or write a test that asserts no fetch calls are made for the old CDN URL).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### Task 7 — Final integration smoke test [DONE]
|
|
||||||
|
|
||||||
After all changes, manually verify the following against the feature specification in `Docs/Features.md` §4:
|
|
||||||
|
|
||||||
- Countries are colour-coded by ban count (transparent → green → yellow → red) using smooth interpolation.
|
|
||||||
- Ban count numbers are displayed centred inside each country that has bans.
|
|
||||||
- Countries with zero bans are transparent with no label.
|
|
||||||
- Clicking a country filters the companion ban table below.
|
|
||||||
- Clicking the same country again deselects it.
|
|
||||||
- Zoom in / zoom out / reset buttons work correctly (range 1×–8×).
|
|
||||||
- Mouse-wheel zoom and click-drag pan work.
|
|
||||||
- Tooltip appears on hover showing country name and localised ban count.
|
|
||||||
- Keyboard navigation works (Tab to focus, Enter/Space to toggle selection).
|
|
||||||
- The map is responsive and scales with the container width.
|
|
||||||
- Time-range selector on `MapPage` still updates the map data correctly.
|
|
||||||
- Colour thresholds from settings are applied (thresholdLow, thresholdMedium, thresholdHigh props).
|
|
||||||
- Run `npm run test` — all existing tests pass.
|
|
||||||
- Run `npm run build` — production build succeeds with no errors or warnings.
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "bangui-backend"
|
name = "bangui-backend"
|
||||||
version = "0.9.15"
|
version = "0.9.18"
|
||||||
description = "BanGUI backend — fail2ban web management interface"
|
description = "BanGUI backend — fail2ban web management interface"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
|||||||
4
frontend/package-lock.json
generated
4
frontend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "bangui-frontend",
|
"name": "bangui-frontend",
|
||||||
"version": "0.9.15",
|
"version": "0.9.17",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "bangui-frontend",
|
"name": "bangui-frontend",
|
||||||
"version": "0.9.15",
|
"version": "0.9.17",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fluentui/react-components": "^9.55.0",
|
"@fluentui/react-components": "^9.55.0",
|
||||||
"@fluentui/react-icons": "^2.0.257",
|
"@fluentui/react-icons": "^2.0.257",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "bangui-frontend",
|
"name": "bangui-frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.9.17",
|
"version": "0.9.18",
|
||||||
"description": "BanGUI frontend — fail2ban web management interface",
|
"description": "BanGUI frontend — fail2ban web management interface",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -346,10 +346,6 @@ export function WorldMap({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const centroid = pathGenerator.centroid(featureItem);
|
|
||||||
const [cx, cy] = centroid;
|
|
||||||
const isCentroidValid = Number.isFinite(cx) && Number.isFinite(cy);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<g key={String(rawId)}>
|
<g key={String(rawId)}>
|
||||||
<path
|
<path
|
||||||
@@ -410,17 +406,6 @@ export function WorldMap({
|
|||||||
setTooltip(null);
|
setTooltip(null);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{count > 0 && isCentroidValid && (
|
|
||||||
<text
|
|
||||||
x={cx}
|
|
||||||
y={cy}
|
|
||||||
textAnchor="middle"
|
|
||||||
dominantBaseline="central"
|
|
||||||
className={styles.countLabel}
|
|
||||||
>
|
|
||||||
{count}
|
|
||||||
</text>
|
|
||||||
)}
|
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Badge, Text, makeStyles, tokens } from "@fluentui/react-components";
|
import { Text, makeStyles, tokens } from "@fluentui/react-components";
|
||||||
import { BanTable } from "../components/BanTable";
|
import { BanTable } from "../components/BanTable";
|
||||||
import { BanTrendChart } from "../components/BanTrendChart";
|
import { BanTrendChart } from "../components/BanTrendChart";
|
||||||
import { ChartStateWrapper } from "../components/ChartStateWrapper";
|
import { ChartStateWrapper } from "../components/ChartStateWrapper";
|
||||||
@@ -95,9 +95,6 @@ export function DashboardPage(): React.JSX.Element {
|
|||||||
originFilter={originFilter}
|
originFilter={originFilter}
|
||||||
onOriginFilterChange={setOriginFilter}
|
onOriginFilterChange={setOriginFilter}
|
||||||
/>
|
/>
|
||||||
<Badge appearance="filled" color={source === "archive" ? "brand" : "success"}>
|
|
||||||
{source === "archive" ? "Archive (BanGUI DB)" : "Live (fail2ban DB)"}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ------------------------------------------------------------------ */}
|
{/* ------------------------------------------------------------------ */}
|
||||||
|
|||||||
@@ -402,8 +402,6 @@ export function HistoryPage(): React.JSX.Element {
|
|||||||
const { items, total, page, loading, error, setPage, refresh } =
|
const { items, total, page, loading, error, setPage, refresh } =
|
||||||
useHistory(appliedQuery);
|
useHistory(appliedQuery);
|
||||||
|
|
||||||
const sourceLabel = "Archive (BanGUI DB)";
|
|
||||||
|
|
||||||
useEffect((): void => {
|
useEffect((): void => {
|
||||||
const nextQuery: HistoryQuery = {
|
const nextQuery: HistoryQuery = {
|
||||||
range,
|
range,
|
||||||
@@ -490,28 +488,8 @@ export function HistoryPage(): React.JSX.Element {
|
|||||||
setIpFilter(value);
|
setIpFilter(value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Badge appearance="filled" color="brand" style={{ alignSelf: "flex-start" }}>
|
|
||||||
{sourceLabel}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ---------------------------------------------------------------- */}
|
|
||||||
{/* Error / loading state */}
|
|
||||||
{/* ---------------------------------------------------------------- */}
|
|
||||||
{error && (
|
|
||||||
<MessageBar intent="error">
|
|
||||||
<MessageBarBody>{error}</MessageBarBody>
|
|
||||||
</MessageBar>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{loading && !error && (
|
|
||||||
<div
|
|
||||||
style={{ display: "flex", justifyContent: "center", padding: tokens.spacingVerticalXL }}
|
|
||||||
>
|
|
||||||
<Spinner label="Loading history…" />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* ---------------------------------------------------------------- */}
|
{/* ---------------------------------------------------------------- */}
|
||||||
{/* Summary */}
|
{/* Summary */}
|
||||||
{/* ---------------------------------------------------------------- */}
|
{/* ---------------------------------------------------------------- */}
|
||||||
|
|||||||
@@ -165,9 +165,6 @@ export function MapPage(): React.JSX.Element {
|
|||||||
setSelectedCountry(null);
|
setSelectedCountry(null);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Badge appearance="filled" color={source === "archive" ? "brand" : "success"}>
|
|
||||||
{source === "archive" ? "Archive (BanGUI DB)" : "Live (fail2ban DB)"}
|
|
||||||
</Badge>
|
|
||||||
<Button
|
<Button
|
||||||
icon={<ArrowCounterclockwiseRegular />}
|
icon={<ArrowCounterclockwiseRegular />}
|
||||||
onClick={(): void => {
|
onClick={(): void => {
|
||||||
|
|||||||
Reference in New Issue
Block a user