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:
@@ -21,7 +21,7 @@ Authentication:
|
||||
from fastapi import APIRouter
|
||||
|
||||
# Import shop routers
|
||||
from . import products, cart, orders, auth, content_pages
|
||||
from . import auth, cart, content_pages, orders, products
|
||||
|
||||
# Create shop router
|
||||
router = APIRouter()
|
||||
@@ -43,6 +43,8 @@ router.include_router(cart.router, tags=["shop-cart"])
|
||||
router.include_router(orders.router, tags=["shop-orders"])
|
||||
|
||||
# Content pages (public)
|
||||
router.include_router(content_pages.router, prefix="/content-pages", tags=["shop-content-pages"])
|
||||
router.include_router(
|
||||
content_pages.router, prefix="/content-pages", tags=["shop-content-pages"]
|
||||
)
|
||||
|
||||
__all__ = ["router"]
|
||||
|
||||
@@ -15,15 +15,16 @@ This prevents:
|
||||
"""
|
||||
|
||||
import logging
|
||||
from fastapi import APIRouter, Depends, Response, Request, HTTPException
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, Response
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.core.environment import should_use_secure_cookies
|
||||
from app.services.customer_service import customer_service
|
||||
from models.schema.auth import UserLogin
|
||||
from models.schema.customer import CustomerRegister, CustomerResponse
|
||||
from app.core.environment import should_use_secure_cookies
|
||||
from pydantic import BaseModel
|
||||
|
||||
router = APIRouter()
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -32,6 +33,7 @@ logger = logging.getLogger(__name__)
|
||||
# Response model for customer login
|
||||
class CustomerLoginResponse(BaseModel):
|
||||
"""Customer login response with token and customer data."""
|
||||
|
||||
access_token: str
|
||||
token_type: str
|
||||
expires_in: int
|
||||
@@ -40,9 +42,7 @@ class CustomerLoginResponse(BaseModel):
|
||||
|
||||
@router.post("/auth/register", response_model=CustomerResponse)
|
||||
def register_customer(
|
||||
request: Request,
|
||||
customer_data: CustomerRegister,
|
||||
db: Session = Depends(get_db)
|
||||
request: Request, customer_data: CustomerRegister, db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
Register a new customer for current vendor.
|
||||
@@ -59,12 +59,12 @@ def register_customer(
|
||||
- phone: Customer phone number (optional)
|
||||
"""
|
||||
# Get vendor from middleware
|
||||
vendor = getattr(request.state, 'vendor', None)
|
||||
vendor = getattr(request.state, "vendor", None)
|
||||
|
||||
if not vendor:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path."
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path.",
|
||||
)
|
||||
|
||||
logger.debug(
|
||||
@@ -73,14 +73,12 @@ def register_customer(
|
||||
"vendor_id": vendor.id,
|
||||
"vendor_code": vendor.subdomain,
|
||||
"email": customer_data.email,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# Create customer account
|
||||
customer = customer_service.register_customer(
|
||||
db=db,
|
||||
vendor_id=vendor.id,
|
||||
customer_data=customer_data
|
||||
db=db, vendor_id=vendor.id, customer_data=customer_data
|
||||
)
|
||||
|
||||
logger.info(
|
||||
@@ -89,7 +87,7 @@ def register_customer(
|
||||
"customer_id": customer.id,
|
||||
"vendor_id": vendor.id,
|
||||
"email": customer.email,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return CustomerResponse.model_validate(customer)
|
||||
@@ -100,7 +98,7 @@ def customer_login(
|
||||
request: Request,
|
||||
user_credentials: UserLogin,
|
||||
response: Response,
|
||||
db: Session = Depends(get_db)
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
Customer login for current vendor.
|
||||
@@ -121,12 +119,12 @@ def customer_login(
|
||||
- password: Customer password
|
||||
"""
|
||||
# Get vendor from middleware
|
||||
vendor = getattr(request.state, 'vendor', None)
|
||||
vendor = getattr(request.state, "vendor", None)
|
||||
|
||||
if not vendor:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path."
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path.",
|
||||
)
|
||||
|
||||
logger.debug(
|
||||
@@ -135,33 +133,39 @@ def customer_login(
|
||||
"vendor_id": vendor.id,
|
||||
"vendor_code": vendor.subdomain,
|
||||
"email_or_username": user_credentials.email_or_username,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# Authenticate customer
|
||||
login_result = customer_service.login_customer(
|
||||
db=db,
|
||||
vendor_id=vendor.id,
|
||||
credentials=user_credentials
|
||||
db=db, vendor_id=vendor.id, credentials=user_credentials
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"Customer login successful: {login_result['customer'].email} for vendor {vendor.subdomain}",
|
||||
extra={
|
||||
"customer_id": login_result['customer'].id,
|
||||
"customer_id": login_result["customer"].id,
|
||||
"vendor_id": vendor.id,
|
||||
"email": login_result['customer'].email,
|
||||
}
|
||||
"email": login_result["customer"].email,
|
||||
},
|
||||
)
|
||||
|
||||
# Calculate cookie path based on vendor access method
|
||||
vendor_context = getattr(request.state, 'vendor_context', None)
|
||||
access_method = vendor_context.get('detection_method', 'unknown') if vendor_context else 'unknown'
|
||||
vendor_context = getattr(request.state, "vendor_context", None)
|
||||
access_method = (
|
||||
vendor_context.get("detection_method", "unknown")
|
||||
if vendor_context
|
||||
else "unknown"
|
||||
)
|
||||
|
||||
cookie_path = "/shop" # Default for domain/subdomain access
|
||||
if access_method == "path":
|
||||
# For path-based access like /vendors/wizamart/shop
|
||||
full_prefix = vendor_context.get('full_prefix', '/vendor/') if vendor_context else '/vendor/'
|
||||
full_prefix = (
|
||||
vendor_context.get("full_prefix", "/vendor/")
|
||||
if vendor_context
|
||||
else "/vendor/"
|
||||
)
|
||||
cookie_path = f"{full_prefix}{vendor.subdomain}/shop"
|
||||
|
||||
# Set HTTP-only cookie for browser navigation
|
||||
@@ -180,10 +184,10 @@ def customer_login(
|
||||
f"Set customer_token cookie with {login_result['token_data']['expires_in']}s expiry "
|
||||
f"(path={cookie_path}, httponly=True, secure={should_use_secure_cookies()})",
|
||||
extra={
|
||||
"expires_in": login_result['token_data']['expires_in'],
|
||||
"expires_in": login_result["token_data"]["expires_in"],
|
||||
"secure": should_use_secure_cookies(),
|
||||
"cookie_path": cookie_path,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# Return full login response
|
||||
@@ -196,10 +200,7 @@ def customer_login(
|
||||
|
||||
|
||||
@router.post("/auth/logout")
|
||||
def customer_logout(
|
||||
request: Request,
|
||||
response: Response
|
||||
):
|
||||
def customer_logout(request: Request, response: Response):
|
||||
"""
|
||||
Customer logout for current vendor.
|
||||
|
||||
@@ -208,24 +209,32 @@ def customer_logout(
|
||||
Client should also remove token from localStorage.
|
||||
"""
|
||||
# Get vendor from middleware (for logging)
|
||||
vendor = getattr(request.state, 'vendor', None)
|
||||
vendor = getattr(request.state, "vendor", None)
|
||||
|
||||
logger.info(
|
||||
f"Customer logout for vendor {vendor.subdomain if vendor else 'unknown'}",
|
||||
extra={
|
||||
"vendor_id": vendor.id if vendor else None,
|
||||
"vendor_code": vendor.subdomain if vendor else None,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# Calculate cookie path based on vendor access method (must match login)
|
||||
vendor_context = getattr(request.state, 'vendor_context', None)
|
||||
access_method = vendor_context.get('detection_method', 'unknown') if vendor_context else 'unknown'
|
||||
vendor_context = getattr(request.state, "vendor_context", None)
|
||||
access_method = (
|
||||
vendor_context.get("detection_method", "unknown")
|
||||
if vendor_context
|
||||
else "unknown"
|
||||
)
|
||||
|
||||
cookie_path = "/shop" # Default for domain/subdomain access
|
||||
if access_method == "path" and vendor:
|
||||
# For path-based access like /vendors/wizamart/shop
|
||||
full_prefix = vendor_context.get('full_prefix', '/vendor/') if vendor_context else '/vendor/'
|
||||
full_prefix = (
|
||||
vendor_context.get("full_prefix", "/vendor/")
|
||||
if vendor_context
|
||||
else "/vendor/"
|
||||
)
|
||||
cookie_path = f"{full_prefix}{vendor.subdomain}/shop"
|
||||
|
||||
# Clear the cookie (must match path used when setting)
|
||||
@@ -240,11 +249,7 @@ def customer_logout(
|
||||
|
||||
|
||||
@router.post("/auth/forgot-password")
|
||||
def forgot_password(
|
||||
request: Request,
|
||||
email: str,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
def forgot_password(request: Request, email: str, db: Session = Depends(get_db)):
|
||||
"""
|
||||
Request password reset for customer.
|
||||
|
||||
@@ -255,12 +260,12 @@ def forgot_password(
|
||||
- email: Customer email address
|
||||
"""
|
||||
# Get vendor from middleware
|
||||
vendor = getattr(request.state, 'vendor', None)
|
||||
vendor = getattr(request.state, "vendor", None)
|
||||
|
||||
if not vendor:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path."
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path.",
|
||||
)
|
||||
|
||||
logger.debug(
|
||||
@@ -269,7 +274,7 @@ def forgot_password(
|
||||
"vendor_id": vendor.id,
|
||||
"vendor_code": vendor.subdomain,
|
||||
"email": email,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# TODO: Implement password reset functionality
|
||||
@@ -278,9 +283,7 @@ def forgot_password(
|
||||
# - Send reset email to customer
|
||||
# - Return success message (don't reveal if email exists)
|
||||
|
||||
logger.info(
|
||||
f"Password reset requested for {email} (vendor: {vendor.subdomain})"
|
||||
)
|
||||
logger.info(f"Password reset requested for {email} (vendor: {vendor.subdomain})")
|
||||
|
||||
return {
|
||||
"message": "If an account exists with this email, a password reset link has been sent."
|
||||
@@ -289,10 +292,7 @@ def forgot_password(
|
||||
|
||||
@router.post("/auth/reset-password")
|
||||
def reset_password(
|
||||
request: Request,
|
||||
reset_token: str,
|
||||
new_password: str,
|
||||
db: Session = Depends(get_db)
|
||||
request: Request, reset_token: str, new_password: str, db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
Reset customer password using reset token.
|
||||
@@ -304,12 +304,12 @@ def reset_password(
|
||||
- new_password: New password
|
||||
"""
|
||||
# Get vendor from middleware
|
||||
vendor = getattr(request.state, 'vendor', None)
|
||||
vendor = getattr(request.state, "vendor", None)
|
||||
|
||||
if not vendor:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path."
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path.",
|
||||
)
|
||||
|
||||
logger.debug(
|
||||
@@ -317,7 +317,7 @@ def reset_password(
|
||||
extra={
|
||||
"vendor_id": vendor.id,
|
||||
"vendor_code": vendor.subdomain,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# TODO: Implement password reset
|
||||
@@ -327,9 +327,7 @@ def reset_password(
|
||||
# - Invalidate reset token
|
||||
# - Return success
|
||||
|
||||
logger.info(
|
||||
f"Password reset completed (vendor: {vendor.subdomain})"
|
||||
)
|
||||
logger.info(f"Password reset completed (vendor: {vendor.subdomain})")
|
||||
|
||||
return {
|
||||
"message": "Password reset successfully. You can now log in with your new password."
|
||||
|
||||
@@ -8,18 +8,15 @@ No authentication required - uses session ID for cart tracking.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from fastapi import APIRouter, Depends, Path, Body, Request, HTTPException
|
||||
|
||||
from fastapi import APIRouter, Body, Depends, HTTPException, Path, Request
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.services.cart_service import cart_service
|
||||
from models.schema.cart import (
|
||||
AddToCartRequest,
|
||||
UpdateCartItemRequest,
|
||||
CartResponse,
|
||||
CartOperationResponse,
|
||||
ClearCartResponse,
|
||||
)
|
||||
from models.schema.cart import (AddToCartRequest, CartOperationResponse,
|
||||
CartResponse, ClearCartResponse,
|
||||
UpdateCartItemRequest)
|
||||
|
||||
router = APIRouter()
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -29,6 +26,7 @@ logger = logging.getLogger(__name__)
|
||||
# CART ENDPOINTS
|
||||
# ============================================================================
|
||||
|
||||
|
||||
@router.get("/cart/{session_id}", response_model=CartResponse)
|
||||
def get_cart(
|
||||
request: Request,
|
||||
@@ -45,12 +43,12 @@ def get_cart(
|
||||
- session_id: Unique session identifier for the cart
|
||||
"""
|
||||
# Get vendor from middleware
|
||||
vendor = getattr(request.state, 'vendor', None)
|
||||
vendor = getattr(request.state, "vendor", None)
|
||||
|
||||
if not vendor:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path."
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path.",
|
||||
)
|
||||
|
||||
logger.info(
|
||||
@@ -59,23 +57,19 @@ def get_cart(
|
||||
"vendor_id": vendor.id,
|
||||
"vendor_code": vendor.subdomain,
|
||||
"session_id": session_id,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
cart = cart_service.get_cart(
|
||||
db=db,
|
||||
vendor_id=vendor.id,
|
||||
session_id=session_id
|
||||
)
|
||||
cart = cart_service.get_cart(db=db, vendor_id=vendor.id, session_id=session_id)
|
||||
|
||||
logger.info(
|
||||
f"[SHOP_API] get_cart result: {len(cart.get('items', []))} items in cart",
|
||||
extra={
|
||||
"session_id": session_id,
|
||||
"vendor_id": vendor.id,
|
||||
"item_count": len(cart.get('items', [])),
|
||||
"total": cart.get('total', 0),
|
||||
}
|
||||
"item_count": len(cart.get("items", [])),
|
||||
"total": cart.get("total", 0),
|
||||
},
|
||||
)
|
||||
|
||||
return CartResponse.from_service_dict(cart)
|
||||
@@ -102,12 +96,12 @@ def add_to_cart(
|
||||
- quantity: Quantity to add (default: 1)
|
||||
"""
|
||||
# Get vendor from middleware
|
||||
vendor = getattr(request.state, 'vendor', None)
|
||||
vendor = getattr(request.state, "vendor", None)
|
||||
|
||||
if not vendor:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path."
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path.",
|
||||
)
|
||||
|
||||
logger.info(
|
||||
@@ -118,7 +112,7 @@ def add_to_cart(
|
||||
"session_id": session_id,
|
||||
"product_id": cart_data.product_id,
|
||||
"quantity": cart_data.quantity,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
result = cart_service.add_to_cart(
|
||||
@@ -126,7 +120,7 @@ def add_to_cart(
|
||||
vendor_id=vendor.id,
|
||||
session_id=session_id,
|
||||
product_id=cart_data.product_id,
|
||||
quantity=cart_data.quantity
|
||||
quantity=cart_data.quantity,
|
||||
)
|
||||
|
||||
logger.info(
|
||||
@@ -134,13 +128,15 @@ def add_to_cart(
|
||||
extra={
|
||||
"session_id": session_id,
|
||||
"result": result,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return CartOperationResponse(**result)
|
||||
|
||||
|
||||
@router.put("/cart/{session_id}/items/{product_id}", response_model=CartOperationResponse)
|
||||
@router.put(
|
||||
"/cart/{session_id}/items/{product_id}", response_model=CartOperationResponse
|
||||
)
|
||||
def update_cart_item(
|
||||
request: Request,
|
||||
session_id: str = Path(..., description="Shopping session ID"),
|
||||
@@ -162,12 +158,12 @@ def update_cart_item(
|
||||
- quantity: New quantity (must be >= 1)
|
||||
"""
|
||||
# Get vendor from middleware
|
||||
vendor = getattr(request.state, 'vendor', None)
|
||||
vendor = getattr(request.state, "vendor", None)
|
||||
|
||||
if not vendor:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path."
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path.",
|
||||
)
|
||||
|
||||
logger.debug(
|
||||
@@ -178,7 +174,7 @@ def update_cart_item(
|
||||
"session_id": session_id,
|
||||
"product_id": product_id,
|
||||
"quantity": cart_data.quantity,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
result = cart_service.update_cart_item(
|
||||
@@ -186,13 +182,15 @@ def update_cart_item(
|
||||
vendor_id=vendor.id,
|
||||
session_id=session_id,
|
||||
product_id=product_id,
|
||||
quantity=cart_data.quantity
|
||||
quantity=cart_data.quantity,
|
||||
)
|
||||
|
||||
return CartOperationResponse(**result)
|
||||
|
||||
|
||||
@router.delete("/cart/{session_id}/items/{product_id}", response_model=CartOperationResponse)
|
||||
@router.delete(
|
||||
"/cart/{session_id}/items/{product_id}", response_model=CartOperationResponse
|
||||
)
|
||||
def remove_from_cart(
|
||||
request: Request,
|
||||
session_id: str = Path(..., description="Shopping session ID"),
|
||||
@@ -210,12 +208,12 @@ def remove_from_cart(
|
||||
- product_id: ID of product to remove
|
||||
"""
|
||||
# Get vendor from middleware
|
||||
vendor = getattr(request.state, 'vendor', None)
|
||||
vendor = getattr(request.state, "vendor", None)
|
||||
|
||||
if not vendor:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path."
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path.",
|
||||
)
|
||||
|
||||
logger.debug(
|
||||
@@ -225,14 +223,11 @@ def remove_from_cart(
|
||||
"vendor_code": vendor.subdomain,
|
||||
"session_id": session_id,
|
||||
"product_id": product_id,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
result = cart_service.remove_from_cart(
|
||||
db=db,
|
||||
vendor_id=vendor.id,
|
||||
session_id=session_id,
|
||||
product_id=product_id
|
||||
db=db, vendor_id=vendor.id, session_id=session_id, product_id=product_id
|
||||
)
|
||||
|
||||
return CartOperationResponse(**result)
|
||||
@@ -254,12 +249,12 @@ def clear_cart(
|
||||
- session_id: Unique session identifier for the cart
|
||||
"""
|
||||
# Get vendor from middleware
|
||||
vendor = getattr(request.state, 'vendor', None)
|
||||
vendor = getattr(request.state, "vendor", None)
|
||||
|
||||
if not vendor:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path."
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path.",
|
||||
)
|
||||
|
||||
logger.debug(
|
||||
@@ -268,13 +263,9 @@ def clear_cart(
|
||||
"vendor_id": vendor.id,
|
||||
"vendor_code": vendor.subdomain,
|
||||
"session_id": session_id,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
result = cart_service.clear_cart(
|
||||
db=db,
|
||||
vendor_id=vendor.id,
|
||||
session_id=session_id
|
||||
)
|
||||
result = cart_service.clear_cart(db=db, vendor_id=vendor.id, session_id=session_id)
|
||||
|
||||
return ClearCartResponse(**result)
|
||||
|
||||
@@ -8,6 +8,7 @@ No authentication required.
|
||||
|
||||
import logging
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.orm import Session
|
||||
@@ -23,8 +24,10 @@ logger = logging.getLogger(__name__)
|
||||
# RESPONSE SCHEMAS
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class PublicContentPageResponse(BaseModel):
|
||||
"""Public content page response (no internal IDs)."""
|
||||
|
||||
slug: str
|
||||
title: str
|
||||
content: str
|
||||
@@ -36,6 +39,7 @@ class PublicContentPageResponse(BaseModel):
|
||||
|
||||
class ContentPageListItem(BaseModel):
|
||||
"""Content page list item for navigation."""
|
||||
|
||||
slug: str
|
||||
title: str
|
||||
show_in_footer: bool
|
||||
@@ -47,25 +51,21 @@ class ContentPageListItem(BaseModel):
|
||||
# PUBLIC ENDPOINTS
|
||||
# ============================================================================
|
||||
|
||||
|
||||
@router.get("/navigation", response_model=List[ContentPageListItem])
|
||||
def get_navigation_pages(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
def get_navigation_pages(request: Request, db: Session = Depends(get_db)):
|
||||
"""
|
||||
Get list of content pages for navigation (footer/header).
|
||||
|
||||
Uses vendor from request.state (set by middleware).
|
||||
Returns vendor overrides + platform defaults.
|
||||
"""
|
||||
vendor = getattr(request.state, 'vendor', None)
|
||||
vendor = getattr(request.state, "vendor", None)
|
||||
vendor_id = vendor.id if vendor else None
|
||||
|
||||
# Get all published pages for this vendor
|
||||
pages = content_page_service.list_pages_for_vendor(
|
||||
db,
|
||||
vendor_id=vendor_id,
|
||||
include_unpublished=False
|
||||
db, vendor_id=vendor_id, include_unpublished=False
|
||||
)
|
||||
|
||||
return [
|
||||
@@ -81,25 +81,21 @@ def get_navigation_pages(
|
||||
|
||||
|
||||
@router.get("/{slug}", response_model=PublicContentPageResponse)
|
||||
def get_content_page(
|
||||
slug: str,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
def get_content_page(slug: str, request: Request, db: Session = Depends(get_db)):
|
||||
"""
|
||||
Get a specific content page by slug.
|
||||
|
||||
Uses vendor from request.state (set by middleware).
|
||||
Returns vendor override if exists, otherwise platform default.
|
||||
"""
|
||||
vendor = getattr(request.state, 'vendor', None)
|
||||
vendor = getattr(request.state, "vendor", None)
|
||||
vendor_id = vendor.id if vendor else None
|
||||
|
||||
page = content_page_service.get_page_for_vendor(
|
||||
db,
|
||||
slug=slug,
|
||||
vendor_id=vendor_id,
|
||||
include_unpublished=False # Only show published pages
|
||||
include_unpublished=False, # Only show published pages
|
||||
)
|
||||
|
||||
if not page:
|
||||
|
||||
@@ -10,31 +10,23 @@ Requires customer authentication for most operations.
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, Path, Query, Request, HTTPException
|
||||
from fastapi import APIRouter, Depends, HTTPException, Path, Query, Request
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.api.deps import get_current_customer_api
|
||||
from app.services.order_service import order_service
|
||||
from app.core.database import get_db
|
||||
from app.services.customer_service import customer_service
|
||||
from models.schema.order import (
|
||||
OrderCreate,
|
||||
OrderResponse,
|
||||
OrderDetailResponse,
|
||||
OrderListResponse
|
||||
)
|
||||
from models.database.user import User
|
||||
from app.services.order_service import order_service
|
||||
from models.database.customer import Customer
|
||||
from models.database.user import User
|
||||
from models.schema.order import (OrderCreate, OrderDetailResponse,
|
||||
OrderListResponse, OrderResponse)
|
||||
|
||||
router = APIRouter()
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_customer_from_user(
|
||||
request: Request,
|
||||
user: User,
|
||||
db: Session
|
||||
) -> Customer:
|
||||
def get_customer_from_user(request: Request, user: User, db: Session) -> Customer:
|
||||
"""
|
||||
Helper to get Customer record from authenticated User.
|
||||
|
||||
@@ -49,25 +41,22 @@ def get_customer_from_user(
|
||||
Raises:
|
||||
HTTPException: If customer not found or vendor mismatch
|
||||
"""
|
||||
vendor = getattr(request.state, 'vendor', None)
|
||||
vendor = getattr(request.state, "vendor", None)
|
||||
|
||||
if not vendor:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path."
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path.",
|
||||
)
|
||||
|
||||
# Find customer record for this user and vendor
|
||||
customer = customer_service.get_customer_by_user_id(
|
||||
db=db,
|
||||
vendor_id=vendor.id,
|
||||
user_id=user.id
|
||||
db=db, vendor_id=vendor.id, user_id=user.id
|
||||
)
|
||||
|
||||
if not customer:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Customer account not found for current vendor"
|
||||
status_code=404, detail="Customer account not found for current vendor"
|
||||
)
|
||||
|
||||
return customer
|
||||
@@ -91,12 +80,12 @@ def place_order(
|
||||
- Order data including shipping address, payment method, etc.
|
||||
"""
|
||||
# Get vendor from middleware
|
||||
vendor = getattr(request.state, 'vendor', None)
|
||||
vendor = getattr(request.state, "vendor", None)
|
||||
|
||||
if not vendor:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path."
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path.",
|
||||
)
|
||||
|
||||
# Get customer record
|
||||
@@ -109,14 +98,12 @@ def place_order(
|
||||
"vendor_code": vendor.subdomain,
|
||||
"customer_id": customer.id,
|
||||
"user_id": current_user.id,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# Create order
|
||||
order = order_service.create_order(
|
||||
db=db,
|
||||
vendor_id=vendor.id,
|
||||
order_data=order_data
|
||||
db=db, vendor_id=vendor.id, order_data=order_data
|
||||
)
|
||||
|
||||
logger.info(
|
||||
@@ -127,7 +114,7 @@ def place_order(
|
||||
"order_number": order.order_number,
|
||||
"customer_id": customer.id,
|
||||
"total_amount": float(order.total_amount),
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# TODO: Update customer stats
|
||||
@@ -156,12 +143,12 @@ def get_my_orders(
|
||||
- limit: Maximum number of orders to return
|
||||
"""
|
||||
# Get vendor from middleware
|
||||
vendor = getattr(request.state, 'vendor', None)
|
||||
vendor = getattr(request.state, "vendor", None)
|
||||
|
||||
if not vendor:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path."
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path.",
|
||||
)
|
||||
|
||||
# Get customer record
|
||||
@@ -175,23 +162,19 @@ def get_my_orders(
|
||||
"customer_id": customer.id,
|
||||
"skip": skip,
|
||||
"limit": limit,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# Get orders
|
||||
orders, total = order_service.get_customer_orders(
|
||||
db=db,
|
||||
vendor_id=vendor.id,
|
||||
customer_id=customer.id,
|
||||
skip=skip,
|
||||
limit=limit
|
||||
db=db, vendor_id=vendor.id, customer_id=customer.id, skip=skip, limit=limit
|
||||
)
|
||||
|
||||
return OrderListResponse(
|
||||
orders=[OrderResponse.model_validate(o) for o in orders],
|
||||
total=total,
|
||||
skip=skip,
|
||||
limit=limit
|
||||
limit=limit,
|
||||
)
|
||||
|
||||
|
||||
@@ -212,12 +195,12 @@ def get_order_details(
|
||||
- order_id: ID of the order to retrieve
|
||||
"""
|
||||
# Get vendor from middleware
|
||||
vendor = getattr(request.state, 'vendor', None)
|
||||
vendor = getattr(request.state, "vendor", None)
|
||||
|
||||
if not vendor:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path."
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path.",
|
||||
)
|
||||
|
||||
# Get customer record
|
||||
@@ -230,19 +213,16 @@ def get_order_details(
|
||||
"vendor_code": vendor.subdomain,
|
||||
"customer_id": customer.id,
|
||||
"order_id": order_id,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# Get order
|
||||
order = order_service.get_order(
|
||||
db=db,
|
||||
vendor_id=vendor.id,
|
||||
order_id=order_id
|
||||
)
|
||||
order = order_service.get_order(db=db, vendor_id=vendor.id, order_id=order_id)
|
||||
|
||||
# Verify order belongs to customer
|
||||
if order.customer_id != customer.id:
|
||||
from app.exceptions import OrderNotFoundException
|
||||
|
||||
raise OrderNotFoundException(str(order_id))
|
||||
|
||||
return OrderDetailResponse.model_validate(order)
|
||||
|
||||
@@ -10,12 +10,13 @@ No authentication required.
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, Query, Path, Request, HTTPException
|
||||
from fastapi import APIRouter, Depends, HTTPException, Path, Query, Request
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.services.product_service import product_service
|
||||
from models.schema.product import ProductResponse, ProductDetailResponse, ProductListResponse
|
||||
from models.schema.product import (ProductDetailResponse, ProductListResponse,
|
||||
ProductResponse)
|
||||
|
||||
router = APIRouter()
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -27,7 +28,9 @@ def get_product_catalog(
|
||||
skip: int = Query(0, ge=0),
|
||||
limit: int = Query(100, ge=1, le=1000),
|
||||
search: Optional[str] = Query(None, description="Search products by name"),
|
||||
is_featured: Optional[bool] = Query(None, description="Filter by featured products"),
|
||||
is_featured: Optional[bool] = Query(
|
||||
None, description="Filter by featured products"
|
||||
),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
@@ -44,12 +47,12 @@ def get_product_catalog(
|
||||
- is_featured: Filter by featured products only
|
||||
"""
|
||||
# Get vendor from middleware (injected by VendorContextMiddleware)
|
||||
vendor = getattr(request.state, 'vendor', None)
|
||||
vendor = getattr(request.state, "vendor", None)
|
||||
|
||||
if not vendor:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path."
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path.",
|
||||
)
|
||||
|
||||
logger.debug(
|
||||
@@ -61,7 +64,7 @@ def get_product_catalog(
|
||||
"limit": limit,
|
||||
"search": search,
|
||||
"is_featured": is_featured,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# Get only active products for public view
|
||||
@@ -71,14 +74,14 @@ def get_product_catalog(
|
||||
skip=skip,
|
||||
limit=limit,
|
||||
is_active=True, # Only show active products to customers
|
||||
is_featured=is_featured
|
||||
is_featured=is_featured,
|
||||
)
|
||||
|
||||
return ProductListResponse(
|
||||
products=[ProductResponse.model_validate(p) for p in products],
|
||||
total=total,
|
||||
skip=skip,
|
||||
limit=limit
|
||||
limit=limit,
|
||||
)
|
||||
|
||||
|
||||
@@ -98,12 +101,12 @@ def get_product_details(
|
||||
- product_id: ID of the product to retrieve
|
||||
"""
|
||||
# Get vendor from middleware
|
||||
vendor = getattr(request.state, 'vendor', None)
|
||||
vendor = getattr(request.state, "vendor", None)
|
||||
|
||||
if not vendor:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path."
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path.",
|
||||
)
|
||||
|
||||
logger.debug(
|
||||
@@ -112,18 +115,17 @@ def get_product_details(
|
||||
"vendor_id": vendor.id,
|
||||
"vendor_code": vendor.subdomain,
|
||||
"product_id": product_id,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
product = product_service.get_product(
|
||||
db=db,
|
||||
vendor_id=vendor.id,
|
||||
product_id=product_id
|
||||
db=db, vendor_id=vendor.id, product_id=product_id
|
||||
)
|
||||
|
||||
# Check if product is active
|
||||
if not product.is_active:
|
||||
from app.exceptions import ProductNotActiveException
|
||||
|
||||
raise ProductNotActiveException(str(product_id))
|
||||
|
||||
return ProductDetailResponse.model_validate(product)
|
||||
@@ -150,12 +152,12 @@ def search_products(
|
||||
- limit: Maximum number of results to return
|
||||
"""
|
||||
# Get vendor from middleware
|
||||
vendor = getattr(request.state, 'vendor', None)
|
||||
vendor = getattr(request.state, "vendor", None)
|
||||
|
||||
if not vendor:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path."
|
||||
detail="Vendor not found. Please access via vendor domain/subdomain/path.",
|
||||
)
|
||||
|
||||
logger.debug(
|
||||
@@ -166,22 +168,18 @@ def search_products(
|
||||
"query": q,
|
||||
"skip": skip,
|
||||
"limit": limit,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
# TODO: Implement full-text search functionality
|
||||
# For now, return filtered products
|
||||
products, total = product_service.get_vendor_products(
|
||||
db=db,
|
||||
vendor_id=vendor.id,
|
||||
skip=skip,
|
||||
limit=limit,
|
||||
is_active=True
|
||||
db=db, vendor_id=vendor.id, skip=skip, limit=limit, is_active=True
|
||||
)
|
||||
|
||||
return ProductListResponse(
|
||||
products=[ProductResponse.model_validate(p) for p in products],
|
||||
total=total,
|
||||
skip=skip,
|
||||
limit=limit
|
||||
limit=limit,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user