Refactor: Split blocklist import flow into focused components
Extracted the monolithic import_source() function (776 lines) into focused, testable components with clear single responsibilities: - BlocklistDownloader: HTTP download with exponential backoff retry logic * Handles transient failures (429, 5xx errors, timeouts) * Configurable retry attempts and backoff strategy * 93% test coverage - BlocklistParser: Parse and validate IP addresses * Extract valid IPv4/IPv6 addresses from text * Skip CIDRs and malformed entries gracefully * Separate parsing from validation concerns * 100% test coverage - BanExecutor: Ban execution with error handling * Ban IPs via fail2ban socket * Stop on JailNotFoundError (jail doesn't exist) * Continue on JailOperationError (individual ban failures) * 100% test coverage - BlocklistImportWorkflow: Thin orchestrator * Coordinates the download → parse → ban → log flow * Pre-warms geo cache with newly banned IPs * 96% test coverage - blocklist_service.py: Maintains public API * Source CRUD (create, read, update, delete) * URL validation and preview functionality * Scheduling configuration and import triggers * 92% test coverage Benefits: * Each component is independently testable with mock dependencies * Error handling is explicit and localized * Components can evolve independently * Logging is contextual and clear * Retry and transient error handling are isolated Testing: * All 36 existing blocklist_service tests pass * All 13 blocklist import task tests pass * Added 17 comprehensive component unit tests * Combined 96%+ coverage on new modules * Zero type errors in new code Documentation: * Updated Refactoring.md with detailed architecture notes * Added component architecture diagram to Architekture.md * Documented ownership and responsibilities of each component Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -122,7 +122,11 @@ backend/
|
||||
│ │ ├── log_service.py # Log preview and regex test operations
|
||||
│ │ ├── fail2ban_metadata_service.py # Resolve and cache the fail2ban SQLite DB path via the fail2ban socket
|
||||
│ │ ├── history_service.py # Historical ban queries, per-IP timeline
|
||||
│ │ ├── blocklist_service.py # Download, validate, apply blocklists
|
||||
│ │ ├── blocklist_service.py # Orchestration: source CRUD, scheduling, import triggers
|
||||
│ │ ├── blocklist_downloader.py # HTTP download with retry logic
|
||||
│ │ ├── blocklist_parser.py # Parse and validate IP addresses
|
||||
│ │ ├── blocklist_ban_executor.py # Ban execution with error handling
|
||||
│ │ ├── blocklist_import_workflow.py # Import orchestration (coordinates components)
|
||||
│ │ ├── geo_service.py # IP-to-country resolution, ASN/RIR lookup
|
||||
│ │ ├── server_service.py # Server settings, log management, DB purge
|
||||
│ │ └── health_service.py # fail2ban connectivity checks, version detection
|
||||
@@ -197,12 +201,60 @@ The business logic layer. Services orchestrate operations, enforce rules, and co
|
||||
| `fail2ban_metadata_service.py` | Resolves the fail2ban SQLite database path by querying the fail2ban socket and caches the result for reuse across services |
|
||||
| `log_service.py` | Log preview and regex test operations (extracted from config_service) |
|
||||
| `history_service.py` | Queries the fail2ban database for historical ban records, builds per-IP timelines, computes ban counts and repeat-offender flags, and syncs new records into BanGUI's archive table |
|
||||
| `blocklist_service.py` | Downloads blocklists via aiohttp, validates IPs/CIDRs, applies bans through fail2ban or iptables, logs import results |
|
||||
| `blocklist_service.py` | Orchestration layer for blocklist imports. Delegates to focused components: `BlocklistDownloader` (HTTP download with retry), `BlocklistParser` (IP validation), `BanExecutor` (fail2ban integration), and `BlocklistImportWorkflow` (orchestrates the flow). Maintains public API for source CRUD, preview, scheduling, and import triggers. |
|
||||
| `geo_cache.py` | **GeoCache** class that encapsulates all IP geolocation caching: resolves IP addresses to country, ASN, and organization using a primary local MaxMind GeoLite2-Country database (if available) with optional HTTP fallback to ip-api.com (disabled by default for security). Maintains in-memory and persistent caches with negative cache support, and manages background re-resolution. Instantiated once at startup with allow_http_fallback flag and stored on `app.state.geo_cache` |
|
||||
| `geo_service.py` | (Deprecated) Backward-compatibility wrappers that delegate to the `GeoCache` instance. Kept for compatibility with existing code. New code should use `GeoCache` directly or via dependency injection |
|
||||
| `server_service.py` | Reads and writes fail2ban server-level settings (log level, log target, syslog socket, DB location, purge age) |
|
||||
| `health_service.py` | Probes fail2ban socket connectivity, retrieves server version and global stats, reports online/offline status |
|
||||
|
||||
##### Blocklist Import Architecture
|
||||
|
||||
The blocklist import flow has been refactored to separate concerns into focused components:
|
||||
|
||||
```
|
||||
blocklist_service.py (Public API)
|
||||
│
|
||||
├─ import_source() ──┐
|
||||
│ │
|
||||
└─ import_all() ├──> BlocklistImportWorkflow (Orchestrator)
|
||||
│ │
|
||||
│ ├──> BlocklistDownloader
|
||||
│ │ • HTTP GET with retry logic
|
||||
│ │ • Exponential backoff (429, 5xx)
|
||||
│ │ • Timeout handling
|
||||
│ │
|
||||
│ ├──> BlocklistParser
|
||||
│ │ • Parse text to IP lines
|
||||
│ │ • Validate IPv4/IPv6 addresses
|
||||
│ │ • Skip CIDRs and malformed entries
|
||||
│ │
|
||||
│ ├──> BanExecutor
|
||||
│ │ • Ban each IP via fail2ban socket
|
||||
│ │ • Abort on JailNotFoundError
|
||||
│ │ • Continue on individual ban failures
|
||||
│ │
|
||||
│ └──> Geo pre-warming
|
||||
│ (optional batch lookup for newly banned IPs)
|
||||
│
|
||||
└──> Result logging (import_log_repo)
|
||||
```
|
||||
|
||||
**Component Responsibilities:**
|
||||
|
||||
- **BlocklistDownloader**: Handles HTTP transport concerns (retries, timeouts, backoff)
|
||||
- **BlocklistParser**: Handles parsing and validation logic (clean, testable, no I/O)
|
||||
- **BanExecutor**: Handles fail2ban integration with error aggregation
|
||||
- **BlocklistImportWorkflow**: Coordinates the flow, handles result aggregation and geo pre-warming
|
||||
- **blocklist_service.py**: Maintains public API (source CRUD, scheduling, import triggers)
|
||||
|
||||
**Benefits of This Architecture:**
|
||||
|
||||
- Each component is independently testable with mock dependencies
|
||||
- Error handling is clear: JailNotFoundError stops processing, JailOperationError continues
|
||||
- Components can be evolved independently (e.g., replace HTTP client, add batch validation)
|
||||
- Logging is contextual and tied to the appropriate layer
|
||||
- Retry logic and transient error handling are isolated
|
||||
|
||||
#### Repositories (`app/repositories/`)
|
||||
|
||||
The data access layer. Repositories execute raw SQL queries against the application SQLite database. They return plain data or domain models — they never raise HTTP exceptions or contain business logic.
|
||||
|
||||
Reference in New Issue
Block a user