refactoring-backend #3

Merged
lukas.pupkalipinski merged 403 commits from refactoring-backend into main 2026-05-20 20:23:46 +02:00
7 changed files with 53 additions and 41 deletions
Showing only changes of commit 0e22d1c425 - Show all commits

View File

@@ -7,4 +7,5 @@ This document catalogues architecture violations, code smells, and structural is
## Completed Refactors
- Moved `Fail2BanConnectionError` and `Fail2BanProtocolError` from `backend/app/utils/fail2ban_client.py` into `backend/app/exceptions.py`. Updated all router, service, and test call sites to import these domain exceptions from `app.exceptions` and retained backward compatibility through re-exporting in `app.utils.fail2ban_client`.
- Moved config file exceptions (`ConfigDirError`, `ConfigFileNotFoundError`, `ConfigFileExistsError`, `ConfigFileWriteError`, `ConfigFileNameError`) from `backend/app/services/raw_config_io_service.py` into `backend/app/exceptions.py`. Updated router and tests to import the shared domain exceptions from `app.exceptions`.

View File

@@ -44,6 +44,8 @@ Reference: `Docs/Refactoring.md` for full analysis of each issue.
**Why this is needed:** Routers should not reach into service internals to import exception types. All domain exceptions belong in the central registry so `main.py` global handlers and any future middleware can catch them uniformly.
**Status:** Completed ✅
---
### Task 3 — Eliminate the `config_file_service.py` delegation façade

View File

@@ -23,6 +23,44 @@ class ConfigOperationError(Exception):
"""Raised when a config payload update or command fails."""
class ConfigDirError(Exception):
"""Raised when the fail2ban config directory is missing or inaccessible."""
class ConfigFileNotFoundError(Exception):
"""Raised when a requested config file does not exist."""
def __init__(self, filename: str) -> None:
"""Initialize with the filename that was not found.
Args:
filename: The filename that could not be located.
"""
self.filename = filename
super().__init__(f"Config file not found: {filename!r}")
class ConfigFileExistsError(Exception):
"""Raised when trying to create a file that already exists."""
def __init__(self, filename: str) -> None:
"""Initialize with the filename that already exists.
Args:
filename: The filename that conflicts.
"""
self.filename = filename
super().__init__(f"Config file already exists: {filename!r}")
class ConfigFileWriteError(Exception):
"""Raised when a file cannot be written (permissions, disk full, etc.)."""
class ConfigFileNameError(Exception):
"""Raised when a supplied filename is invalid or unsafe."""
class ServerOperationError(Exception):
"""Raised when a server control command (e.g. refresh) fails."""

View File

@@ -52,7 +52,7 @@ from app.models.file_config import (
JailConfigFilesResponse,
)
from app.services import raw_config_io_service
from app.services.raw_config_io_service import (
from app.exceptions import (
ConfigDirError,
ConfigFileExistsError,
ConfigFileNameError,

View File

@@ -17,6 +17,13 @@ from __future__ import annotations
import asyncio
from app.utils.async_utils import run_blocking
from app.exceptions import (
ConfigDirError,
ConfigFileExistsError,
ConfigFileNameError,
ConfigFileNotFoundError,
ConfigFileWriteError,
)
import configparser
import re
from pathlib import Path
@@ -63,44 +70,6 @@ _SAFE_NAME_RE: re.Pattern[str] = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$
# ---------------------------------------------------------------------------
class ConfigDirError(Exception):
"""Raised when the fail2ban config directory is missing or inaccessible."""
class ConfigFileNotFoundError(Exception):
"""Raised when a requested config file does not exist."""
def __init__(self, filename: str) -> None:
"""Initialise with the filename that was not found.
Args:
filename: The filename that could not be located.
"""
self.filename = filename
super().__init__(f"Config file not found: {filename!r}")
class ConfigFileExistsError(Exception):
"""Raised when trying to create a file that already exists."""
def __init__(self, filename: str) -> None:
"""Initialise with the filename that already exists.
Args:
filename: The filename that conflicts.
"""
self.filename = filename
super().__init__(f"Config file already exists: {filename!r}")
class ConfigFileWriteError(Exception):
"""Raised when a file cannot be written (permissions, disk full, etc.)."""
class ConfigFileNameError(Exception):
"""Raised when a supplied filename is invalid or unsafe."""
# ---------------------------------------------------------------------------
# Internal path helpers
# ---------------------------------------------------------------------------

View File

@@ -26,7 +26,7 @@ from app.models.file_config import (
JailConfigFileContent,
JailConfigFilesResponse,
)
from app.services.raw_config_io_service import (
from app.exceptions import (
ConfigDirError,
ConfigFileExistsError,
ConfigFileNameError,

View File

@@ -8,12 +8,14 @@ import pytest
from app.models.config import ActionConfigUpdate, FilterConfigUpdate, JailFileConfigUpdate
from app.models.file_config import ConfFileCreateRequest, ConfFileUpdateRequest
from app.services.raw_config_io_service import (
from app.exceptions import (
ConfigDirError,
ConfigFileExistsError,
ConfigFileNameError,
ConfigFileNotFoundError,
ConfigFileWriteError,
)
from app.services.raw_config_io_service import (
_parse_enabled,
_set_enabled_in_content,
_validate_new_name,