Files
BanGUI/Docs/Refactoring.md

9.1 KiB
Raw Blame History

BanGUI — Architecture Issues & Refactoring Plan

This document catalogues architecture violations, code smells, and structural issues found during a full project review. Issues are grouped by category and prioritised.


1. Service-to-Service Coupling (Backend)

The architecture mandates that dependencies flow routers → services → repositories, yet 15 service-to-service imports exist, with 7 using lazy imports to work around circular dependencies.

Source Service Imports From Line Mechanism
history_service ban_service L29 Direct import of 3 private functions: _get_fail2ban_db_path, _parse_data_json, _ts_to_iso
auth_service setup_service L23 Top-level import
config_service setup_service L47 Top-level import
config_service health_service L891 Lazy import inside function
config_file_service jail_service L5758 Top-level import + re-export of JailNotFoundError
blocklist_service jail_service L299 Lazy import
blocklist_service geo_service L343 Lazy import
jail_service geo_service L860, L1047 Lazy import (2 sites)
ban_service geo_service L251, L392 Lazy import (2 sites)
history_service geo_service L19 TYPE_CHECKING import

Impact: Circular dependency risk; lazy imports hide coupling; private function imports create fragile links between services.

Recommendation:

  • Extract _get_fail2ban_db_path(), _parse_data_json(), _ts_to_iso() from ban_service to app/utils/fail2ban_db_utils.py (shared utility).
  • Pass geo-enrichment as a callback parameter instead of each service importing geo_service directly. The router or dependency layer should wire this.
  • Where services depend on another service's domain exceptions (e.g., JailNotFoundError), move exceptions to app/models/ or a shared app/exceptions.py.

2. God Modules (Backend)

Several service files far exceed a reasonable size for a single-domain module:

File Lines Functions Issue
config_file_service.py 3105 73 Handles jails, filters, and actions — three distinct domains crammed into one file
jail_service.py 1382 34 Manages jail listing, status, controls, banned-IP queries, and geo enrichment
config_service.py 921 ~20 Socket-based config, log preview, regex testing, and service status
file_config_service.py 1011 ~20 Raw file I/O for jails, filters, and actions

Recommendation:

  • Split config_file_service.py into filter_config_service.py, action_config_service.py, and a slimmed-down jail_config_service.py.
  • Extract log-preview / regex-test functionality from config_service.py into a dedicated log_service.py.

3. Confusing Config Service Naming (Backend)

Three services with overlapping names handle different aspects of configuration, causing developer confusion:

Current Name Purpose
config_service Read/write via the fail2ban socket
config_file_service Parse/activate/deactivate jails from files on disk
file_config_service Raw file I/O for jail/filter/action .conf files

config_file_service vs file_config_service differ only by word order, making it easy to import the wrong one.

Recommendation: Rename for clarity:

  • config_service → keep (socket-based)
  • config_file_servicejail_activation_service (its main job is activating/deactivating jails)
  • file_config_serviceraw_config_io_service or merge into config_file_service

4. Architecture Doc Drift

The architecture doc does not fully reflect the current codebase:

Category In Architecture Doc Actually Exists Notes
Repositories 4 listed 6 files fail2ban_db_repo.py and geo_cache_repo.py are missing from the doc
Utils 4 listed 8 files conffile_parser.py, config_parser.py, config_writer.py, jail_config.py are undocumented
Tasks 3 listed 4 files geo_re_resolve.py is missing from the doc
Services conffile_parser listed as a service Actually in app/utils/ Doc says "Services" but the file is in utils/
Routers file_config.py not listed Exists Missing from router table

Recommendation: Update the Architecture doc to reflect the actual file inventory.


5. Shared Private Functions Cross Service Boundary (Backend)

history_service.py imports three underscore-prefixed ("private") functions from ban_service.py:

from app.services.ban_service import _get_fail2ban_db_path, _parse_data_json, _ts_to_iso

