2025-07-19 17:49:03 +02:00
|
|
|
"""
|
2025-08-08 09:08:41 +02:00
|
|
|
Enhanced Tenant API endpoints using repository pattern and dependency injection
|
2025-07-19 17:49:03 +02:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import structlog
|
2025-08-02 09:41:50 +02:00
|
|
|
from datetime import datetime
|
2025-08-08 09:08:41 +02:00
|
|
|
from fastapi import APIRouter, Depends, HTTPException, status, Path, Query
|
|
|
|
|
from typing import List, Dict, Any, Optional
|
|
|
|
|
from uuid import UUID
|
2025-07-19 17:49:03 +02:00
|
|
|
|
|
|
|
|
from app.schemas.tenants import (
|
|
|
|
|
BakeryRegistration, TenantResponse, TenantAccessResponse,
|
2025-08-08 09:08:41 +02:00
|
|
|
TenantUpdate, TenantMemberResponse, TenantSearchRequest
|
2025-07-19 17:49:03 +02:00
|
|
|
)
|
2025-08-08 09:08:41 +02:00
|
|
|
from app.services.tenant_service import EnhancedTenantService
|
2025-07-21 14:41:33 +02:00
|
|
|
from shared.auth.decorators import (
|
|
|
|
|
get_current_user_dep,
|
2025-08-02 22:36:02 +02:00
|
|
|
require_admin_role,
|
|
|
|
|
require_admin_role_dep
|
2025-07-21 14:41:33 +02:00
|
|
|
)
|
2025-08-08 09:08:41 +02:00
|
|
|
from shared.database.base import create_database_manager
|
|
|
|
|
from shared.monitoring.metrics import track_endpoint_metrics
|
2025-07-19 17:49:03 +02:00
|
|
|
|
|
|
|
|
logger = structlog.get_logger()
|
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
2025-08-08 09:08:41 +02:00
|
|
|
# Dependency injection for enhanced tenant service
|
|
|
|
|
def get_enhanced_tenant_service():
|
2025-08-15 22:40:19 +02:00
|
|
|
try:
|
|
|
|
|
from app.core.config import settings
|
|
|
|
|
database_manager = create_database_manager(settings.DATABASE_URL, "tenant-service")
|
|
|
|
|
return EnhancedTenantService(database_manager)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("Failed to create enhanced tenant service", error=str(e))
|
|
|
|
|
raise HTTPException(status_code=500, detail="Service initialization failed")
|
2025-08-08 09:08:41 +02:00
|
|
|
|
2025-07-20 23:15:57 +02:00
|
|
|
@router.post("/tenants/register", response_model=TenantResponse)
|
2025-08-08 09:08:41 +02:00
|
|
|
async def register_bakery_enhanced(
|
2025-07-19 17:49:03 +02:00
|
|
|
bakery_data: BakeryRegistration,
|
2025-07-21 14:41:33 +02:00
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
2025-08-08 09:08:41 +02:00
|
|
|
tenant_service: EnhancedTenantService = Depends(get_enhanced_tenant_service)
|
2025-07-19 17:49:03 +02:00
|
|
|
):
|
2025-08-08 09:08:41 +02:00
|
|
|
"""Register a new bakery/tenant with enhanced validation and features"""
|
2025-07-19 17:49:03 +02:00
|
|
|
|
|
|
|
|
try:
|
2025-08-08 09:08:41 +02:00
|
|
|
result = await tenant_service.create_bakery(
|
|
|
|
|
bakery_data,
|
|
|
|
|
current_user["user_id"]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
logger.info("Bakery registered successfully",
|
|
|
|
|
name=bakery_data.name,
|
|
|
|
|
owner_email=current_user.get('email'),
|
|
|
|
|
tenant_id=result.id)
|
|
|
|
|
|
2025-07-19 17:49:03 +02:00
|
|
|
return result
|
|
|
|
|
|
2025-08-08 09:08:41 +02:00
|
|
|
except HTTPException:
|
|
|
|
|
raise
|
2025-07-19 17:49:03 +02:00
|
|
|
except Exception as e:
|
2025-08-08 09:08:41 +02:00
|
|
|
logger.error("Bakery registration failed",
|
|
|
|
|
name=bakery_data.name,
|
|
|
|
|
owner_id=current_user["user_id"],
|
|
|
|
|
error=str(e))
|
2025-07-19 17:49:03 +02:00
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Bakery registration failed"
|
|
|
|
|
)
|
|
|
|
|
|
2025-09-09 07:32:59 +02:00
|
|
|
@router.get("/tenants/{tenant_id}/my-access", response_model=TenantAccessResponse)
|
|
|
|
|
async def get_current_user_tenant_access(
|
|
|
|
|
tenant_id: UUID = Path(..., description="Tenant ID"),
|
|
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep)
|
|
|
|
|
):
|
|
|
|
|
"""Get current user's access to tenant with role and permissions"""
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
# Create tenant service directly
|
|
|
|
|
from app.core.config import settings
|
|
|
|
|
database_manager = create_database_manager(settings.DATABASE_URL, "tenant-service")
|
|
|
|
|
tenant_service = EnhancedTenantService(database_manager)
|
|
|
|
|
|
|
|
|
|
access_info = await tenant_service.verify_user_access(current_user["user_id"], str(tenant_id))
|
|
|
|
|
return access_info
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("Current user access verification failed",
|
|
|
|
|
user_id=current_user["user_id"],
|
|
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
error=str(e))
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Access verification failed"
|
|
|
|
|
)
|
|
|
|
|
|
2025-07-19 17:49:03 +02:00
|
|
|
@router.get("/tenants/{tenant_id}/access/{user_id}", response_model=TenantAccessResponse)
|
2025-08-08 09:08:41 +02:00
|
|
|
async def verify_tenant_access_enhanced(
|
2025-07-26 18:46:52 +02:00
|
|
|
tenant_id: UUID = Path(..., description="Tenant ID"),
|
2025-08-08 09:08:41 +02:00
|
|
|
user_id: str = Path(..., description="User ID")
|
2025-07-19 17:49:03 +02:00
|
|
|
):
|
2025-08-08 09:08:41 +02:00
|
|
|
"""Verify if user has access to tenant - Enhanced version with detailed permissions"""
|
|
|
|
|
|
2025-07-29 12:01:56 +02:00
|
|
|
# Check if this is a service request
|
2025-08-02 19:09:43 +02:00
|
|
|
if user_id in ["training-service", "data-service", "forecasting-service", "auth-service"]:
|
2025-07-29 12:01:56 +02:00
|
|
|
# Services have access to all tenants for their operations
|
|
|
|
|
return TenantAccessResponse(
|
|
|
|
|
has_access=True,
|
|
|
|
|
role="service",
|
|
|
|
|
permissions=["read", "write"]
|
|
|
|
|
)
|
2025-07-19 17:49:03 +02:00
|
|
|
|
|
|
|
|
try:
|
2025-08-08 09:08:41 +02:00
|
|
|
# Create tenant service directly
|
|
|
|
|
from app.core.config import settings
|
|
|
|
|
database_manager = create_database_manager(settings.DATABASE_URL, "tenant-service")
|
|
|
|
|
tenant_service = EnhancedTenantService(database_manager)
|
|
|
|
|
|
|
|
|
|
access_info = await tenant_service.verify_user_access(user_id, str(tenant_id))
|
2025-07-19 17:49:03 +02:00
|
|
|
return access_info
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
2025-08-08 09:08:41 +02:00
|
|
|
logger.error("Access verification failed",
|
|
|
|
|
user_id=user_id,
|
|
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
error=str(e))
|
2025-07-19 17:49:03 +02:00
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Access verification failed"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@router.get("/tenants/{tenant_id}", response_model=TenantResponse)
|
2025-08-08 09:08:41 +02:00
|
|
|
@track_endpoint_metrics("tenant_get")
|
|
|
|
|
async def get_tenant_enhanced(
|
2025-07-26 18:46:52 +02:00
|
|
|
tenant_id: UUID = Path(..., description="Tenant ID"),
|
2025-07-21 14:41:33 +02:00
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
2025-08-08 09:08:41 +02:00
|
|
|
tenant_service: EnhancedTenantService = Depends(get_enhanced_tenant_service)
|
2025-07-19 17:49:03 +02:00
|
|
|
):
|
2025-08-08 09:08:41 +02:00
|
|
|
"""Get tenant by ID with enhanced data and access control"""
|
2025-07-19 17:49:03 +02:00
|
|
|
|
2025-08-08 09:08:41 +02:00
|
|
|
tenant = await tenant_service.get_tenant_by_id(str(tenant_id))
|
2025-07-19 17:49:03 +02:00
|
|
|
if not tenant:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
|
|
|
detail="Tenant not found"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return tenant
|
|
|
|
|
|
2025-08-08 09:08:41 +02:00
|
|
|
@router.get("/tenants/subdomain/{subdomain}", response_model=TenantResponse)
|
|
|
|
|
@track_endpoint_metrics("tenant_get_by_subdomain")
|
|
|
|
|
async def get_tenant_by_subdomain_enhanced(
|
|
|
|
|
subdomain: str = Path(..., description="Tenant subdomain"),
|
|
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
|
|
|
|
tenant_service: EnhancedTenantService = Depends(get_enhanced_tenant_service)
|
|
|
|
|
):
|
|
|
|
|
"""Get tenant by subdomain with enhanced validation"""
|
|
|
|
|
|
|
|
|
|
tenant = await tenant_service.get_tenant_by_subdomain(subdomain)
|
|
|
|
|
if not tenant:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
|
|
|
detail="Tenant not found"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Verify user has access to this tenant
|
|
|
|
|
access = await tenant_service.verify_user_access(current_user["user_id"], tenant.id)
|
|
|
|
|
if not access.has_access:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
|
|
|
detail="Access denied to tenant"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return tenant
|
|
|
|
|
|
|
|
|
|
@router.get("/tenants/user/{user_id}/owned", response_model=List[TenantResponse])
|
2025-08-15 22:40:19 +02:00
|
|
|
# @track_endpoint_metrics("tenant_get_user_owned") # Temporarily disabled
|
2025-08-08 09:08:41 +02:00
|
|
|
async def get_user_owned_tenants_enhanced(
|
|
|
|
|
user_id: str = Path(..., description="User ID"),
|
|
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
|
|
|
|
tenant_service: EnhancedTenantService = Depends(get_enhanced_tenant_service)
|
|
|
|
|
):
|
|
|
|
|
"""Get all tenants owned by a user with enhanced data"""
|
|
|
|
|
|
|
|
|
|
# Users can only get their own tenants unless they're admin
|
|
|
|
|
user_role = current_user.get('role', '').lower()
|
|
|
|
|
if user_id != current_user["user_id"] and user_role != 'admin':
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
|
|
|
detail="Can only access your own tenants"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
tenants = await tenant_service.get_user_tenants(user_id)
|
|
|
|
|
return tenants
|
|
|
|
|
|
|
|
|
|
@router.get("/tenants/search", response_model=List[TenantResponse])
|
|
|
|
|
@track_endpoint_metrics("tenant_search")
|
|
|
|
|
async def search_tenants_enhanced(
|
|
|
|
|
search_term: str = Query(..., description="Search term"),
|
|
|
|
|
business_type: Optional[str] = Query(None, description="Business type filter"),
|
|
|
|
|
city: Optional[str] = Query(None, description="City filter"),
|
|
|
|
|
skip: int = Query(0, ge=0, description="Number of records to skip"),
|
|
|
|
|
limit: int = Query(50, ge=1, le=100, description="Maximum number of records to return"),
|
|
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
|
|
|
|
tenant_service: EnhancedTenantService = Depends(get_enhanced_tenant_service)
|
|
|
|
|
):
|
|
|
|
|
"""Search tenants with advanced filters and pagination"""
|
|
|
|
|
|
|
|
|
|
tenants = await tenant_service.search_tenants(
|
|
|
|
|
search_term=search_term,
|
|
|
|
|
business_type=business_type,
|
|
|
|
|
city=city,
|
|
|
|
|
skip=skip,
|
|
|
|
|
limit=limit
|
|
|
|
|
)
|
|
|
|
|
return tenants
|
|
|
|
|
|
|
|
|
|
@router.get("/tenants/nearby", response_model=List[TenantResponse])
|
|
|
|
|
@track_endpoint_metrics("tenant_get_nearby")
|
|
|
|
|
async def get_nearby_tenants_enhanced(
|
|
|
|
|
latitude: float = Query(..., description="Latitude coordinate"),
|
|
|
|
|
longitude: float = Query(..., description="Longitude coordinate"),
|
|
|
|
|
radius_km: float = Query(10.0, ge=0.1, le=100.0, description="Search radius in kilometers"),
|
|
|
|
|
limit: int = Query(50, ge=1, le=100, description="Maximum number of results"),
|
|
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
|
|
|
|
tenant_service: EnhancedTenantService = Depends(get_enhanced_tenant_service)
|
|
|
|
|
):
|
|
|
|
|
"""Get tenants near a geographic location with enhanced geospatial search"""
|
|
|
|
|
|
|
|
|
|
tenants = await tenant_service.get_tenants_near_location(
|
|
|
|
|
latitude=latitude,
|
|
|
|
|
longitude=longitude,
|
|
|
|
|
radius_km=radius_km,
|
|
|
|
|
limit=limit
|
|
|
|
|
)
|
|
|
|
|
return tenants
|
|
|
|
|
|
2025-07-19 17:49:03 +02:00
|
|
|
@router.put("/tenants/{tenant_id}", response_model=TenantResponse)
|
2025-08-08 09:08:41 +02:00
|
|
|
async def update_tenant_enhanced(
|
2025-07-19 17:49:03 +02:00
|
|
|
update_data: TenantUpdate,
|
2025-07-26 18:46:52 +02:00
|
|
|
tenant_id: UUID = Path(..., description="Tenant ID"),
|
2025-07-21 14:41:33 +02:00
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
2025-08-08 09:08:41 +02:00
|
|
|
tenant_service: EnhancedTenantService = Depends(get_enhanced_tenant_service)
|
2025-07-19 17:49:03 +02:00
|
|
|
):
|
2025-08-08 09:08:41 +02:00
|
|
|
"""Update tenant information with enhanced validation and permission checks"""
|
2025-07-21 14:41:33 +02:00
|
|
|
|
2025-07-19 17:49:03 +02:00
|
|
|
try:
|
2025-08-08 09:08:41 +02:00
|
|
|
result = await tenant_service.update_tenant(
|
|
|
|
|
str(tenant_id),
|
|
|
|
|
update_data,
|
|
|
|
|
current_user["user_id"]
|
|
|
|
|
)
|
2025-07-19 17:49:03 +02:00
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
except HTTPException:
|
|
|
|
|
raise
|
|
|
|
|
except Exception as e:
|
2025-08-08 09:08:41 +02:00
|
|
|
logger.error("Tenant update failed",
|
|
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
user_id=current_user["user_id"],
|
|
|
|
|
error=str(e))
|
2025-07-19 17:49:03 +02:00
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Tenant update failed"
|
|
|
|
|
)
|
|
|
|
|
|
2025-08-08 09:08:41 +02:00
|
|
|
@router.put("/tenants/{tenant_id}/model-status")
|
|
|
|
|
@track_endpoint_metrics("tenant_update_model_status")
|
|
|
|
|
async def update_tenant_model_status_enhanced(
|
|
|
|
|
tenant_id: UUID = Path(..., description="Tenant ID"),
|
|
|
|
|
model_trained: bool = Query(..., description="Whether model is trained"),
|
|
|
|
|
last_training_date: Optional[datetime] = Query(None, description="Last training date"),
|
|
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
|
|
|
|
tenant_service: EnhancedTenantService = Depends(get_enhanced_tenant_service)
|
|
|
|
|
):
|
|
|
|
|
"""Update tenant model training status with enhanced tracking"""
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
result = await tenant_service.update_model_status(
|
|
|
|
|
str(tenant_id),
|
|
|
|
|
model_trained,
|
|
|
|
|
current_user["user_id"],
|
|
|
|
|
last_training_date
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
except HTTPException:
|
|
|
|
|
raise
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("Model status update failed",
|
|
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
error=str(e))
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to update model status"
|
|
|
|
|
)
|
|
|
|
|
|
2025-07-19 17:49:03 +02:00
|
|
|
@router.post("/tenants/{tenant_id}/members", response_model=TenantMemberResponse)
|
2025-08-08 09:08:41 +02:00
|
|
|
@track_endpoint_metrics("tenant_add_member")
|
|
|
|
|
async def add_team_member_enhanced(
|
2025-07-19 17:49:03 +02:00
|
|
|
user_id: str,
|
|
|
|
|
role: str,
|
2025-07-26 18:46:52 +02:00
|
|
|
tenant_id: UUID = Path(..., description="Tenant ID"),
|
2025-07-21 14:41:33 +02:00
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
2025-08-08 09:08:41 +02:00
|
|
|
tenant_service: EnhancedTenantService = Depends(get_enhanced_tenant_service)
|
2025-07-19 17:49:03 +02:00
|
|
|
):
|
2025-08-08 09:08:41 +02:00
|
|
|
"""Add a team member to tenant with enhanced validation and role management"""
|
2025-07-21 14:41:33 +02:00
|
|
|
|
2025-07-19 17:49:03 +02:00
|
|
|
try:
|
2025-08-08 09:08:41 +02:00
|
|
|
result = await tenant_service.add_team_member(
|
|
|
|
|
str(tenant_id),
|
|
|
|
|
user_id,
|
|
|
|
|
role,
|
|
|
|
|
current_user["user_id"]
|
2025-07-19 17:49:03 +02:00
|
|
|
)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
except HTTPException:
|
|
|
|
|
raise
|
|
|
|
|
except Exception as e:
|
2025-08-08 09:08:41 +02:00
|
|
|
logger.error("Add team member failed",
|
|
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
user_id=user_id,
|
|
|
|
|
role=role,
|
|
|
|
|
error=str(e))
|
2025-07-19 17:49:03 +02:00
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to add team member"
|
2025-08-02 09:41:50 +02:00
|
|
|
)
|
|
|
|
|
|
2025-08-08 09:08:41 +02:00
|
|
|
@router.get("/tenants/{tenant_id}/members", response_model=List[TenantMemberResponse])
|
|
|
|
|
async def get_team_members_enhanced(
|
|
|
|
|
tenant_id: UUID = Path(..., description="Tenant ID"),
|
|
|
|
|
active_only: bool = Query(True, description="Only return active members"),
|
|
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
|
|
|
|
tenant_service: EnhancedTenantService = Depends(get_enhanced_tenant_service)
|
2025-08-02 09:41:50 +02:00
|
|
|
):
|
2025-08-08 09:08:41 +02:00
|
|
|
"""Get all team members for a tenant with enhanced filtering"""
|
2025-08-02 09:41:50 +02:00
|
|
|
|
|
|
|
|
try:
|
2025-08-08 09:08:41 +02:00
|
|
|
members = await tenant_service.get_team_members(
|
|
|
|
|
str(tenant_id),
|
|
|
|
|
current_user["user_id"],
|
|
|
|
|
active_only=active_only
|
2025-08-02 09:41:50 +02:00
|
|
|
)
|
2025-08-08 09:08:41 +02:00
|
|
|
return members
|
2025-08-02 09:41:50 +02:00
|
|
|
|
|
|
|
|
except HTTPException:
|
|
|
|
|
raise
|
|
|
|
|
except Exception as e:
|
2025-08-08 09:08:41 +02:00
|
|
|
logger.error("Get team members failed",
|
|
|
|
|
tenant_id=str(tenant_id),
|
2025-08-02 09:41:50 +02:00
|
|
|
error=str(e))
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
2025-08-08 09:08:41 +02:00
|
|
|
detail="Failed to get team members"
|
2025-08-02 17:09:53 +02:00
|
|
|
)
|
|
|
|
|
|
2025-08-08 09:08:41 +02:00
|
|
|
@router.put("/tenants/{tenant_id}/members/{member_user_id}/role", response_model=TenantMemberResponse)
|
|
|
|
|
@track_endpoint_metrics("tenant_update_member_role")
|
|
|
|
|
async def update_member_role_enhanced(
|
|
|
|
|
new_role: str,
|
|
|
|
|
tenant_id: UUID = Path(..., description="Tenant ID"),
|
|
|
|
|
member_user_id: str = Path(..., description="Member user ID"),
|
|
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
|
|
|
|
tenant_service: EnhancedTenantService = Depends(get_enhanced_tenant_service)
|
2025-08-02 17:09:53 +02:00
|
|
|
):
|
2025-08-08 09:08:41 +02:00
|
|
|
"""Update team member role with enhanced permission validation"""
|
2025-08-02 23:05:18 +02:00
|
|
|
|
2025-08-02 17:09:53 +02:00
|
|
|
try:
|
2025-08-08 09:08:41 +02:00
|
|
|
result = await tenant_service.update_member_role(
|
|
|
|
|
str(tenant_id),
|
|
|
|
|
member_user_id,
|
|
|
|
|
new_role,
|
|
|
|
|
current_user["user_id"]
|
2025-08-02 17:09:53 +02:00
|
|
|
)
|
2025-08-08 09:08:41 +02:00
|
|
|
return result
|
2025-08-02 17:09:53 +02:00
|
|
|
|
2025-08-08 09:08:41 +02:00
|
|
|
except HTTPException:
|
|
|
|
|
raise
|
2025-08-02 17:09:53 +02:00
|
|
|
except Exception as e:
|
2025-08-08 09:08:41 +02:00
|
|
|
logger.error("Update member role failed",
|
|
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
member_user_id=member_user_id,
|
|
|
|
|
new_role=new_role,
|
|
|
|
|
error=str(e))
|
2025-08-02 17:09:53 +02:00
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
2025-08-08 09:08:41 +02:00
|
|
|
detail="Failed to update member role"
|
2025-08-02 17:09:53 +02:00
|
|
|
)
|
|
|
|
|
|
2025-08-08 09:08:41 +02:00
|
|
|
@router.delete("/tenants/{tenant_id}/members/{member_user_id}")
|
|
|
|
|
@track_endpoint_metrics("tenant_remove_member")
|
|
|
|
|
async def remove_team_member_enhanced(
|
|
|
|
|
tenant_id: UUID = Path(..., description="Tenant ID"),
|
|
|
|
|
member_user_id: str = Path(..., description="Member user ID"),
|
|
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
|
|
|
|
tenant_service: EnhancedTenantService = Depends(get_enhanced_tenant_service)
|
2025-08-02 17:09:53 +02:00
|
|
|
):
|
2025-08-08 09:08:41 +02:00
|
|
|
"""Remove team member from tenant with enhanced validation"""
|
2025-08-02 17:09:53 +02:00
|
|
|
|
|
|
|
|
try:
|
2025-08-08 09:08:41 +02:00
|
|
|
success = await tenant_service.remove_team_member(
|
|
|
|
|
str(tenant_id),
|
|
|
|
|
member_user_id,
|
|
|
|
|
current_user["user_id"]
|
2025-08-02 17:09:53 +02:00
|
|
|
)
|
|
|
|
|
|
2025-08-08 09:08:41 +02:00
|
|
|
if success:
|
|
|
|
|
return {"success": True, "message": "Team member removed successfully"}
|
|
|
|
|
else:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to remove team member"
|
|
|
|
|
)
|
2025-08-02 17:09:53 +02:00
|
|
|
|
2025-08-08 09:08:41 +02:00
|
|
|
except HTTPException:
|
|
|
|
|
raise
|
2025-08-02 17:09:53 +02:00
|
|
|
except Exception as e:
|
2025-08-08 09:08:41 +02:00
|
|
|
logger.error("Remove team member failed",
|
|
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
member_user_id=member_user_id,
|
2025-08-02 17:09:53 +02:00
|
|
|
error=str(e))
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
2025-08-08 09:08:41 +02:00
|
|
|
detail="Failed to remove team member"
|
2025-08-02 17:09:53 +02:00
|
|
|
)
|
|
|
|
|
|
2025-08-08 09:08:41 +02:00
|
|
|
@router.post("/tenants/{tenant_id}/deactivate")
|
|
|
|
|
@track_endpoint_metrics("tenant_deactivate")
|
|
|
|
|
async def deactivate_tenant_enhanced(
|
|
|
|
|
tenant_id: UUID = Path(..., description="Tenant ID"),
|
|
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
|
|
|
|
tenant_service: EnhancedTenantService = Depends(get_enhanced_tenant_service)
|
2025-08-02 17:09:53 +02:00
|
|
|
):
|
2025-08-08 09:08:41 +02:00
|
|
|
"""Deactivate a tenant (owner only) with enhanced validation"""
|
2025-08-02 17:09:53 +02:00
|
|
|
|
|
|
|
|
try:
|
2025-08-08 09:08:41 +02:00
|
|
|
success = await tenant_service.deactivate_tenant(
|
|
|
|
|
str(tenant_id),
|
|
|
|
|
current_user["user_id"]
|
2025-08-02 17:09:53 +02:00
|
|
|
)
|
|
|
|
|
|
2025-08-08 09:08:41 +02:00
|
|
|
if success:
|
|
|
|
|
return {"success": True, "message": "Tenant deactivated successfully"}
|
|
|
|
|
else:
|
2025-08-02 17:09:53 +02:00
|
|
|
raise HTTPException(
|
2025-08-08 09:08:41 +02:00
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to deactivate tenant"
|
2025-08-02 17:09:53 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
except HTTPException:
|
|
|
|
|
raise
|
|
|
|
|
except Exception as e:
|
2025-08-08 09:08:41 +02:00
|
|
|
logger.error("Tenant deactivation failed",
|
|
|
|
|
tenant_id=str(tenant_id),
|
2025-08-02 17:09:53 +02:00
|
|
|
error=str(e))
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
2025-08-08 09:08:41 +02:00
|
|
|
detail="Failed to deactivate tenant"
|
2025-08-02 17:09:53 +02:00
|
|
|
)
|
|
|
|
|
|
2025-08-08 09:08:41 +02:00
|
|
|
@router.post("/tenants/{tenant_id}/activate")
|
|
|
|
|
@track_endpoint_metrics("tenant_activate")
|
|
|
|
|
async def activate_tenant_enhanced(
|
|
|
|
|
tenant_id: UUID = Path(..., description="Tenant ID"),
|
|
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
|
|
|
|
tenant_service: EnhancedTenantService = Depends(get_enhanced_tenant_service)
|
2025-08-02 17:09:53 +02:00
|
|
|
):
|
2025-08-08 09:08:41 +02:00
|
|
|
"""Activate a previously deactivated tenant (owner only) with enhanced validation"""
|
2025-08-03 14:42:33 +02:00
|
|
|
|
2025-08-08 09:08:41 +02:00
|
|
|
try:
|
|
|
|
|
success = await tenant_service.activate_tenant(
|
|
|
|
|
str(tenant_id),
|
|
|
|
|
current_user["user_id"]
|
2025-08-03 14:42:33 +02:00
|
|
|
)
|
|
|
|
|
|
2025-08-08 09:08:41 +02:00
|
|
|
if success:
|
|
|
|
|
return {"success": True, "message": "Tenant activated successfully"}
|
|
|
|
|
else:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to activate tenant"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
except HTTPException:
|
|
|
|
|
raise
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("Tenant activation failed",
|
|
|
|
|
tenant_id=str(tenant_id),
|
|
|
|
|
error=str(e))
|
2025-08-02 17:09:53 +02:00
|
|
|
raise HTTPException(
|
2025-08-08 09:08:41 +02:00
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to activate tenant"
|
2025-08-02 17:09:53 +02:00
|
|
|
)
|
2025-08-08 09:08:41 +02:00
|
|
|
|
2025-08-17 10:28:58 +02:00
|
|
|
@router.get("/tenants/users/{user_id}", response_model=List[TenantResponse])
|
|
|
|
|
@track_endpoint_metrics("tenant_get_user_tenants")
|
|
|
|
|
async def get_user_tenants_enhanced(
|
|
|
|
|
user_id: str = Path(..., description="User ID"),
|
|
|
|
|
tenant_service: EnhancedTenantService = Depends(get_enhanced_tenant_service)
|
|
|
|
|
):
|
|
|
|
|
"""Get all tenants owned by a user - Fixed endpoint for frontend"""
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
tenants = await tenant_service.get_user_tenants(user_id)
|
|
|
|
|
logger.info("Retrieved user tenants", user_id=user_id, tenant_count=len(tenants))
|
|
|
|
|
return tenants
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("Get user tenants failed", user_id=user_id, error=str(e))
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to get user tenants"
|
|
|
|
|
)
|
|
|
|
|
|
2025-09-23 12:49:35 +02:00
|
|
|
@router.get("/tenants/members/user/{user_id}")
|
|
|
|
|
@track_endpoint_metrics("tenant_get_user_memberships")
|
|
|
|
|
async def get_user_memberships(
|
|
|
|
|
user_id: str = Path(..., description="User ID"),
|
|
|
|
|
tenant_service: EnhancedTenantService = Depends(get_enhanced_tenant_service)
|
|
|
|
|
):
|
|
|
|
|
"""Get all tenant memberships for a user (for authentication service)"""
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
memberships = await tenant_service.get_user_memberships(user_id)
|
|
|
|
|
logger.info("Retrieved user memberships", user_id=user_id, membership_count=len(memberships))
|
|
|
|
|
return memberships
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error("Get user memberships failed", user_id=user_id, error=str(e))
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
|
detail="Failed to get user memberships"
|
|
|
|
|
)
|
|
|
|
|
|
2025-08-08 09:08:41 +02:00
|
|
|
@router.get("/tenants/statistics", dependencies=[Depends(require_admin_role_dep)])
|
|
|
|
|
@track_endpoint_metrics("tenant_get_statistics")
|
|
|
|
|
async def get_tenant_statistics_enhanced(
|
|
|
|
|
current_user: Dict[str, Any] = Depends(get_current_user_dep),
|
|
|
|
|
tenant_service: EnhancedTenantService = Depends(get_enhanced_tenant_service)
|
|
|
|
|
):
|
|
|
|
|
"""Get comprehensive tenant statistics (admin only) with enhanced analytics"""
|
2025-09-23 12:49:35 +02:00
|
|
|
|
2025-08-02 17:09:53 +02:00
|
|
|
try:
|
2025-08-08 09:08:41 +02:00
|
|
|
stats = await tenant_service.get_tenant_statistics()
|
|
|
|
|
return stats
|
2025-09-23 12:49:35 +02:00
|
|
|
|
2025-08-02 17:09:53 +02:00
|
|
|
except Exception as e:
|
2025-08-08 09:08:41 +02:00
|
|
|
logger.error("Get tenant statistics failed", error=str(e))
|
2025-08-02 17:09:53 +02:00
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
2025-08-08 09:08:41 +02:00
|
|
|
detail="Failed to get tenant statistics"
|
2025-07-19 17:49:03 +02:00
|
|
|
)
|