# app/api/v1/public/vendors/auth.py """ Customer authentication endpoints (public-facing). This module provides: - Customer registration (vendor-scoped) - Customer login (vendor-scoped) - Customer password reset """ import logging from fastapi import APIRouter, Depends, Path from sqlalchemy.orm import Session from app.core.database import get_db from app.services.customer_service import customer_service from app.exceptions import VendorNotFoundException from models.schema.auth import LoginResponse, UserLogin from models.schema.customer import CustomerRegister, CustomerResponse from models.database.vendor import Vendor router = APIRouter(prefix="/auth") logger = logging.getLogger(__name__) @router.post("/{vendor_id}/customers/register", response_model=CustomerResponse) def register_customer( vendor_id: int, customer_data: CustomerRegister, db: Session = Depends(get_db) ): """ Register a new customer for a specific vendor. Customer accounts are vendor-scoped - each vendor has independent customers. Same email can be used for different vendors. """ # Verify vendor exists and is active vendor = db.query(Vendor).filter( Vendor.id == vendor_id, Vendor.is_active == True ).first() if not vendor: raise VendorNotFoundException(str(vendor_id), identifier_type="id") # Create customer account customer = customer_service.register_customer( db=db, vendor_id=vendor_id, customer_data=customer_data ) logger.info( f"New customer registered: {customer.email} " f"for vendor {vendor.vendor_code}" ) return CustomerResponse.model_validate(customer) @router.post("/{vendor_id}/customers/login", response_model=LoginResponse) def customer_login( vendor_id: int, user_credentials: UserLogin, db: Session = Depends(get_db) ): """ Customer login for a specific vendor. Authenticates customer and returns JWT token. Customer must belong to the specified vendor. """ # Verify vendor exists and is active vendor = db.query(Vendor).filter( Vendor.id == vendor_id, Vendor.is_active == True ).first() if not vendor: raise VendorNotFoundException(str(vendor_id), identifier_type="id") # Authenticate customer login_result = customer_service.login_customer( db=db, vendor_id=vendor_id, credentials=user_credentials ) logger.info( f"Customer login successful: {login_result['customer'].email} " f"for vendor {vendor.vendor_code}" ) return LoginResponse( access_token=login_result["token_data"]["access_token"], token_type=login_result["token_data"]["token_type"], expires_in=login_result["token_data"]["expires_in"], user=login_result["customer"], # Return customer as user ) @router.post("/{vendor_id}/customers/logout") def customer_logout(vendor_id: int): """ Customer logout. Client should remove token from storage. """ return {"message": "Logged out successfully"} @router.post("/{vendor_id}/customers/forgot-password") def forgot_password( vendor_id: int, email: str, db: Session = Depends(get_db) ): """ Request password reset for customer. Sends password reset email to customer if account exists. """ # Verify vendor exists vendor = db.query(Vendor).filter( Vendor.id == vendor_id, Vendor.is_active == True ).first() if not vendor: raise VendorNotFoundException(str(vendor_id), identifier_type="id") # TODO: Implement password reset logic # - Generate reset token # - Send email with reset link # - Store token in database logger.info(f"Password reset requested for {email} in vendor {vendor.vendor_code}") return { "message": "If an account exists, a password reset link has been sent", "email": email } @router.post("/{vendor_id}/customers/reset-password") def reset_password( vendor_id: int, token: str, new_password: str, db: Session = Depends(get_db) ): """ Reset customer password using reset token. Validates token and updates password. """ # Verify vendor exists vendor = db.query(Vendor).filter( Vendor.id == vendor_id, Vendor.is_active == True ).first() if not vendor: raise VendorNotFoundException(str(vendor_id), identifier_type="id") # TODO: Implement password reset logic # - Validate reset token # - Check token expiration # - Update password # - Invalidate token logger.info(f"Password reset completed for vendor {vendor.vendor_code}") return {"message": "Password reset successful"}