refactor removing mixed logic
This commit is contained in:
parent
43c2bcce83
commit
fba3662776
@ -1,8 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Header, Request, Query
|
from fastapi import APIRouter, Depends, HTTPException, Header, Request, Query, status
|
||||||
from bson import ObjectId
|
|
||||||
|
|
||||||
from src.dependencies import AuthServiceDep
|
from src.dependencies import AuthServiceDep
|
||||||
from src.schemas.api_key import ApiKeyCreate, ApiKeyResponse, ApiKeyWithValueResponse, ApiKeyListResponse
|
from src.schemas.api_key import ApiKeyCreate, ApiKeyResponse, ApiKeyWithValueResponse, ApiKeyListResponse
|
||||||
@ -13,12 +12,13 @@ from src.models.api_key import ApiKeyModel
|
|||||||
from src.models.team import TeamModel
|
from src.models.team import TeamModel
|
||||||
from src.models.user import UserModel
|
from src.models.user import UserModel
|
||||||
from src.utils.logging import log_request
|
from src.utils.logging import log_request
|
||||||
|
from src.api.v1.error_handlers import handle_service_error
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
router = APIRouter(tags=["Authentication"], prefix="/auth")
|
router = APIRouter(tags=["Authentication"], prefix="/auth")
|
||||||
|
|
||||||
@router.post("/api-keys", response_model=ApiKeyWithValueResponse, status_code=201)
|
@router.post("/api-keys", response_model=ApiKeyWithValueResponse, status_code=status.HTTP_201_CREATED)
|
||||||
async def create_api_key(
|
async def create_api_key(
|
||||||
key_data: ApiKeyCreate,
|
key_data: ApiKeyCreate,
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -31,20 +31,6 @@ async def create_api_key(
|
|||||||
|
|
||||||
This endpoint creates an API key without requiring authentication.
|
This endpoint creates an API key without requiring authentication.
|
||||||
Both user_id and team_id must be provided as query parameters.
|
Both user_id and team_id must be provided as query parameters.
|
||||||
|
|
||||||
Args:
|
|
||||||
key_data: API key creation data including name and description
|
|
||||||
user_id: The user ID to create the key for
|
|
||||||
team_id: The team ID the user belongs to
|
|
||||||
auth_service: Injected authentication service
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
ApiKeyWithValueResponse: The created API key with the raw key value
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
400: Invalid input data or user/team validation errors
|
|
||||||
404: User or team not found
|
|
||||||
500: Internal server error
|
|
||||||
"""
|
"""
|
||||||
log_request(
|
log_request(
|
||||||
{
|
{
|
||||||
@ -60,17 +46,10 @@ async def create_api_key(
|
|||||||
response = await auth_service.create_api_key_for_user_and_team(user_id, team_id, key_data)
|
response = await auth_service.create_api_key_for_user_and_team(user_id, team_id, key_data)
|
||||||
logger.info(f"API key created successfully for user {user_id} in team {team_id}")
|
logger.info(f"API key created successfully for user {user_id} in team {team_id}")
|
||||||
return response
|
return response
|
||||||
except ValueError as e:
|
|
||||||
logger.warning(f"Invalid input for API key creation: {e}")
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
except RuntimeError as e:
|
|
||||||
logger.warning(f"Resource not found for API key creation: {e}")
|
|
||||||
raise HTTPException(status_code=404, detail=str(e))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error creating API key: {e}")
|
raise handle_service_error(e, "API key creation")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
|
||||||
|
|
||||||
@router.post("/admin/api-keys/{user_id}", response_model=ApiKeyWithValueResponse, status_code=201)
|
@router.post("/admin/api-keys/{user_id}", response_model=ApiKeyWithValueResponse, status_code=status.HTTP_201_CREATED)
|
||||||
async def create_api_key_for_user(
|
async def create_api_key_for_user(
|
||||||
user_id: str,
|
user_id: str,
|
||||||
key_data: ApiKeyCreate,
|
key_data: ApiKeyCreate,
|
||||||
@ -83,21 +62,6 @@ async def create_api_key_for_user(
|
|||||||
|
|
||||||
This endpoint requires admin authentication and allows creating API keys
|
This endpoint requires admin authentication and allows creating API keys
|
||||||
for any user in the system.
|
for any user in the system.
|
||||||
|
|
||||||
Args:
|
|
||||||
user_id: The target user ID to create the key for
|
|
||||||
key_data: API key creation data including name and description
|
|
||||||
current_user: The authenticated admin user
|
|
||||||
auth_service: Injected authentication service
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
ApiKeyWithValueResponse: The created API key with the raw key value
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
400: Invalid input data
|
|
||||||
403: Insufficient permissions (not admin)
|
|
||||||
404: Target user or team not found
|
|
||||||
500: Internal server error
|
|
||||||
"""
|
"""
|
||||||
log_request(
|
log_request(
|
||||||
{
|
{
|
||||||
@ -114,18 +78,8 @@ async def create_api_key_for_user(
|
|||||||
response = await auth_service.create_api_key_for_user_by_admin(user_id, key_data, current_user)
|
response = await auth_service.create_api_key_for_user_by_admin(user_id, key_data, current_user)
|
||||||
logger.info(f"Admin {current_user.id} created API key for user {user_id}")
|
logger.info(f"Admin {current_user.id} created API key for user {user_id}")
|
||||||
return response
|
return response
|
||||||
except PermissionError as e:
|
|
||||||
logger.warning(f"Permission denied for admin API key creation: {e}")
|
|
||||||
raise HTTPException(status_code=403, detail=str(e))
|
|
||||||
except ValueError as e:
|
|
||||||
logger.warning(f"Invalid input for admin API key creation: {e}")
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
except RuntimeError as e:
|
|
||||||
logger.warning(f"Resource not found for admin API key creation: {e}")
|
|
||||||
raise HTTPException(status_code=404, detail=str(e))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error creating API key for user: {e}")
|
raise handle_service_error(e, "admin API key creation")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
|
||||||
|
|
||||||
@router.get("/api-keys", response_model=ApiKeyListResponse)
|
@router.get("/api-keys", response_model=ApiKeyListResponse)
|
||||||
async def list_api_keys(
|
async def list_api_keys(
|
||||||
@ -137,16 +91,6 @@ async def list_api_keys(
|
|||||||
List API keys for the current authenticated user
|
List API keys for the current authenticated user
|
||||||
|
|
||||||
Returns all active and inactive API keys belonging to the authenticated user.
|
Returns all active and inactive API keys belonging to the authenticated user.
|
||||||
|
|
||||||
Args:
|
|
||||||
current_user: The authenticated user
|
|
||||||
auth_service: Injected authentication service
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
ApiKeyListResponse: List of API keys with metadata
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
500: Internal server error
|
|
||||||
"""
|
"""
|
||||||
log_request(
|
log_request(
|
||||||
{"path": request.url.path, "method": request.method},
|
{"path": request.url.path, "method": request.method},
|
||||||
@ -159,10 +103,9 @@ async def list_api_keys(
|
|||||||
logger.info(f"Listed {response.total} API keys for user {current_user.id}")
|
logger.info(f"Listed {response.total} API keys for user {current_user.id}")
|
||||||
return response
|
return response
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error listing API keys: {e}")
|
raise handle_service_error(e, "API key listing")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
|
||||||
|
|
||||||
@router.delete("/api-keys/{key_id}", status_code=204)
|
@router.delete("/api-keys/{key_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||||
async def revoke_api_key(
|
async def revoke_api_key(
|
||||||
key_id: str,
|
key_id: str,
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -173,20 +116,6 @@ async def revoke_api_key(
|
|||||||
Revoke (deactivate) an API key
|
Revoke (deactivate) an API key
|
||||||
|
|
||||||
Deactivates the specified API key. Only the key owner or an admin can revoke keys.
|
Deactivates the specified API key. Only the key owner or an admin can revoke keys.
|
||||||
|
|
||||||
Args:
|
|
||||||
key_id: The ID of the API key to revoke
|
|
||||||
current_user: The authenticated user
|
|
||||||
auth_service: Injected authentication service
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
None (204 No Content)
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
400: Invalid key ID format
|
|
||||||
403: Insufficient permissions to revoke this key
|
|
||||||
404: API key not found
|
|
||||||
500: Internal server error
|
|
||||||
"""
|
"""
|
||||||
log_request(
|
log_request(
|
||||||
{"path": request.url.path, "method": request.method, "key_id": key_id},
|
{"path": request.url.path, "method": request.method, "key_id": key_id},
|
||||||
@ -198,20 +127,10 @@ async def revoke_api_key(
|
|||||||
await auth_service.revoke_api_key(key_id, current_user)
|
await auth_service.revoke_api_key(key_id, current_user)
|
||||||
logger.info(f"API key {key_id} revoked by user {current_user.id}")
|
logger.info(f"API key {key_id} revoked by user {current_user.id}")
|
||||||
return None
|
return None
|
||||||
except ValueError as e:
|
|
||||||
logger.warning(f"Invalid input for API key revocation: {e}")
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
except RuntimeError as e:
|
|
||||||
logger.warning(f"API key not found for revocation: {e}")
|
|
||||||
raise HTTPException(status_code=404, detail=str(e))
|
|
||||||
except PermissionError as e:
|
|
||||||
logger.warning(f"Permission denied for API key revocation: {e}")
|
|
||||||
raise HTTPException(status_code=403, detail=str(e))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error revoking API key: {e}")
|
raise handle_service_error(e, "API key revocation")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
|
||||||
|
|
||||||
@router.get("/verify", status_code=200)
|
@router.get("/verify", status_code=status.HTTP_200_OK)
|
||||||
async def verify_authentication(
|
async def verify_authentication(
|
||||||
request: Request,
|
request: Request,
|
||||||
current_user: UserModel = Depends(get_current_user),
|
current_user: UserModel = Depends(get_current_user),
|
||||||
@ -222,16 +141,6 @@ async def verify_authentication(
|
|||||||
|
|
||||||
Validates the current API key and returns user information.
|
Validates the current API key and returns user information.
|
||||||
Useful for checking if an API key is still valid and active.
|
Useful for checking if an API key is still valid and active.
|
||||||
|
|
||||||
Args:
|
|
||||||
current_user: The authenticated user
|
|
||||||
auth_service: Injected authentication service
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
dict: Authentication verification response with user details
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
500: Internal server error
|
|
||||||
"""
|
"""
|
||||||
log_request(
|
log_request(
|
||||||
{"path": request.url.path, "method": request.method},
|
{"path": request.url.path, "method": request.method},
|
||||||
@ -244,5 +153,4 @@ async def verify_authentication(
|
|||||||
logger.info(f"Authentication verified for user {current_user.id}")
|
logger.info(f"Authentication verified for user {current_user.id}")
|
||||||
return response
|
return response
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error verifying authentication: {e}")
|
raise handle_service_error(e, "authentication verification")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
|
||||||
113
src/api/v1/error_handlers.py
Normal file
113
src/api/v1/error_handlers.py
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
"""
|
||||||
|
Shared error handling utilities for API endpoints.
|
||||||
|
|
||||||
|
This module provides centralized error handling to ensure consistent
|
||||||
|
HTTP status codes and error responses across all API endpoints.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import Dict, Type
|
||||||
|
from fastapi import HTTPException, status
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# HTTP Status Code Mapping for consistent error responses
|
||||||
|
HTTP_ERROR_MAP: Dict[Type[Exception], int] = {
|
||||||
|
ValueError: status.HTTP_400_BAD_REQUEST,
|
||||||
|
RuntimeError: status.HTTP_404_NOT_FOUND,
|
||||||
|
PermissionError: status.HTTP_403_FORBIDDEN,
|
||||||
|
FileNotFoundError: status.HTTP_404_NOT_FOUND,
|
||||||
|
TimeoutError: status.HTTP_408_REQUEST_TIMEOUT,
|
||||||
|
ConnectionError: status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||||
|
Exception: status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||||
|
}
|
||||||
|
|
||||||
|
def handle_service_error(error: Exception, operation: str) -> HTTPException:
|
||||||
|
"""
|
||||||
|
Centralized error handling for service layer exceptions
|
||||||
|
|
||||||
|
This function maps service layer exceptions to appropriate HTTP status codes
|
||||||
|
and provides consistent error logging and response formatting.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
error: The exception raised by the service layer
|
||||||
|
operation: Description of the operation that failed (for logging)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
HTTPException: Properly formatted HTTP exception with appropriate status code
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
>>> try:
|
||||||
|
... await some_service_method()
|
||||||
|
... except Exception as e:
|
||||||
|
... raise handle_service_error(e, "user creation")
|
||||||
|
"""
|
||||||
|
error_type = type(error)
|
||||||
|
status_code = HTTP_ERROR_MAP.get(error_type, status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
|
|
||||||
|
# Log errors appropriately based on severity
|
||||||
|
if status_code == status.HTTP_500_INTERNAL_SERVER_ERROR:
|
||||||
|
logger.error(f"Unexpected error in {operation}: {error}", exc_info=True)
|
||||||
|
detail = "Internal server error"
|
||||||
|
elif status_code >= 500:
|
||||||
|
logger.error(f"Server error in {operation}: {error}")
|
||||||
|
detail = "Service temporarily unavailable"
|
||||||
|
else:
|
||||||
|
logger.warning(f"Client error in {operation}: {error}")
|
||||||
|
detail = str(error)
|
||||||
|
|
||||||
|
return HTTPException(status_code=status_code, detail=detail)
|
||||||
|
|
||||||
|
def handle_validation_error(error: Exception, field_name: str = None) -> HTTPException:
|
||||||
|
"""
|
||||||
|
Handle validation errors with specific formatting
|
||||||
|
|
||||||
|
Args:
|
||||||
|
error: The validation exception
|
||||||
|
field_name: Optional field name that failed validation
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
HTTPException: Formatted validation error response
|
||||||
|
"""
|
||||||
|
detail = f"Validation error"
|
||||||
|
if field_name:
|
||||||
|
detail += f" for field '{field_name}'"
|
||||||
|
detail += f": {str(error)}"
|
||||||
|
|
||||||
|
logger.warning(f"Validation error: {detail}")
|
||||||
|
return HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=detail)
|
||||||
|
|
||||||
|
def handle_authentication_error(error: Exception) -> HTTPException:
|
||||||
|
"""
|
||||||
|
Handle authentication-related errors
|
||||||
|
|
||||||
|
Args:
|
||||||
|
error: The authentication exception
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
HTTPException: Formatted authentication error response
|
||||||
|
"""
|
||||||
|
logger.warning(f"Authentication error: {error}")
|
||||||
|
return HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Authentication failed",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"}
|
||||||
|
)
|
||||||
|
|
||||||
|
def handle_authorization_error(error: Exception, resource: str = None) -> HTTPException:
|
||||||
|
"""
|
||||||
|
Handle authorization-related errors
|
||||||
|
|
||||||
|
Args:
|
||||||
|
error: The authorization exception
|
||||||
|
resource: Optional resource name that access was denied to
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
HTTPException: Formatted authorization error response
|
||||||
|
"""
|
||||||
|
detail = "Access denied"
|
||||||
|
if resource:
|
||||||
|
detail += f" to {resource}"
|
||||||
|
|
||||||
|
logger.warning(f"Authorization error: {detail} - {error}")
|
||||||
|
return HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=detail)
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Optional, List
|
from typing import Optional, List
|
||||||
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Query, Request, Response
|
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Query, Request, Response, status
|
||||||
from fastapi.responses import StreamingResponse
|
from fastapi.responses import StreamingResponse
|
||||||
from bson import ObjectId
|
from bson import ObjectId
|
||||||
import io
|
import io
|
||||||
@ -10,12 +10,13 @@ from src.dependencies import ImageServiceDep
|
|||||||
from src.models.user import UserModel
|
from src.models.user import UserModel
|
||||||
from src.schemas.image import ImageResponse, ImageListResponse, ImageCreate, ImageUpdate
|
from src.schemas.image import ImageResponse, ImageListResponse, ImageCreate, ImageUpdate
|
||||||
from src.utils.logging import log_request
|
from src.utils.logging import log_request
|
||||||
|
from src.api.v1.error_handlers import handle_service_error
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
router = APIRouter(tags=["Images"], prefix="/images")
|
router = APIRouter(tags=["Images"], prefix="/images")
|
||||||
|
|
||||||
@router.post("", response_model=ImageResponse, status_code=201)
|
@router.post("", response_model=ImageResponse, status_code=status.HTTP_201_CREATED)
|
||||||
async def upload_image(
|
async def upload_image(
|
||||||
request: Request,
|
request: Request,
|
||||||
file: UploadFile = File(..., description="Image file to upload"),
|
file: UploadFile = File(..., description="Image file to upload"),
|
||||||
@ -62,15 +63,8 @@ async def upload_image(
|
|||||||
response = await image_service.upload_image(file, current_user, request, description, collection_id)
|
response = await image_service.upload_image(file, current_user, request, description, collection_id)
|
||||||
logger.info(f"Image uploaded successfully: {file.filename} by user {current_user.id}")
|
logger.info(f"Image uploaded successfully: {file.filename} by user {current_user.id}")
|
||||||
return response
|
return response
|
||||||
except ValueError as e:
|
|
||||||
logger.warning(f"Invalid input for image upload: {e}")
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
except RuntimeError as e:
|
|
||||||
logger.error(f"Runtime error during image upload: {e}")
|
|
||||||
raise HTTPException(status_code=500, detail=str(e))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error uploading image: {e}")
|
raise handle_service_error(e, "image upload")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
|
||||||
|
|
||||||
@router.get("", response_model=ImageListResponse)
|
@router.get("", response_model=ImageListResponse)
|
||||||
async def list_images(
|
async def list_images(
|
||||||
@ -118,12 +112,8 @@ async def list_images(
|
|||||||
response = await image_service.list_images(current_user, request, skip, limit, collection_id)
|
response = await image_service.list_images(current_user, request, skip, limit, collection_id)
|
||||||
logger.info(f"Listed {len(response.images)} images for user {current_user.id} (admin: {current_user.is_admin})")
|
logger.info(f"Listed {len(response.images)} images for user {current_user.id} (admin: {current_user.is_admin})")
|
||||||
return response
|
return response
|
||||||
except ValueError as e:
|
|
||||||
logger.warning(f"Invalid parameters for image listing: {e}")
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error listing images: {e}")
|
raise handle_service_error(e, "image listing")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
|
||||||
|
|
||||||
@router.get("/{image_id}", response_model=ImageResponse)
|
@router.get("/{image_id}", response_model=ImageResponse)
|
||||||
async def get_image(
|
async def get_image(
|
||||||
@ -167,18 +157,8 @@ async def get_image(
|
|||||||
response = await image_service.get_image(image_id, current_user, request)
|
response = await image_service.get_image(image_id, current_user, request)
|
||||||
logger.info(f"Retrieved image {image_id} for user {current_user.id}")
|
logger.info(f"Retrieved image {image_id} for user {current_user.id}")
|
||||||
return response
|
return response
|
||||||
except ValueError as e:
|
|
||||||
logger.warning(f"Invalid image ID: {e}")
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
except RuntimeError as e:
|
|
||||||
logger.warning(f"Image not found: {e}")
|
|
||||||
raise HTTPException(status_code=404, detail=str(e))
|
|
||||||
except PermissionError as e:
|
|
||||||
logger.warning(f"Permission denied for image access: {e}")
|
|
||||||
raise HTTPException(status_code=403, detail=str(e))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error getting image: {e}")
|
raise handle_service_error(e, "image retrieval")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
|
||||||
|
|
||||||
@router.get("/{image_id}/download")
|
@router.get("/{image_id}/download")
|
||||||
async def download_image(
|
async def download_image(
|
||||||
@ -229,18 +209,8 @@ async def download_image(
|
|||||||
media_type=content_type,
|
media_type=content_type,
|
||||||
headers={"Content-Disposition": f"attachment; filename={filename}"}
|
headers={"Content-Disposition": f"attachment; filename={filename}"}
|
||||||
)
|
)
|
||||||
except ValueError as e:
|
|
||||||
logger.warning(f"Invalid image ID for download: {e}")
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
except RuntimeError as e:
|
|
||||||
logger.warning(f"Image not found for download: {e}")
|
|
||||||
raise HTTPException(status_code=404, detail=str(e))
|
|
||||||
except PermissionError as e:
|
|
||||||
logger.warning(f"Permission denied for image download: {e}")
|
|
||||||
raise HTTPException(status_code=403, detail=str(e))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error downloading image: {e}")
|
raise handle_service_error(e, "image download")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
|
||||||
|
|
||||||
@router.put("/{image_id}", response_model=ImageResponse)
|
@router.put("/{image_id}", response_model=ImageResponse)
|
||||||
async def update_image(
|
async def update_image(
|
||||||
@ -287,20 +257,10 @@ async def update_image(
|
|||||||
response = await image_service.update_image(image_id, image_data, current_user, request)
|
response = await image_service.update_image(image_id, image_data, current_user, request)
|
||||||
logger.info(f"Image {image_id} updated by user {current_user.id}")
|
logger.info(f"Image {image_id} updated by user {current_user.id}")
|
||||||
return response
|
return response
|
||||||
except ValueError as e:
|
|
||||||
logger.warning(f"Invalid input for image update: {e}")
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
except RuntimeError as e:
|
|
||||||
logger.warning(f"Image not found for update: {e}")
|
|
||||||
raise HTTPException(status_code=404, detail=str(e))
|
|
||||||
except PermissionError as e:
|
|
||||||
logger.warning(f"Permission denied for image update: {e}")
|
|
||||||
raise HTTPException(status_code=403, detail=str(e))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error updating image: {e}")
|
raise handle_service_error(e, "image update")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
|
||||||
|
|
||||||
@router.delete("/{image_id}", status_code=204)
|
@router.delete("/{image_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||||
async def delete_image(
|
async def delete_image(
|
||||||
image_id: str,
|
image_id: str,
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -342,15 +302,5 @@ async def delete_image(
|
|||||||
await image_service.delete_image(image_id, current_user)
|
await image_service.delete_image(image_id, current_user)
|
||||||
logger.info(f"Image {image_id} deleted by user {current_user.id}")
|
logger.info(f"Image {image_id} deleted by user {current_user.id}")
|
||||||
return None
|
return None
|
||||||
except ValueError as e:
|
|
||||||
logger.warning(f"Invalid image ID for deletion: {e}")
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
except RuntimeError as e:
|
|
||||||
logger.warning(f"Image not found for deletion: {e}")
|
|
||||||
raise HTTPException(status_code=404, detail=str(e))
|
|
||||||
except PermissionError as e:
|
|
||||||
logger.warning(f"Permission denied for image deletion: {e}")
|
|
||||||
raise HTTPException(status_code=403, detail=str(e))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error deleting image: {e}")
|
raise handle_service_error(e, "image deletion")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
|
||||||
@ -1,12 +1,13 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Optional, List, Dict, Any
|
from typing import Optional, List, Dict, Any
|
||||||
from fastapi import APIRouter, Depends, Query, Request, HTTPException
|
from fastapi import APIRouter, Depends, Query, Request, HTTPException, status
|
||||||
|
|
||||||
from src.auth.security import get_current_user
|
from src.auth.security import get_current_user
|
||||||
from src.dependencies import SearchServiceDep
|
from src.dependencies import SearchServiceDep
|
||||||
from src.models.user import UserModel
|
from src.models.user import UserModel
|
||||||
from src.schemas.search import SearchResponse, SearchRequest
|
from src.schemas.search import SearchResponse, SearchRequest
|
||||||
from src.utils.logging import log_request
|
from src.utils.logging import log_request
|
||||||
|
from src.api.v1.error_handlers import handle_service_error
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -69,15 +70,8 @@ async def search_images(
|
|||||||
)
|
)
|
||||||
logger.info(f"Search completed: '{q}' returned {len(response.results)} results for user {current_user.id}")
|
logger.info(f"Search completed: '{q}' returned {len(response.results)} results for user {current_user.id}")
|
||||||
return response
|
return response
|
||||||
except ValueError as e:
|
|
||||||
logger.warning(f"Invalid search parameters: {e}")
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
except RuntimeError as e:
|
|
||||||
logger.error(f"Search service error: {e}")
|
|
||||||
raise HTTPException(status_code=500, detail=str(e))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error in search: {e}")
|
raise handle_service_error(e, "image search")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
|
||||||
|
|
||||||
@router.post("", response_model=SearchResponse)
|
@router.post("", response_model=SearchResponse)
|
||||||
async def search_images_advanced(
|
async def search_images_advanced(
|
||||||
@ -124,12 +118,5 @@ async def search_images_advanced(
|
|||||||
)
|
)
|
||||||
logger.info(f"Advanced search completed: '{search_request.query}' returned {len(response.results)} results for user {current_user.id}")
|
logger.info(f"Advanced search completed: '{search_request.query}' returned {len(response.results)} results for user {current_user.id}")
|
||||||
return response
|
return response
|
||||||
except ValueError as e:
|
|
||||||
logger.warning(f"Invalid advanced search request: {e}")
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
except RuntimeError as e:
|
|
||||||
logger.error(f"Advanced search service error: {e}")
|
|
||||||
raise HTTPException(status_code=500, detail=str(e))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error in advanced search: {e}")
|
raise handle_service_error(e, "advanced image search")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
|
||||||
|
|||||||
@ -1,16 +1,17 @@
|
|||||||
import logging
|
import logging
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Request
|
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||||
from bson import ObjectId
|
from bson import ObjectId
|
||||||
|
|
||||||
from src.dependencies import TeamServiceDep
|
from src.dependencies import TeamServiceDep
|
||||||
from src.schemas.team import TeamCreate, TeamUpdate, TeamResponse, TeamListResponse
|
from src.schemas.team import TeamCreate, TeamUpdate, TeamResponse, TeamListResponse
|
||||||
from src.utils.logging import log_request
|
from src.utils.logging import log_request
|
||||||
|
from src.api.v1.error_handlers import handle_service_error
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
router = APIRouter(tags=["Teams"], prefix="/teams")
|
router = APIRouter(tags=["Teams"], prefix="/teams")
|
||||||
|
|
||||||
@router.post("", response_model=TeamResponse, status_code=201)
|
@router.post("", response_model=TeamResponse, status_code=status.HTTP_201_CREATED)
|
||||||
async def create_team(
|
async def create_team(
|
||||||
team_data: TeamCreate,
|
team_data: TeamCreate,
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -28,10 +29,6 @@ async def create_team(
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
TeamResponse: The created team information
|
TeamResponse: The created team information
|
||||||
|
|
||||||
Raises:
|
|
||||||
400: Invalid input data or team already exists
|
|
||||||
500: Internal server error
|
|
||||||
"""
|
"""
|
||||||
log_request(
|
log_request(
|
||||||
{"path": request.url.path, "method": request.method, "team_data": team_data.dict()}
|
{"path": request.url.path, "method": request.method, "team_data": team_data.dict()}
|
||||||
@ -41,12 +38,8 @@ async def create_team(
|
|||||||
response = await team_service.create_team(team_data)
|
response = await team_service.create_team(team_data)
|
||||||
logger.info(f"Created new team: {team_data.name}")
|
logger.info(f"Created new team: {team_data.name}")
|
||||||
return response
|
return response
|
||||||
except ValueError as e:
|
|
||||||
logger.warning(f"Invalid input for team creation: {e}")
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error creating team: {e}")
|
raise handle_service_error(e, "team creation")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
|
||||||
|
|
||||||
@router.get("", response_model=TeamListResponse)
|
@router.get("", response_model=TeamListResponse)
|
||||||
async def list_teams(
|
async def list_teams(
|
||||||
@ -64,9 +57,6 @@ async def list_teams(
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
TeamListResponse: List of all teams with total count
|
TeamListResponse: List of all teams with total count
|
||||||
|
|
||||||
Raises:
|
|
||||||
500: Internal server error
|
|
||||||
"""
|
"""
|
||||||
log_request(
|
log_request(
|
||||||
{"path": request.url.path, "method": request.method}
|
{"path": request.url.path, "method": request.method}
|
||||||
@ -77,8 +67,7 @@ async def list_teams(
|
|||||||
logger.info(f"Listed {response.total} teams")
|
logger.info(f"Listed {response.total} teams")
|
||||||
return response
|
return response
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error listing teams: {e}")
|
raise handle_service_error(e, "team listing")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
|
||||||
|
|
||||||
@router.get("/{team_id}", response_model=TeamResponse)
|
@router.get("/{team_id}", response_model=TeamResponse)
|
||||||
async def get_team(
|
async def get_team(
|
||||||
@ -98,11 +87,6 @@ async def get_team(
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
TeamResponse: Complete team information
|
TeamResponse: Complete team information
|
||||||
|
|
||||||
Raises:
|
|
||||||
400: Invalid team ID format
|
|
||||||
404: Team not found
|
|
||||||
500: Internal server error
|
|
||||||
"""
|
"""
|
||||||
log_request(
|
log_request(
|
||||||
{"path": request.url.path, "method": request.method, "team_id": team_id}
|
{"path": request.url.path, "method": request.method, "team_id": team_id}
|
||||||
@ -112,15 +96,8 @@ async def get_team(
|
|||||||
response = await team_service.get_team(team_id)
|
response = await team_service.get_team(team_id)
|
||||||
logger.info(f"Retrieved team {team_id}")
|
logger.info(f"Retrieved team {team_id}")
|
||||||
return response
|
return response
|
||||||
except ValueError as e:
|
|
||||||
logger.warning(f"Invalid team ID: {e}")
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
except RuntimeError as e:
|
|
||||||
logger.warning(f"Team not found: {e}")
|
|
||||||
raise HTTPException(status_code=404, detail=str(e))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error getting team: {e}")
|
raise handle_service_error(e, "team retrieval")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
|
||||||
|
|
||||||
@router.put("/{team_id}", response_model=TeamResponse)
|
@router.put("/{team_id}", response_model=TeamResponse)
|
||||||
async def update_team(
|
async def update_team(
|
||||||
@ -142,11 +119,6 @@ async def update_team(
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
TeamResponse: Updated team information
|
TeamResponse: Updated team information
|
||||||
|
|
||||||
Raises:
|
|
||||||
400: Invalid team ID format or validation errors
|
|
||||||
404: Team not found
|
|
||||||
500: Internal server error
|
|
||||||
"""
|
"""
|
||||||
log_request(
|
log_request(
|
||||||
{"path": request.url.path, "method": request.method, "team_id": team_id, "team_data": team_data.dict()}
|
{"path": request.url.path, "method": request.method, "team_id": team_id, "team_data": team_data.dict()}
|
||||||
@ -156,17 +128,10 @@ async def update_team(
|
|||||||
response = await team_service.update_team(team_id, team_data)
|
response = await team_service.update_team(team_id, team_data)
|
||||||
logger.info(f"Updated team {team_id}")
|
logger.info(f"Updated team {team_id}")
|
||||||
return response
|
return response
|
||||||
except ValueError as e:
|
|
||||||
logger.warning(f"Invalid input for team update: {e}")
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
except RuntimeError as e:
|
|
||||||
logger.warning(f"Team not found for update: {e}")
|
|
||||||
raise HTTPException(status_code=404, detail=str(e))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error updating team: {e}")
|
raise handle_service_error(e, "team update")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
|
||||||
|
|
||||||
@router.delete("/{team_id}", status_code=204)
|
@router.delete("/{team_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||||
async def delete_team(
|
async def delete_team(
|
||||||
team_id: str,
|
team_id: str,
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -184,11 +149,6 @@ async def delete_team(
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
None (204 No Content)
|
None (204 No Content)
|
||||||
|
|
||||||
Raises:
|
|
||||||
400: Invalid team ID format or team has dependencies
|
|
||||||
404: Team not found
|
|
||||||
500: Internal server error
|
|
||||||
"""
|
"""
|
||||||
log_request(
|
log_request(
|
||||||
{"path": request.url.path, "method": request.method, "team_id": team_id}
|
{"path": request.url.path, "method": request.method, "team_id": team_id}
|
||||||
@ -198,12 +158,5 @@ async def delete_team(
|
|||||||
await team_service.delete_team(team_id)
|
await team_service.delete_team(team_id)
|
||||||
logger.info(f"Deleted team {team_id}")
|
logger.info(f"Deleted team {team_id}")
|
||||||
return None
|
return None
|
||||||
except ValueError as e:
|
|
||||||
logger.warning(f"Invalid team ID or team has dependencies: {e}")
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
except RuntimeError as e:
|
|
||||||
logger.warning(f"Team not found for deletion: {e}")
|
|
||||||
raise HTTPException(status_code=404, detail=str(e))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error deleting team: {e}")
|
raise handle_service_error(e, "team deletion")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
|
||||||
@ -1,10 +1,11 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Request, Query
|
from fastapi import APIRouter, Depends, HTTPException, Request, Query, status
|
||||||
|
|
||||||
from src.dependencies import UserServiceDep
|
from src.dependencies import UserServiceDep
|
||||||
from src.schemas.user import UserResponse, UserListResponse, UserCreate, UserUpdate
|
from src.schemas.user import UserResponse, UserListResponse, UserCreate, UserUpdate
|
||||||
from src.utils.logging import log_request
|
from src.utils.logging import log_request
|
||||||
|
from src.api.v1.error_handlers import handle_service_error
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -42,15 +43,8 @@ async def read_users_me(
|
|||||||
response = await user_service.get_user_by_id(user_id)
|
response = await user_service.get_user_by_id(user_id)
|
||||||
logger.info(f"Retrieved user information for user {user_id}")
|
logger.info(f"Retrieved user information for user {user_id}")
|
||||||
return response
|
return response
|
||||||
except ValueError as e:
|
|
||||||
logger.warning(f"Invalid user ID provided: {e}")
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
except RuntimeError as e:
|
|
||||||
logger.warning(f"User not found: {e}")
|
|
||||||
raise HTTPException(status_code=404, detail=str(e))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error getting user: {e}")
|
raise handle_service_error(e, "user retrieval by ID")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
|
||||||
|
|
||||||
@router.put("/me", response_model=UserResponse)
|
@router.put("/me", response_model=UserResponse)
|
||||||
async def update_current_user(
|
async def update_current_user(
|
||||||
@ -86,17 +80,10 @@ async def update_current_user(
|
|||||||
response = await user_service.update_user_by_id(user_id, user_data)
|
response = await user_service.update_user_by_id(user_id, user_data)
|
||||||
logger.info(f"Updated user information for user {user_id}")
|
logger.info(f"Updated user information for user {user_id}")
|
||||||
return response
|
return response
|
||||||
except ValueError as e:
|
|
||||||
logger.warning(f"Invalid input for user update: {e}")
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
except RuntimeError as e:
|
|
||||||
logger.warning(f"User not found for update: {e}")
|
|
||||||
raise HTTPException(status_code=404, detail=str(e))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error updating user: {e}")
|
raise handle_service_error(e, "user update by ID")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
|
||||||
|
|
||||||
@router.post("", response_model=UserResponse, status_code=201)
|
@router.post("", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
|
||||||
async def create_user(
|
async def create_user(
|
||||||
user_data: UserCreate,
|
user_data: UserCreate,
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -128,15 +115,8 @@ async def create_user(
|
|||||||
response = await user_service.create_user(user_data)
|
response = await user_service.create_user(user_data)
|
||||||
logger.info(f"Created new user with email {user_data.email}")
|
logger.info(f"Created new user with email {user_data.email}")
|
||||||
return response
|
return response
|
||||||
except ValueError as e:
|
|
||||||
logger.warning(f"Invalid input for user creation: {e}")
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
except RuntimeError as e:
|
|
||||||
logger.warning(f"Resource not found for user creation: {e}")
|
|
||||||
raise HTTPException(status_code=404, detail=str(e))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error creating user: {e}")
|
raise handle_service_error(e, "user creation")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
|
||||||
|
|
||||||
@router.get("", response_model=UserListResponse)
|
@router.get("", response_model=UserListResponse)
|
||||||
async def list_users(
|
async def list_users(
|
||||||
@ -169,12 +149,8 @@ async def list_users(
|
|||||||
response = await user_service.list_users(team_id)
|
response = await user_service.list_users(team_id)
|
||||||
logger.info(f"Listed {response.total} users" + (f" for team {team_id}" if team_id else ""))
|
logger.info(f"Listed {response.total} users" + (f" for team {team_id}" if team_id else ""))
|
||||||
return response
|
return response
|
||||||
except ValueError as e:
|
|
||||||
logger.warning(f"Invalid team ID for user listing: {e}")
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error listing users: {e}")
|
raise handle_service_error(e, "user listing")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
|
||||||
|
|
||||||
@router.get("/{user_id}", response_model=UserResponse)
|
@router.get("/{user_id}", response_model=UserResponse)
|
||||||
async def get_user(
|
async def get_user(
|
||||||
@ -207,15 +183,8 @@ async def get_user(
|
|||||||
response = await user_service.get_user(user_id)
|
response = await user_service.get_user(user_id)
|
||||||
logger.info(f"Retrieved user {user_id}")
|
logger.info(f"Retrieved user {user_id}")
|
||||||
return response
|
return response
|
||||||
except ValueError as e:
|
|
||||||
logger.warning(f"Invalid user ID: {e}")
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
except RuntimeError as e:
|
|
||||||
logger.warning(f"User not found: {e}")
|
|
||||||
raise HTTPException(status_code=404, detail=str(e))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error getting user: {e}")
|
raise handle_service_error(e, "user retrieval")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
|
||||||
|
|
||||||
@router.put("/{user_id}", response_model=UserResponse)
|
@router.put("/{user_id}", response_model=UserResponse)
|
||||||
async def update_user(
|
async def update_user(
|
||||||
@ -251,17 +220,10 @@ async def update_user(
|
|||||||
response = await user_service.update_user(user_id, user_data)
|
response = await user_service.update_user(user_id, user_data)
|
||||||
logger.info(f"Updated user {user_id}")
|
logger.info(f"Updated user {user_id}")
|
||||||
return response
|
return response
|
||||||
except ValueError as e:
|
|
||||||
logger.warning(f"Invalid input for user update: {e}")
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
except RuntimeError as e:
|
|
||||||
logger.warning(f"User not found for update: {e}")
|
|
||||||
raise HTTPException(status_code=404, detail=str(e))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error updating user: {e}")
|
raise handle_service_error(e, "user update")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
|
||||||
|
|
||||||
@router.delete("/{user_id}", status_code=204)
|
@router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||||
async def delete_user(
|
async def delete_user(
|
||||||
user_id: str,
|
user_id: str,
|
||||||
request: Request,
|
request: Request,
|
||||||
@ -292,12 +254,5 @@ async def delete_user(
|
|||||||
await user_service.delete_user(user_id)
|
await user_service.delete_user(user_id)
|
||||||
logger.info(f"Deleted user {user_id}")
|
logger.info(f"Deleted user {user_id}")
|
||||||
return None
|
return None
|
||||||
except ValueError as e:
|
|
||||||
logger.warning(f"Invalid user ID for deletion: {e}")
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
except RuntimeError as e:
|
|
||||||
logger.warning(f"User not found for deletion: {e}")
|
|
||||||
raise HTTPException(status_code=404, detail=str(e))
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error deleting user: {e}")
|
raise handle_service_error(e, "user deletion")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
|
||||||
Loading…
x
Reference in New Issue
Block a user