These are implementation details of ban_service that should not be consumed externally. Their _ prefix signals they are not part of the public API.

Recommendation: Move these to app/utils/fail2ban_db_utils.py as public functions and import from there in both services.


6. Missing Error Boundaries (Frontend)

No React Error Boundary component exists anywhere in the frontend. A single unhandled exception in any component will crash the entire application with a white screen.

Recommendation: Add an <ErrorBoundary> wrapper in MainLayout.tsx or App.tsx with a fallback UI that shows the error and offers a retry/reload.


7. Duplicated Formatting Functions (Frontend)

Several formatting functions are independently defined in multiple files instead of being shared:

Function Defined In Also In
formatTimestamp() BanTable.tsx (L103) — (but fmtTime() in BannedIpsSection.tsx does the same thing)
fmtSeconds() JailDetailPage.tsx (L152) JailsPage.tsx (L147) — identical
fmtTime() BannedIpsSection.tsx (L139)

Recommendation: Consolidate into src/utils/formatDate.ts and import from there.


8. Duplicated Hook Logic (Frontend)

Three hooks follow an identical fetch-then-save pattern with near-identical code:

Hook Lines Pattern
useFilterConfig.ts 91 Load item → expose save → handle abort
useActionConfig.ts 89 Load item → expose save → handle abort
useJailFileConfig.ts 76 Load item → expose save → handle abort

Recommendation: Create a generic useConfigItem<T>() hook that takes fetchFn and saveFn parameters and eliminates the triplication.


9. Inconsistent Error Handling in Hooks (Frontend)

Hooks handle errors differently:

  • Some filter out AbortError (e.g., useHistory, useMapData)
  • Others catch all errors indiscriminately (e.g., useBans, useBlocklist)

This means some hooks surface spurious "request aborted" errors to the UI while others don't.

Recommendation: Standardise a shared error-catching pattern, e.g. a handleFetchError(err, setError) utility that always filters AbortError.


10. No Global Request State / Caching (Frontend)

Each hook manages its own loading/error/data state independently. There is:

  • No request deduplication (two components fetching the same data trigger two requests)
  • No stale-while-revalidate caching
  • No automatic background refetching

Recommendation: Consider adopting React Query (TanStack Query) or SWR for data-fetching hooks. This would eliminate boilerplate in every hook (abort handling, loading state, error state, caching) and provide automatic deduplication.


11. Large Frontend Components

Component Lines Issue
BlocklistsPage.tsx 968 Page does a lot: source list, add/edit dialogs, import log, schedule config
JailsTab.tsx 939 Combines jail list, config editing, raw config, validation, activate/deactivate
JailsPage.tsx 691 Mixes jail table, detail drawer, ban/unban forms
JailDetailPage.tsx 663 Full detail view with multiple sections

Recommendation: Extract sub-sections into focused child components. For example, JailsTab.tsx could delegate to <JailConfigEditor>, <JailValidation>, and <JailActivateDialog>.


12. Duplicated Section Styles (Frontend)

The same card/section styling pattern (backgroundColor, borderRadius, border, padding using Fluent UI tokens) is repeated across 13+ files. Each page recreates it in its own makeStyles block.

Recommendation: Define a shared useCardStyles() or export a sectionStyle in src/theme/commonStyles.ts and import it.


Summary by Priority

Priority Issue Section
High Service-to-service coupling / circular deps §1
High God module config_file_service.py (3105 lines, 73 functions) §2
High Shared private function imports across services §5
Medium Confusing config service naming §3
Medium Architecture doc drift §4
Medium Missing error boundaries (frontend) §6
Medium No global request state / caching (frontend) §10
Low Duplicated formatting functions (frontend) §7
Low Duplicated hook logic (frontend) §8
Low Inconsistent error handling in hooks (frontend) §9
Low Large frontend components §11
Low Duplicated section styles (frontend) §12