From c03a5c1cbcde6f048a889f4c74c7091cc858468f Mon Sep 17 00:00:00 2001 From: Lukas Date: Sun, 5 Apr 2026 21:46:25 +0200 Subject: [PATCH] backup --- Docs/Tasks.md | 161 +++++++++------------------ backend/pyproject.toml | 2 +- frontend/package-lock.json | 4 +- frontend/src/components/WorldMap.tsx | 4 - frontend/src/pages/HistoryPage.tsx | 2 +- 5 files changed, 56 insertions(+), 117 deletions(-) diff --git a/Docs/Tasks.md b/Docs/Tasks.md index d43185d..5a37717 100644 --- a/Docs/Tasks.md +++ b/Docs/Tasks.md @@ -8,128 +8,71 @@ Reference: `Docs/Refactoring.md` for full analysis of each issue. ## 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. -- `@types/d3-geo` — TypeScript definitions for d3-geo. -- `topojson-client` — converts TopoJSON to GeoJSON `FeatureCollection`. -- `@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). +- **Add backend lifecycle tests for resource cleanup.** + - Verify startup opens and initialises DB, HTTP session, scheduler, and geo cache correctly. + - Verify shutdown closes those resources cleanly. -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 `` with `` 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 `` 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. - -6. **Preserve ban-count labels** — For every country with `count > 0`, compute the centroid with `pathGenerator.centroid(feature)` and render a `` element at that position showing the count. Countries with zero bans must remain blank and transparent (no fill, no label). - -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. - -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 `` 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 `` and `` elements in a `` 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. +1. Fix the global SQLite connection pattern and tests. +2. Refactor dependency injection / explicit shared resources. +3. Harden fail2ban client concurrency and packaging. +4. Convert setup guard to a safer startup-driven model. +5. Add deployment-safe configuration and production-ready CORS. +6. Add lifecycle and concurrency regression tests. diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 12dcce8..4caa38f 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "bangui-backend" -version = "0.9.15" +version = "0.9.18" description = "BanGUI backend — fail2ban web management interface" requires-python = ">=3.12" dependencies = [ diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 2aba3b3..23a6b75 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "bangui-frontend", - "version": "0.9.15", + "version": "0.9.17", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bangui-frontend", - "version": "0.9.15", + "version": "0.9.17", "dependencies": { "@fluentui/react-components": "^9.55.0", "@fluentui/react-icons": "^2.0.257", diff --git a/frontend/src/components/WorldMap.tsx b/frontend/src/components/WorldMap.tsx index d771bb1..e102790 100644 --- a/frontend/src/components/WorldMap.tsx +++ b/frontend/src/components/WorldMap.tsx @@ -346,10 +346,6 @@ export function WorldMap({ return null; } - const centroid = pathGenerator.centroid(featureItem); - const [cx, cy] = centroid; - const isCentroidValid = Number.isFinite(cx) && Number.isFinite(cy); - return ( - + {/* ---------------------------------------------------------------- */} {/* Summary */}