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
+22
View File
@@ -20,6 +20,7 @@ from __future__ import annotations
import hashlib
import hmac
import logging
import re
import secrets
import uuid
from datetime import datetime, timezone, timedelta
@@ -36,6 +37,11 @@ from sqlalchemy.ext.asyncio import AsyncSession
from config import settings
from db.models import BackupCode, Quota, RefreshToken, User
_PASSWORD_DETAIL = (
"Password must be at least 12 characters and include uppercase, "
"lowercase, a number, and a special character."
)
logger = logging.getLogger(__name__)
# ── Password hashing ────────────────────────────────────────────────────────────
@@ -59,6 +65,22 @@ def verify_password(plain: str, hashed: str) -> bool:
return False
def validate_password_strength(password: str) -> None:
"""Raise ValueError with a descriptive message if *password* fails any strength rule.
Rules (AUTH-01): min 12 chars, uppercase, lowercase, digit, special char.
Callers at the API boundary should catch ValueError and map it to HTTP 422.
"""
if (
len(password) < 12
or not re.search(r"[A-Z]", password)
or not re.search(r"[a-z]", password)
or not re.search(r"[0-9]", password)
or not re.search(r"[^A-Za-z0-9]", password)
):
raise ValueError(_PASSWORD_DETAIL)
# ── JWT helpers ─────────────────────────────────────────────────────────────────
def create_access_token(user_id: str, role: str) -> str: