style: apply black and isort formatting across entire codebase
- Standardize quote style (single to double quotes) - Reorder and group imports alphabetically - Fix line breaks and indentation for consistency - Apply PEP 8 formatting standards Also updated Makefile to exclude both venv and .venv from code quality checks. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -26,14 +26,10 @@ from jose import jwt
|
||||
from passlib.context import CryptContext
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.exceptions import (
|
||||
AdminRequiredException,
|
||||
InvalidTokenException,
|
||||
TokenExpiredException,
|
||||
UserNotActiveException,
|
||||
InvalidCredentialsException,
|
||||
InsufficientPermissionsException
|
||||
)
|
||||
from app.exceptions import (AdminRequiredException,
|
||||
InsufficientPermissionsException,
|
||||
InvalidCredentialsException, InvalidTokenException,
|
||||
TokenExpiredException, UserNotActiveException)
|
||||
from models.database.user import User
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -99,7 +95,9 @@ class AuthManager:
|
||||
"""
|
||||
return pwd_context.verify(plain_password, hashed_password)
|
||||
|
||||
def authenticate_user(self, db: Session, username: str, password: str) -> Optional[User]:
|
||||
def authenticate_user(
|
||||
self, db: Session, username: str, password: str
|
||||
) -> Optional[User]:
|
||||
"""Authenticate user credentials against the database.
|
||||
|
||||
Supports authentication using either username or email address.
|
||||
@@ -201,7 +199,9 @@ 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(timezone.utc) > datetime.fromtimestamp(
|
||||
exp, tz=timezone.utc
|
||||
):
|
||||
raise TokenExpiredException()
|
||||
|
||||
# Validate user identifier claim exists
|
||||
@@ -214,7 +214,9 @@ class AuthManager:
|
||||
"user_id": int(user_id),
|
||||
"username": payload.get("username"),
|
||||
"email": payload.get("email"),
|
||||
"role": payload.get("role", "user"), # Default to "user" role if not specified
|
||||
"role": payload.get(
|
||||
"role", "user"
|
||||
), # Default to "user" role if not specified
|
||||
}
|
||||
|
||||
except jwt.ExpiredSignatureError:
|
||||
@@ -232,7 +234,9 @@ class AuthManager:
|
||||
logger.error(f"Token verification error: {e}")
|
||||
raise InvalidTokenException("Authentication failed")
|
||||
|
||||
def get_current_user(self, db: Session, credentials: HTTPAuthorizationCredentials) -> User:
|
||||
def get_current_user(
|
||||
self, db: Session, credentials: HTTPAuthorizationCredentials
|
||||
) -> User:
|
||||
"""Extract and validate the current authenticated user from request credentials.
|
||||
|
||||
Verifies the JWT token from the Authorization header, looks up the user
|
||||
@@ -286,8 +290,10 @@ class AuthManager:
|
||||
# This will only execute if user has "admin" role
|
||||
pass
|
||||
"""
|
||||
|
||||
def decorator(func):
|
||||
"""Decorator that wraps the function with role checking."""
|
||||
|
||||
def wrapper(current_user: User, *args, **kwargs):
|
||||
# Check if current user has the required role
|
||||
if current_user.role != required_role:
|
||||
@@ -339,8 +345,7 @@ class AuthManager:
|
||||
# Check if user has vendor or admin role (admins have full access)
|
||||
if current_user.role not in ["vendor", "admin"]:
|
||||
raise InsufficientPermissionsException(
|
||||
message="Vendor access required",
|
||||
required_permission="vendor"
|
||||
message="Vendor access required", required_permission="vendor"
|
||||
)
|
||||
return current_user
|
||||
|
||||
@@ -363,7 +368,7 @@ class AuthManager:
|
||||
if current_user.role not in ["customer", "admin"]:
|
||||
raise InsufficientPermissionsException(
|
||||
message="Customer account access required",
|
||||
required_permission="customer"
|
||||
required_permission="customer",
|
||||
)
|
||||
return current_user
|
||||
|
||||
|
||||
@@ -17,14 +17,16 @@ Class-based middleware provides:
|
||||
|
||||
import logging
|
||||
from enum import Enum
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
|
||||
from fastapi import Request
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RequestContext(str, Enum):
|
||||
"""Request context types for the application."""
|
||||
|
||||
API = "api"
|
||||
ADMIN = "admin"
|
||||
VENDOR_DASHBOARD = "vendor"
|
||||
@@ -59,7 +61,7 @@ class ContextManager:
|
||||
# Use clean_path if available (extracted by vendor_context_middleware)
|
||||
# Falls back to original path if clean_path not set
|
||||
# This is critical for correct context detection with path-based routing
|
||||
path = getattr(request.state, 'clean_path', request.url.path)
|
||||
path = getattr(request.state, "clean_path", request.url.path)
|
||||
|
||||
host = request.headers.get("host", "")
|
||||
|
||||
@@ -71,10 +73,10 @@ class ContextManager:
|
||||
f"[CONTEXT] Detecting context",
|
||||
extra={
|
||||
"original_path": request.url.path,
|
||||
"clean_path": getattr(request.state, 'clean_path', 'NOT SET'),
|
||||
"clean_path": getattr(request.state, "clean_path", "NOT SET"),
|
||||
"path_to_check": path,
|
||||
"host": host,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# 1. API context (highest priority)
|
||||
@@ -84,24 +86,30 @@ class ContextManager:
|
||||
|
||||
# 2. Admin context
|
||||
if ContextManager._is_admin_context(request, host, path):
|
||||
logger.debug("[CONTEXT] Detected as ADMIN", extra={"path": path, "host": host})
|
||||
logger.debug(
|
||||
"[CONTEXT] Detected as ADMIN", extra={"path": path, "host": host}
|
||||
)
|
||||
return RequestContext.ADMIN
|
||||
|
||||
# 3. Vendor Dashboard context (vendor management area)
|
||||
# Check both clean_path and original path for vendor dashboard
|
||||
original_path = request.url.path
|
||||
if ContextManager._is_vendor_dashboard_context(path) or \
|
||||
ContextManager._is_vendor_dashboard_context(original_path):
|
||||
logger.debug("[CONTEXT] Detected as VENDOR_DASHBOARD", extra={"path": path, "original_path": original_path})
|
||||
if ContextManager._is_vendor_dashboard_context(
|
||||
path
|
||||
) or ContextManager._is_vendor_dashboard_context(original_path):
|
||||
logger.debug(
|
||||
"[CONTEXT] Detected as VENDOR_DASHBOARD",
|
||||
extra={"path": path, "original_path": original_path},
|
||||
)
|
||||
return RequestContext.VENDOR_DASHBOARD
|
||||
|
||||
# 4. Shop context (vendor storefront)
|
||||
# Check if vendor context exists (set by vendor_context_middleware)
|
||||
if hasattr(request.state, 'vendor') and request.state.vendor:
|
||||
if hasattr(request.state, "vendor") and request.state.vendor:
|
||||
# If we have a vendor and it's not admin or vendor dashboard, it's shop
|
||||
logger.debug(
|
||||
"[CONTEXT] Detected as SHOP (has vendor context)",
|
||||
extra={"vendor": request.state.vendor.name}
|
||||
extra={"vendor": request.state.vendor.name},
|
||||
)
|
||||
return RequestContext.SHOP
|
||||
|
||||
@@ -173,11 +181,12 @@ class ContextMiddleware(BaseHTTPMiddleware):
|
||||
f"[CONTEXT_MIDDLEWARE] Context detected: {context_type.value}",
|
||||
extra={
|
||||
"path": request.url.path,
|
||||
"clean_path": getattr(request.state, 'clean_path', 'NOT SET'),
|
||||
"clean_path": getattr(request.state, "clean_path", "NOT SET"),
|
||||
"host": request.headers.get("host", ""),
|
||||
"context": context_type.value,
|
||||
"has_vendor": hasattr(request.state, 'vendor') and request.state.vendor is not None,
|
||||
}
|
||||
"has_vendor": hasattr(request.state, "vendor")
|
||||
and request.state.vendor is not None,
|
||||
},
|
||||
)
|
||||
|
||||
# Continue processing
|
||||
|
||||
@@ -9,12 +9,14 @@ This module provides classes and functions for:
|
||||
"""
|
||||
|
||||
from functools import wraps
|
||||
|
||||
from app.exceptions.base import RateLimitException # Add this import
|
||||
from middleware.rate_limiter import RateLimiter
|
||||
|
||||
# Initialize rate limiter instance
|
||||
rate_limiter = RateLimiter()
|
||||
|
||||
|
||||
def rate_limit(max_requests: int = 100, window_seconds: int = 3600):
|
||||
"""Rate limiting decorator for FastAPI endpoints."""
|
||||
|
||||
@@ -26,10 +28,11 @@ def rate_limit(max_requests: int = 100, window_seconds: int = 3600):
|
||||
if not rate_limiter.allow_request(client_id, max_requests, window_seconds):
|
||||
# Use custom exception instead of HTTPException
|
||||
raise RateLimitException(
|
||||
message="Rate limit exceeded",
|
||||
retry_after=window_seconds
|
||||
message="Rate limit exceeded", retry_after=window_seconds
|
||||
)
|
||||
|
||||
return await func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
@@ -11,9 +11,10 @@ Class-based middleware provides:
|
||||
"""
|
||||
|
||||
import logging
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
|
||||
from fastapi import Request
|
||||
from sqlalchemy.orm import Session
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
|
||||
from app.core.database import get_db
|
||||
from models.database.vendor_theme import VendorTheme
|
||||
@@ -30,10 +31,11 @@ class ThemeContextManager:
|
||||
Get theme configuration for vendor.
|
||||
Returns default theme if no custom theme is configured.
|
||||
"""
|
||||
theme = db.query(VendorTheme).filter(
|
||||
VendorTheme.vendor_id == vendor_id,
|
||||
VendorTheme.is_active == True
|
||||
).first()
|
||||
theme = (
|
||||
db.query(VendorTheme)
|
||||
.filter(VendorTheme.vendor_id == vendor_id, VendorTheme.is_active == True)
|
||||
.first()
|
||||
)
|
||||
|
||||
if theme:
|
||||
return theme.to_dict()
|
||||
@@ -52,23 +54,16 @@ class ThemeContextManager:
|
||||
"accent": "#ec4899",
|
||||
"background": "#ffffff",
|
||||
"text": "#1f2937",
|
||||
"border": "#e5e7eb"
|
||||
},
|
||||
"fonts": {
|
||||
"heading": "Inter, sans-serif",
|
||||
"body": "Inter, sans-serif"
|
||||
"border": "#e5e7eb",
|
||||
},
|
||||
"fonts": {"heading": "Inter, sans-serif", "body": "Inter, sans-serif"},
|
||||
"branding": {
|
||||
"logo": None,
|
||||
"logo_dark": None,
|
||||
"favicon": None,
|
||||
"banner": None
|
||||
},
|
||||
"layout": {
|
||||
"style": "grid",
|
||||
"header": "fixed",
|
||||
"product_card": "modern"
|
||||
"banner": None,
|
||||
},
|
||||
"layout": {"style": "grid", "header": "fixed", "product_card": "modern"},
|
||||
"social_links": {},
|
||||
"custom_css": None,
|
||||
"css_variables": {
|
||||
@@ -80,7 +75,7 @@ class ThemeContextManager:
|
||||
"--color-border": "#e5e7eb",
|
||||
"--font-heading": "Inter, sans-serif",
|
||||
"--font-body": "Inter, sans-serif",
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -106,7 +101,7 @@ class ThemeContextMiddleware(BaseHTTPMiddleware):
|
||||
Load and inject theme context.
|
||||
"""
|
||||
# Only inject theme for shop pages (not admin or API)
|
||||
if hasattr(request.state, 'vendor') and request.state.vendor:
|
||||
if hasattr(request.state, "vendor") and request.state.vendor:
|
||||
vendor = request.state.vendor
|
||||
|
||||
# Get database session
|
||||
@@ -123,13 +118,13 @@ class ThemeContextMiddleware(BaseHTTPMiddleware):
|
||||
extra={
|
||||
"vendor_id": vendor.id,
|
||||
"vendor_name": vendor.name,
|
||||
"theme_name": theme.get('theme_name', 'default'),
|
||||
}
|
||||
"theme_name": theme.get("theme_name", "default"),
|
||||
},
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"[THEME] Failed to load theme for vendor {vendor.id}: {e}",
|
||||
exc_info=True
|
||||
exc_info=True,
|
||||
)
|
||||
# Fallback to default theme
|
||||
request.state.theme = ThemeContextManager.get_default_theme()
|
||||
@@ -140,7 +135,7 @@ class ThemeContextMiddleware(BaseHTTPMiddleware):
|
||||
request.state.theme = ThemeContextManager.get_default_theme()
|
||||
logger.debug(
|
||||
"[THEME] No vendor context, using default theme",
|
||||
extra={"has_vendor": False}
|
||||
extra={"has_vendor": False},
|
||||
)
|
||||
|
||||
# Continue processing
|
||||
|
||||
@@ -13,10 +13,11 @@ Also extracts clean_path for nested routing patterns.
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import func
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
|
||||
from fastapi import Request
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.orm import Session
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
|
||||
from app.core.config import settings
|
||||
from app.core.database import get_db
|
||||
@@ -50,14 +51,15 @@ class VendorContextManager:
|
||||
|
||||
# Method 1: Custom domain detection (HIGHEST PRIORITY)
|
||||
# Check if this is a custom domain (not platform.com and not localhost)
|
||||
platform_domain = getattr(settings, 'platform_domain', 'platform.com')
|
||||
platform_domain = getattr(settings, "platform_domain", "platform.com")
|
||||
|
||||
is_custom_domain = (
|
||||
host and
|
||||
not host.endswith(f".{platform_domain}") and
|
||||
host != platform_domain and
|
||||
host not in ["localhost", "127.0.0.1", "admin.localhost", "admin.127.0.0.1"] and
|
||||
not host.startswith("admin.")
|
||||
host
|
||||
and not host.endswith(f".{platform_domain}")
|
||||
and host != platform_domain
|
||||
and host
|
||||
not in ["localhost", "127.0.0.1", "admin.localhost", "admin.127.0.0.1"]
|
||||
and not host.startswith("admin.")
|
||||
)
|
||||
|
||||
if is_custom_domain:
|
||||
@@ -66,7 +68,7 @@ class VendorContextManager:
|
||||
"domain": normalized_domain,
|
||||
"detection_method": "custom_domain",
|
||||
"host": host,
|
||||
"original_host": request.headers.get("host", "")
|
||||
"original_host": request.headers.get("host", ""),
|
||||
}
|
||||
|
||||
# Method 2: Subdomain detection (vendor1.platform.com)
|
||||
@@ -78,7 +80,7 @@ class VendorContextManager:
|
||||
return {
|
||||
"subdomain": subdomain,
|
||||
"detection_method": "subdomain",
|
||||
"host": host
|
||||
"host": host,
|
||||
}
|
||||
|
||||
# Method 3: Path-based detection (/vendor/vendorname/ or /vendors/vendorname/)
|
||||
@@ -96,9 +98,9 @@ class VendorContextManager:
|
||||
return {
|
||||
"subdomain": vendor_code,
|
||||
"detection_method": "path",
|
||||
"path_prefix": path[:prefix_len + len(vendor_code)],
|
||||
"path_prefix": path[: prefix_len + len(vendor_code)],
|
||||
"full_prefix": path[:prefix_len], # /vendor/ or /vendors/
|
||||
"host": host
|
||||
"host": host,
|
||||
}
|
||||
|
||||
return None
|
||||
@@ -136,10 +138,14 @@ class VendorContextManager:
|
||||
logger.warning(f"Vendor for domain {domain} is not active")
|
||||
return None
|
||||
|
||||
logger.info(f"[OK] Vendor found via custom domain: {domain} → {vendor.name}")
|
||||
logger.info(
|
||||
f"[OK] Vendor found via custom domain: {domain} → {vendor.name}"
|
||||
)
|
||||
return vendor
|
||||
else:
|
||||
logger.warning(f"No active vendor found for custom domain: {domain}")
|
||||
logger.warning(
|
||||
f"No active vendor found for custom domain: {domain}"
|
||||
)
|
||||
return None
|
||||
|
||||
# Method 2 & 3: Subdomain or path-based lookup
|
||||
@@ -154,7 +160,9 @@ class VendorContextManager:
|
||||
|
||||
if vendor:
|
||||
method = context.get("detection_method", "unknown")
|
||||
logger.info(f"[OK] Vendor found via {method}: {subdomain} → {vendor.name}")
|
||||
logger.info(
|
||||
f"[OK] Vendor found via {method}: {subdomain} → {vendor.name}"
|
||||
)
|
||||
else:
|
||||
logger.warning(f"No active vendor found for subdomain: {subdomain}")
|
||||
|
||||
@@ -176,7 +184,7 @@ class VendorContextManager:
|
||||
path_prefix = vendor_context.get("path_prefix", "")
|
||||
|
||||
if path.startswith(path_prefix):
|
||||
clean_path = path[len(path_prefix):]
|
||||
clean_path = path[len(path_prefix) :]
|
||||
return clean_path if clean_path else "/"
|
||||
|
||||
return request.url.path
|
||||
@@ -232,6 +240,7 @@ class VendorContextManager:
|
||||
|
||||
try:
|
||||
from urllib.parse import urlparse
|
||||
|
||||
parsed = urlparse(referer)
|
||||
referer_host = parsed.hostname or ""
|
||||
referer_path = parsed.path or ""
|
||||
@@ -246,27 +255,33 @@ class VendorContextManager:
|
||||
"referer": referer,
|
||||
"referer_host": referer_host,
|
||||
"referer_path": referer_path,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# Method 1: Path-based detection from referer path
|
||||
# /vendors/wizamart/shop/products → wizamart
|
||||
if referer_path.startswith("/vendors/") or referer_path.startswith("/vendor/"):
|
||||
prefix = "/vendors/" if referer_path.startswith("/vendors/") else "/vendor/"
|
||||
path_parts = referer_path[len(prefix):].split("/")
|
||||
if referer_path.startswith("/vendors/") or referer_path.startswith(
|
||||
"/vendor/"
|
||||
):
|
||||
prefix = (
|
||||
"/vendors/" if referer_path.startswith("/vendors/") else "/vendor/"
|
||||
)
|
||||
path_parts = referer_path[len(prefix) :].split("/")
|
||||
if len(path_parts) >= 1 and path_parts[0]:
|
||||
vendor_code = path_parts[0]
|
||||
prefix_len = len(prefix)
|
||||
logger.debug(
|
||||
f"[VENDOR] Extracted vendor from Referer path: {vendor_code}",
|
||||
extra={"vendor_code": vendor_code, "method": "referer_path"}
|
||||
extra={"vendor_code": vendor_code, "method": "referer_path"},
|
||||
)
|
||||
# Use "path" as detection_method to be consistent with direct path detection
|
||||
# This allows cookie path logic to work the same way
|
||||
return {
|
||||
"subdomain": vendor_code,
|
||||
"detection_method": "path", # Consistent with direct path detection
|
||||
"path_prefix": referer_path[:prefix_len + len(vendor_code)], # /vendor/vendor1
|
||||
"path_prefix": referer_path[
|
||||
: prefix_len + len(vendor_code)
|
||||
], # /vendor/vendor1
|
||||
"full_prefix": prefix, # /vendor/ or /vendors/
|
||||
"host": referer_host,
|
||||
"referer": referer,
|
||||
@@ -274,7 +289,7 @@ class VendorContextManager:
|
||||
|
||||
# Method 2: Subdomain detection from referer host
|
||||
# wizamart.platform.com → wizamart
|
||||
platform_domain = getattr(settings, 'platform_domain', 'platform.com')
|
||||
platform_domain = getattr(settings, "platform_domain", "platform.com")
|
||||
if "." in referer_host:
|
||||
parts = referer_host.split(".")
|
||||
if len(parts) >= 2 and parts[0] not in ["www", "admin", "api"]:
|
||||
@@ -283,7 +298,10 @@ class VendorContextManager:
|
||||
subdomain = parts[0]
|
||||
logger.debug(
|
||||
f"[VENDOR] Extracted vendor from Referer subdomain: {subdomain}",
|
||||
extra={"subdomain": subdomain, "method": "referer_subdomain"}
|
||||
extra={
|
||||
"subdomain": subdomain,
|
||||
"method": "referer_subdomain",
|
||||
},
|
||||
)
|
||||
return {
|
||||
"subdomain": subdomain,
|
||||
@@ -295,19 +313,23 @@ class VendorContextManager:
|
||||
# Method 3: Custom domain detection from referer host
|
||||
# custom-shop.com → custom-shop.com
|
||||
is_custom_domain = (
|
||||
referer_host and
|
||||
not referer_host.endswith(f".{platform_domain}") and
|
||||
referer_host != platform_domain and
|
||||
referer_host not in ["localhost", "127.0.0.1"] and
|
||||
not referer_host.startswith("admin.")
|
||||
referer_host
|
||||
and not referer_host.endswith(f".{platform_domain}")
|
||||
and referer_host != platform_domain
|
||||
and referer_host not in ["localhost", "127.0.0.1"]
|
||||
and not referer_host.startswith("admin.")
|
||||
)
|
||||
|
||||
if is_custom_domain:
|
||||
from models.database.vendor_domain import VendorDomain
|
||||
|
||||
normalized_domain = VendorDomain.normalize_domain(referer_host)
|
||||
logger.debug(
|
||||
f"[VENDOR] Extracted vendor from Referer custom domain: {normalized_domain}",
|
||||
extra={"domain": normalized_domain, "method": "referer_custom_domain"}
|
||||
extra={
|
||||
"domain": normalized_domain,
|
||||
"method": "referer_custom_domain",
|
||||
},
|
||||
)
|
||||
return {
|
||||
"domain": normalized_domain,
|
||||
@@ -319,7 +341,7 @@ class VendorContextManager:
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
f"[VENDOR] Failed to extract vendor from Referer: {e}",
|
||||
extra={"referer": referer, "error": str(e)}
|
||||
extra={"referer": referer, "error": str(e)},
|
||||
)
|
||||
|
||||
return None
|
||||
@@ -330,12 +352,28 @@ class VendorContextManager:
|
||||
path = request.url.path.lower()
|
||||
|
||||
static_extensions = (
|
||||
'.ico', '.css', '.js', '.png', '.jpg', '.jpeg', '.gif', '.svg',
|
||||
'.woff', '.woff2', '.ttf', '.eot', '.webp', '.map', '.json',
|
||||
'.xml', '.txt', '.pdf', '.webmanifest'
|
||||
".ico",
|
||||
".css",
|
||||
".js",
|
||||
".png",
|
||||
".jpg",
|
||||
".jpeg",
|
||||
".gif",
|
||||
".svg",
|
||||
".woff",
|
||||
".woff2",
|
||||
".ttf",
|
||||
".eot",
|
||||
".webp",
|
||||
".map",
|
||||
".json",
|
||||
".xml",
|
||||
".txt",
|
||||
".pdf",
|
||||
".webmanifest",
|
||||
)
|
||||
|
||||
static_paths = ('/static/', '/media/', '/assets/', '/.well-known/')
|
||||
static_paths = ("/static/", "/media/", "/assets/", "/.well-known/")
|
||||
|
||||
if path.endswith(static_extensions):
|
||||
return True
|
||||
@@ -343,7 +381,7 @@ class VendorContextManager:
|
||||
if any(path.startswith(static_path) for static_path in static_paths):
|
||||
return True
|
||||
|
||||
if 'favicon.ico' in path:
|
||||
if "favicon.ico" in path:
|
||||
return True
|
||||
|
||||
return False
|
||||
@@ -372,13 +410,13 @@ class VendorContextMiddleware(BaseHTTPMiddleware):
|
||||
"""
|
||||
# Skip vendor detection for admin, static files, and system requests
|
||||
if (
|
||||
VendorContextManager.is_admin_request(request) or
|
||||
VendorContextManager.is_static_file_request(request) or
|
||||
request.url.path in ["/", "/health", "/docs", "/redoc", "/openapi.json"]
|
||||
VendorContextManager.is_admin_request(request)
|
||||
or VendorContextManager.is_static_file_request(request)
|
||||
or request.url.path in ["/", "/health", "/docs", "/redoc", "/openapi.json"]
|
||||
):
|
||||
logger.debug(
|
||||
f"[VENDOR] Skipping vendor detection: {request.url.path}",
|
||||
extra={"path": request.url.path, "reason": "admin/static/system"}
|
||||
extra={"path": request.url.path, "reason": "admin/static/system"},
|
||||
)
|
||||
request.state.vendor = None
|
||||
request.state.vendor_context = None
|
||||
@@ -389,7 +427,10 @@ class VendorContextMiddleware(BaseHTTPMiddleware):
|
||||
if VendorContextManager.is_shop_api_request(request):
|
||||
logger.debug(
|
||||
f"[VENDOR] Shop API request detected: {request.url.path}",
|
||||
extra={"path": request.url.path, "referer": request.headers.get("referer", "")}
|
||||
extra={
|
||||
"path": request.url.path,
|
||||
"referer": request.headers.get("referer", ""),
|
||||
},
|
||||
)
|
||||
|
||||
vendor_context = VendorContextManager.extract_vendor_from_referer(request)
|
||||
@@ -398,7 +439,9 @@ class VendorContextMiddleware(BaseHTTPMiddleware):
|
||||
db_gen = get_db()
|
||||
db = next(db_gen)
|
||||
try:
|
||||
vendor = VendorContextManager.get_vendor_from_context(db, vendor_context)
|
||||
vendor = VendorContextManager.get_vendor_from_context(
|
||||
db, vendor_context
|
||||
)
|
||||
|
||||
if vendor:
|
||||
request.state.vendor = vendor
|
||||
@@ -411,19 +454,23 @@ class VendorContextMiddleware(BaseHTTPMiddleware):
|
||||
"vendor_id": vendor.id,
|
||||
"vendor_name": vendor.name,
|
||||
"vendor_subdomain": vendor.subdomain,
|
||||
"detection_method": vendor_context.get("detection_method"),
|
||||
"detection_method": vendor_context.get(
|
||||
"detection_method"
|
||||
),
|
||||
"api_path": request.url.path,
|
||||
"referer": vendor_context.get("referer", ""),
|
||||
}
|
||||
},
|
||||
)
|
||||
else:
|
||||
logger.warning(
|
||||
f"[WARNING] Vendor context from Referer but vendor not found",
|
||||
extra={
|
||||
"context": vendor_context,
|
||||
"detection_method": vendor_context.get("detection_method"),
|
||||
"detection_method": vendor_context.get(
|
||||
"detection_method"
|
||||
),
|
||||
"api_path": request.url.path,
|
||||
}
|
||||
},
|
||||
)
|
||||
request.state.vendor = None
|
||||
request.state.vendor_context = vendor_context
|
||||
@@ -433,7 +480,7 @@ class VendorContextMiddleware(BaseHTTPMiddleware):
|
||||
else:
|
||||
logger.warning(
|
||||
f"[VENDOR] Shop API request without Referer header",
|
||||
extra={"path": request.url.path}
|
||||
extra={"path": request.url.path},
|
||||
)
|
||||
request.state.vendor = None
|
||||
request.state.vendor_context = None
|
||||
@@ -445,7 +492,7 @@ class VendorContextMiddleware(BaseHTTPMiddleware):
|
||||
if VendorContextManager.is_api_request(request):
|
||||
logger.debug(
|
||||
f"[VENDOR] Skipping vendor detection for non-shop API: {request.url.path}",
|
||||
extra={"path": request.url.path, "reason": "api"}
|
||||
extra={"path": request.url.path, "reason": "api"},
|
||||
)
|
||||
request.state.vendor = None
|
||||
request.state.vendor_context = None
|
||||
@@ -459,7 +506,9 @@ class VendorContextMiddleware(BaseHTTPMiddleware):
|
||||
db_gen = get_db()
|
||||
db = next(db_gen)
|
||||
try:
|
||||
vendor = VendorContextManager.get_vendor_from_context(db, vendor_context)
|
||||
vendor = VendorContextManager.get_vendor_from_context(
|
||||
db, vendor_context
|
||||
)
|
||||
|
||||
if vendor:
|
||||
request.state.vendor = vendor
|
||||
@@ -477,7 +526,7 @@ class VendorContextMiddleware(BaseHTTPMiddleware):
|
||||
"detection_method": vendor_context.get("detection_method"),
|
||||
"original_path": request.url.path,
|
||||
"clean_path": request.state.clean_path,
|
||||
}
|
||||
},
|
||||
)
|
||||
else:
|
||||
logger.warning(
|
||||
@@ -485,7 +534,7 @@ class VendorContextMiddleware(BaseHTTPMiddleware):
|
||||
extra={
|
||||
"context": vendor_context,
|
||||
"detection_method": vendor_context.get("detection_method"),
|
||||
}
|
||||
},
|
||||
)
|
||||
request.state.vendor = None
|
||||
request.state.vendor_context = vendor_context
|
||||
@@ -498,7 +547,7 @@ class VendorContextMiddleware(BaseHTTPMiddleware):
|
||||
extra={
|
||||
"path": request.url.path,
|
||||
"host": request.headers.get("host", ""),
|
||||
}
|
||||
},
|
||||
)
|
||||
request.state.vendor = None
|
||||
request.state.vendor_context = None
|
||||
@@ -520,9 +569,9 @@ def require_vendor_context():
|
||||
vendor = get_current_vendor(request)
|
||||
if not vendor:
|
||||
from fastapi import HTTPException
|
||||
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Vendor not found or not active"
|
||||
status_code=404, detail="Vendor not found or not active"
|
||||
)
|
||||
return vendor
|
||||
|
||||
|
||||
Reference in New Issue
Block a user