refactor(backend): extract shared helper modules per architecture rules

- Add backend/ai/utils.py — parse_classification, parse_suggestions, strip_code_fences
  shared by all AI providers; removes duplicated private functions from
  anthropic_provider.py and openai_provider.py
- Add backend/deps/utils.py — get_client_ip, parse_uuid request-parsing helpers;
  removes local _ip() variants from admin.py, auth.py, shares.py, folders.py
- Add backend/storage/exceptions.py — canonical CloudConnectionError definition;
  all routers and backends import from here instead of redefining
- Move validate_password_strength to backend/services/auth.py; removes duplicated
  _validate_password_strength from admin.py and auth.py

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
curo1305
2026-06-02 16:10:35 +02:00
parent 89f8d5a654
commit a548266461
14 changed files with 171 additions and 232 deletions
+4 -11
View File
@@ -30,6 +30,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from db.models import Document, Folder, Quota, Share, User
from deps.auth import get_regular_user
from deps.db import get_db
from deps.utils import get_client_ip
from services.audit import write_audit_log
from storage import get_storage_backend
@@ -51,14 +52,6 @@ class DocumentMove(BaseModel):
folder_id: Optional[str] = None
# ── Helper: extract IP address ────────────────────────────────────────────────
def _get_ip(request: Request) -> Optional[str]:
"""Extract client IP, honouring X-Forwarded-For for reverse proxy setups (Pitfall 5)."""
return request.headers.get("X-Forwarded-For") or (
request.client.host if request.client else None
)
# ── Helper: folder serialization ──────────────────────────────────────────────
@@ -148,7 +141,7 @@ async def create_folder(
user_id=current_user.id,
actor_id=current_user.id,
resource_id=folder.id,
ip_address=_get_ip(request),
ip_address=get_client_ip(request),
metadata_={"name": folder.name, "parent_id": str(parent_uuid) if parent_uuid else None},
)
@@ -316,7 +309,7 @@ async def rename_folder(
user_id=current_user.id,
actor_id=current_user.id,
resource_id=folder.id,
ip_address=_get_ip(request),
ip_address=get_client_ip(request),
metadata_={"old_name": old_name, "new_name": folder.name},
)
@@ -436,7 +429,7 @@ async def delete_folder(
user_id=current_user.id,
actor_id=current_user.id,
resource_id=uid,
ip_address=_get_ip(request),
ip_address=get_client_ip(request),
metadata_={"name": folder_name, "doc_count": len(docs)},
)