# services/tenant/app/api/tenants.py """ Tenant API endpoints """ from fastapi import APIRouter, Depends, HTTPException, status, Request from sqlalchemy.ext.asyncio import AsyncSession from typing import List import structlog from app.core.database import get_db from app.schemas.tenants import ( BakeryRegistration, TenantResponse, TenantAccessResponse, TenantUpdate, TenantMemberResponse ) from app.services.tenant_service import TenantService from shared.auth.decorators import require_authentication, get_current_user, get_current_tenant_id logger = structlog.get_logger() router = APIRouter() @router.post("/bakeries", response_model=TenantResponse) @require_authentication async def register_bakery( bakery_data: BakeryRegistration, request: Request, db: AsyncSession = Depends(get_db) ): """Register a new bakery/tenant""" user = get_current_user(request) try: result = await TenantService.create_bakery(bakery_data, user["user_id"], db) logger.info(f"Bakery registered: {bakery_data.name} by {user['email']}") return result except Exception as e: logger.error(f"Bakery registration failed: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Bakery registration failed" ) @router.get("/tenants/{tenant_id}/access/{user_id}", response_model=TenantAccessResponse) async def verify_tenant_access( tenant_id: str, user_id: str, db: AsyncSession = Depends(get_db) ): """Verify if user has access to tenant - Called by Gateway""" try: access_info = await TenantService.verify_user_access(user_id, tenant_id, db) return access_info except Exception as e: logger.error(f"Access verification failed: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Access verification failed" ) @router.get("/users/{user_id}/tenants", response_model=List[TenantResponse]) @require_authentication async def get_user_tenants( user_id: str, request: Request, db: AsyncSession = Depends(get_db) ): """Get all tenants accessible by user""" current_user = get_current_user(request) # Users can only see their own tenants if current_user["user_id"] != user_id: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Access denied" ) try: tenants = await TenantService.get_user_tenants(user_id, db) return tenants except Exception as e: logger.error(f"Failed to get user tenants: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to retrieve tenants" ) @router.get("/tenants/{tenant_id}", response_model=TenantResponse) @require_authentication async def get_tenant( tenant_id: str, request: Request, db: AsyncSession = Depends(get_db) ): """Get tenant details""" user = get_current_user(request) # Verify user has access to tenant access = await TenantService.verify_user_access(user["user_id"], tenant_id, db) if not access.has_access: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Access denied to tenant" ) tenant = await TenantService.get_tenant_by_id(tenant_id, db) if not tenant: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Tenant not found" ) return tenant @router.put("/tenants/{tenant_id}", response_model=TenantResponse) @require_authentication async def update_tenant( tenant_id: str, update_data: TenantUpdate, request: Request, db: AsyncSession = Depends(get_db) ): """Update tenant information""" user = get_current_user(request) try: result = await TenantService.update_tenant(tenant_id, update_data, user["user_id"], db) return result except HTTPException: raise except Exception as e: logger.error(f"Tenant update failed: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Tenant update failed" ) @router.post("/tenants/{tenant_id}/members", response_model=TenantMemberResponse) @require_authentication async def add_team_member( tenant_id: str, user_id: str, role: str, request: Request, db: AsyncSession = Depends(get_db) ): """Add a team member to tenant""" current_user = get_current_user(request) try: result = await TenantService.add_team_member( tenant_id, user_id, role, current_user["user_id"], db ) return result except HTTPException: raise except Exception as e: logger.error(f"Add team member failed: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to add team member" )