refactor: modernize code quality tooling with Ruff
- Replace black, isort, and flake8 with Ruff (all-in-one linter and formatter) - Add comprehensive pyproject.toml configuration - Simplify Makefile code quality targets - Configure exclusions for venv/.venv in pyproject.toml - Auto-fix 1,359 linting issues across codebase Benefits: - Much faster builds (Ruff is written in Rust) - Single tool replaces multiple tools - More comprehensive rule set (UP, B, C4, SIM, PIE, RET, Q) - All configuration centralized in pyproject.toml - Better import sorting and formatting consistency 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -17,8 +17,9 @@ The module uses the following technologies:
|
||||
|
||||
import logging
|
||||
import os
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Any, Callable, Dict, Optional
|
||||
from collections.abc import Callable
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from typing import Any
|
||||
|
||||
from fastapi import HTTPException
|
||||
from fastapi.security import HTTPAuthorizationCredentials
|
||||
@@ -26,10 +27,14 @@ from jose import jwt
|
||||
from passlib.context import CryptContext
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.exceptions import (AdminRequiredException,
|
||||
InsufficientPermissionsException,
|
||||
InvalidCredentialsException, InvalidTokenException,
|
||||
TokenExpiredException, UserNotActiveException)
|
||||
from app.exceptions import (
|
||||
AdminRequiredException,
|
||||
InsufficientPermissionsException,
|
||||
InvalidCredentialsException,
|
||||
InvalidTokenException,
|
||||
TokenExpiredException,
|
||||
UserNotActiveException,
|
||||
)
|
||||
from models.database.user import User
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -97,7 +102,7 @@ class AuthManager:
|
||||
|
||||
def authenticate_user(
|
||||
self, db: Session, username: str, password: str
|
||||
) -> Optional[User]:
|
||||
) -> User | None:
|
||||
"""Authenticate user credentials against the database.
|
||||
|
||||
Supports authentication using either username or email address.
|
||||
@@ -129,7 +134,7 @@ class AuthManager:
|
||||
# Authentication successful, return user object
|
||||
return user
|
||||
|
||||
def create_access_token(self, user: User) -> Dict[str, Any]:
|
||||
def create_access_token(self, user: User) -> dict[str, Any]:
|
||||
"""Create a JWT access token for an authenticated user.
|
||||
|
||||
The token includes user identity and role information in the payload.
|
||||
@@ -146,7 +151,7 @@ class AuthManager:
|
||||
"""
|
||||
# Calculate token expiration time
|
||||
expires_delta = timedelta(minutes=self.token_expire_minutes)
|
||||
expire = datetime.now(timezone.utc) + expires_delta
|
||||
expire = datetime.now(UTC) + expires_delta
|
||||
|
||||
# Build JWT payload with user information
|
||||
payload = {
|
||||
@@ -155,7 +160,7 @@ class AuthManager:
|
||||
"email": user.email, # User email address
|
||||
"role": user.role, # User role for authorization
|
||||
"exp": expire, # Expiration time (JWT standard claim)
|
||||
"iat": datetime.now(timezone.utc), # Issued at time (JWT standard claim)
|
||||
"iat": datetime.now(UTC), # Issued at time (JWT standard claim)
|
||||
}
|
||||
|
||||
# Encode the payload into a JWT token
|
||||
@@ -168,7 +173,7 @@ class AuthManager:
|
||||
"expires_in": self.token_expire_minutes * 60, # Convert minutes to seconds
|
||||
}
|
||||
|
||||
def verify_token(self, token: str) -> Dict[str, Any]:
|
||||
def verify_token(self, token: str) -> dict[str, Any]:
|
||||
"""Verify and decode a JWT token, returning the user data.
|
||||
|
||||
Validates the token signature, expiration, and required claims.
|
||||
@@ -199,8 +204,8 @@ class AuthManager:
|
||||
raise InvalidTokenException("Token missing expiration")
|
||||
|
||||
# Check if token has expired (additional check beyond jwt.decode)
|
||||
if datetime.now(timezone.utc) > datetime.fromtimestamp(
|
||||
exp, tz=timezone.utc
|
||||
if datetime.now(UTC) > datetime.fromtimestamp(
|
||||
exp, tz=UTC
|
||||
):
|
||||
raise TokenExpiredException()
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ class ContextManager:
|
||||
host = host.split(":")[0]
|
||||
|
||||
logger.debug(
|
||||
f"[CONTEXT] Detecting context",
|
||||
"[CONTEXT] Detecting context",
|
||||
extra={
|
||||
"original_path": request.url.path,
|
||||
"clean_path": getattr(request.state, "clean_path", "NOT SET"),
|
||||
|
||||
@@ -9,7 +9,7 @@ This module provides classes and functions for:
|
||||
|
||||
import logging
|
||||
import time
|
||||
from typing import Callable
|
||||
from collections.abc import Callable
|
||||
|
||||
from fastapi import Request, Response
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
|
||||
@@ -9,8 +9,7 @@ This module provides classes and functions for:
|
||||
|
||||
import logging
|
||||
from collections import defaultdict, deque
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Dict
|
||||
from datetime import UTC, datetime, timedelta
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -21,9 +20,9 @@ class RateLimiter:
|
||||
def __init__(self):
|
||||
"""Class constructor."""
|
||||
# Dictionary to store request timestamps for each client
|
||||
self.clients: Dict[str, deque] = defaultdict(lambda: deque())
|
||||
self.clients: dict[str, deque] = defaultdict(lambda: deque())
|
||||
self.cleanup_interval = 3600 # Clean up old entries every hour
|
||||
self.last_cleanup = datetime.now(timezone.utc)
|
||||
self.last_cleanup = datetime.now(UTC)
|
||||
|
||||
def allow_request(
|
||||
self, client_id: str, max_requests: int, window_seconds: int
|
||||
@@ -33,7 +32,7 @@ class RateLimiter:
|
||||
|
||||
Uses sliding window algorithm
|
||||
"""
|
||||
now = datetime.now(timezone.utc)
|
||||
now = datetime.now(UTC)
|
||||
window_start = now - timedelta(seconds=window_seconds)
|
||||
|
||||
# Clean up old entries periodically
|
||||
@@ -60,7 +59,7 @@ class RateLimiter:
|
||||
|
||||
def _cleanup_old_entries(self):
|
||||
"""Clean up old entries to prevent memory leaks."""
|
||||
cutoff_time = datetime.now(timezone.utc) - timedelta(hours=24)
|
||||
cutoff_time = datetime.now(UTC) - timedelta(hours=24)
|
||||
|
||||
clients_to_remove = []
|
||||
for client_id, requests in self.clients.items():
|
||||
@@ -80,11 +79,11 @@ class RateLimiter:
|
||||
f"Rate limiter cleanup completed. Removed {len(clients_to_remove)} inactive clients"
|
||||
)
|
||||
|
||||
def get_client_stats(self, client_id: str) -> Dict[str, int]:
|
||||
def get_client_stats(self, client_id: str) -> dict[str, int]:
|
||||
"""Get statistics for a specific client."""
|
||||
client_requests = self.clients.get(client_id, deque())
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
now = datetime.now(UTC)
|
||||
hour_ago = now - timedelta(hours=1)
|
||||
day_ago = now - timedelta(days=1)
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ class ThemeContextMiddleware(BaseHTTPMiddleware):
|
||||
request.state.theme = theme
|
||||
|
||||
logger.debug(
|
||||
f"[THEME] Theme loaded for vendor",
|
||||
"[THEME] Theme loaded for vendor",
|
||||
extra={
|
||||
"vendor_id": vendor.id,
|
||||
"vendor_name": vendor.name,
|
||||
|
||||
@@ -12,7 +12,6 @@ Also extracts clean_path for nested routing patterns.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import Request
|
||||
from sqlalchemy import func
|
||||
@@ -31,7 +30,7 @@ class VendorContextManager:
|
||||
"""Manages vendor context detection for multi-tenant routing."""
|
||||
|
||||
@staticmethod
|
||||
def detect_vendor_context(request: Request) -> Optional[dict]:
|
||||
def detect_vendor_context(request: Request) -> dict | None:
|
||||
"""
|
||||
Detect vendor context from request.
|
||||
|
||||
@@ -106,7 +105,7 @@ class VendorContextManager:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_vendor_from_context(db: Session, context: dict) -> Optional[Vendor]:
|
||||
def get_vendor_from_context(db: Session, context: dict) -> Vendor | None:
|
||||
"""
|
||||
Get vendor from database using context information.
|
||||
|
||||
@@ -142,11 +141,10 @@ class VendorContextManager:
|
||||
f"[OK] Vendor found via custom domain: {domain} → {vendor.name}"
|
||||
)
|
||||
return vendor
|
||||
else:
|
||||
logger.warning(
|
||||
f"No active vendor found for custom domain: {domain}"
|
||||
)
|
||||
return None
|
||||
logger.warning(
|
||||
f"No active vendor found for custom domain: {domain}"
|
||||
)
|
||||
return None
|
||||
|
||||
# Method 2 & 3: Subdomain or path-based lookup
|
||||
if "subdomain" in context:
|
||||
@@ -169,7 +167,7 @@ class VendorContextManager:
|
||||
return vendor
|
||||
|
||||
@staticmethod
|
||||
def extract_clean_path(request: Request, vendor_context: Optional[dict]) -> str:
|
||||
def extract_clean_path(request: Request, vendor_context: dict | None) -> str:
|
||||
"""
|
||||
Extract clean path without vendor prefix for routing.
|
||||
|
||||
@@ -217,7 +215,7 @@ class VendorContextManager:
|
||||
return request.url.path.startswith("/api/v1/shop/")
|
||||
|
||||
@staticmethod
|
||||
def extract_vendor_from_referer(request: Request) -> Optional[dict]:
|
||||
def extract_vendor_from_referer(request: Request) -> dict | None:
|
||||
"""
|
||||
Extract vendor context from Referer header.
|
||||
|
||||
@@ -250,7 +248,7 @@ class VendorContextManager:
|
||||
referer_host = referer_host.split(":")[0]
|
||||
|
||||
logger.debug(
|
||||
f"[VENDOR] Extracting vendor from Referer",
|
||||
"[VENDOR] Extracting vendor from Referer",
|
||||
extra={
|
||||
"referer": referer,
|
||||
"referer_host": referer_host,
|
||||
@@ -449,7 +447,7 @@ class VendorContextMiddleware(BaseHTTPMiddleware):
|
||||
request.state.clean_path = request.url.path
|
||||
|
||||
logger.debug(
|
||||
f"[VENDOR_CONTEXT] Vendor detected from Referer for shop API",
|
||||
"[VENDOR_CONTEXT] Vendor detected from Referer for shop API",
|
||||
extra={
|
||||
"vendor_id": vendor.id,
|
||||
"vendor_name": vendor.name,
|
||||
@@ -463,7 +461,7 @@ class VendorContextMiddleware(BaseHTTPMiddleware):
|
||||
)
|
||||
else:
|
||||
logger.warning(
|
||||
f"[WARNING] Vendor context from Referer but vendor not found",
|
||||
"[WARNING] Vendor context from Referer but vendor not found",
|
||||
extra={
|
||||
"context": vendor_context,
|
||||
"detection_method": vendor_context.get(
|
||||
@@ -479,7 +477,7 @@ class VendorContextMiddleware(BaseHTTPMiddleware):
|
||||
db.close()
|
||||
else:
|
||||
logger.warning(
|
||||
f"[VENDOR] Shop API request without Referer header",
|
||||
"[VENDOR] Shop API request without Referer header",
|
||||
extra={"path": request.url.path},
|
||||
)
|
||||
request.state.vendor = None
|
||||
@@ -518,7 +516,7 @@ class VendorContextMiddleware(BaseHTTPMiddleware):
|
||||
)
|
||||
|
||||
logger.debug(
|
||||
f"[VENDOR_CONTEXT] Vendor detected",
|
||||
"[VENDOR_CONTEXT] Vendor detected",
|
||||
extra={
|
||||
"vendor_id": vendor.id,
|
||||
"vendor_name": vendor.name,
|
||||
@@ -530,7 +528,7 @@ class VendorContextMiddleware(BaseHTTPMiddleware):
|
||||
)
|
||||
else:
|
||||
logger.warning(
|
||||
f"[WARNING] Vendor context detected but vendor not found",
|
||||
"[WARNING] Vendor context detected but vendor not found",
|
||||
extra={
|
||||
"context": vendor_context,
|
||||
"detection_method": vendor_context.get("detection_method"),
|
||||
@@ -543,7 +541,7 @@ class VendorContextMiddleware(BaseHTTPMiddleware):
|
||||
db.close()
|
||||
else:
|
||||
logger.debug(
|
||||
f"[VENDOR] No vendor context detected",
|
||||
"[VENDOR] No vendor context detected",
|
||||
extra={
|
||||
"path": request.url.path,
|
||||
"host": request.headers.get("host", ""),
|
||||
@@ -557,7 +555,7 @@ class VendorContextMiddleware(BaseHTTPMiddleware):
|
||||
return await call_next(request)
|
||||
|
||||
|
||||
def get_current_vendor(request: Request) -> Optional[Vendor]:
|
||||
def get_current_vendor(request: Request) -> Vendor | None:
|
||||
"""Helper function to get current vendor from request state."""
|
||||
return getattr(request.state, "vendor", None)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user