o2o-castad-backend/app/social/exceptions.py

332 lines
10 KiB
Python

"""
Social Media Exceptions
소셜 미디어 연동 관련 예외 클래스를 정의합니다.
"""
from fastapi import status
class SocialException(Exception):
"""소셜 미디어 기본 예외"""
def __init__(
self,
message: str,
status_code: int = status.HTTP_500_INTERNAL_SERVER_ERROR,
code: str = "SOCIAL_ERROR",
):
self.message = message
self.status_code = status_code
self.code = code
super().__init__(self.message)
# =============================================================================
# OAuth 관련 예외
# =============================================================================
class OAuthException(SocialException):
"""OAuth 관련 예외 기본 클래스"""
def __init__(
self,
message: str = "OAuth 인증 중 오류가 발생했습니다.",
status_code: int = status.HTTP_401_UNAUTHORIZED,
code: str = "OAUTH_ERROR",
):
super().__init__(message, status_code, code)
class InvalidStateError(OAuthException):
"""CSRF state 토큰 불일치"""
def __init__(self, message: str = "유효하지 않은 인증 세션입니다. 다시 시도해주세요."):
super().__init__(
message=message,
status_code=status.HTTP_400_BAD_REQUEST,
code="INVALID_STATE",
)
class OAuthStateExpiredError(OAuthException):
"""OAuth state 토큰 만료"""
def __init__(self, message: str = "인증 세션이 만료되었습니다. 다시 시도해주세요."):
super().__init__(
message=message,
status_code=status.HTTP_400_BAD_REQUEST,
code="STATE_EXPIRED",
)
class OAuthTokenError(OAuthException):
"""OAuth 토큰 교환 실패"""
def __init__(self, platform: str, message: str = ""):
error_message = f"{platform} 토큰 발급에 실패했습니다."
if message:
error_message += f" ({message})"
super().__init__(
message=error_message,
status_code=status.HTTP_401_UNAUTHORIZED,
code="TOKEN_EXCHANGE_FAILED",
)
class TokenRefreshError(OAuthException):
"""토큰 갱신 실패"""
def __init__(self, platform: str):
super().__init__(
message=f"{platform} 토큰 갱신에 실패했습니다. 재연동이 필요합니다.",
status_code=status.HTTP_401_UNAUTHORIZED,
code="TOKEN_REFRESH_FAILED",
)
class OAuthCodeExchangeError(OAuthException):
"""OAuth 인가 코드 교환 실패"""
def __init__(self, platform: str, detail: str = ""):
error_message = f"{platform} 인가 코드 교환에 실패했습니다."
if detail:
error_message += f" ({detail})"
super().__init__(
message=error_message,
status_code=status.HTTP_401_UNAUTHORIZED,
code="CODE_EXCHANGE_FAILED",
)
class OAuthTokenRefreshError(OAuthException):
"""OAuth 토큰 갱신 실패"""
def __init__(self, platform: str, detail: str = ""):
error_message = f"{platform} 토큰 갱신에 실패했습니다."
if detail:
error_message += f" ({detail})"
super().__init__(
message=error_message,
status_code=status.HTTP_401_UNAUTHORIZED,
code="TOKEN_REFRESH_FAILED",
)
class TokenExpiredError(OAuthException):
"""토큰 만료"""
def __init__(self, platform: str):
super().__init__(
message=f"{platform} 인증이 만료되었습니다. 재연동이 필요합니다.",
status_code=status.HTTP_401_UNAUTHORIZED,
code="TOKEN_EXPIRED",
)
self.platform = platform
# =============================================================================
# 소셜 계정 관련 예외
# =============================================================================
class SocialAccountException(SocialException):
"""소셜 계정 관련 예외 기본 클래스"""
pass
class SocialAccountNotFoundError(SocialAccountException):
"""연동된 계정을 찾을 수 없음"""
def __init__(self, platform: str = ""):
message = f"{platform} 계정이 연동되어 있지 않습니다." if platform else "연동된 소셜 계정이 없습니다."
super().__init__(
message=message,
status_code=status.HTTP_404_NOT_FOUND,
code="SOCIAL_ACCOUNT_NOT_FOUND",
)
class SocialAccountAlreadyExistsError(SocialAccountException):
"""이미 연동된 계정이 존재함"""
def __init__(self, platform: str):
super().__init__(
message=f"이미 {platform} 계정이 연동되어 있습니다.",
status_code=status.HTTP_409_CONFLICT,
code="SOCIAL_ACCOUNT_EXISTS",
)
# Alias for backward compatibility
SocialAccountAlreadyConnectedError = SocialAccountAlreadyExistsError
class SocialAccountInactiveError(SocialAccountException):
"""비활성화된 소셜 계정"""
def __init__(self, platform: str):
super().__init__(
message=f"{platform} 계정이 비활성화 상태입니다. 재연동이 필요합니다.",
status_code=status.HTTP_403_FORBIDDEN,
code="SOCIAL_ACCOUNT_INACTIVE",
)
class SocialAccountError(SocialAccountException):
"""소셜 계정 일반 오류"""
def __init__(self, platform: str, detail: str = ""):
error_message = f"{platform} 계정 처리 중 오류가 발생했습니다."
if detail:
error_message += f" ({detail})"
super().__init__(
message=error_message,
status_code=status.HTTP_400_BAD_REQUEST,
code="SOCIAL_ACCOUNT_ERROR",
)
# =============================================================================
# 업로드 관련 예외
# =============================================================================
class UploadException(SocialException):
"""업로드 관련 예외 기본 클래스"""
pass
class UploadError(UploadException):
"""업로드 일반 오류"""
def __init__(self, platform: str, detail: str = ""):
error_message = f"{platform} 업로드 중 오류가 발생했습니다."
if detail:
error_message += f" ({detail})"
super().__init__(
message=error_message,
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
code="UPLOAD_ERROR",
)
class UploadValidationError(UploadException):
"""업로드 유효성 검사 실패"""
def __init__(self, message: str):
super().__init__(
message=message,
status_code=status.HTTP_400_BAD_REQUEST,
code="UPLOAD_VALIDATION_FAILED",
)
class VideoNotFoundError(UploadException):
"""영상을 찾을 수 없음"""
def __init__(self, video_id: int, detail: str = ""):
message = f"영상을 찾을 수 없습니다. (video_id: {video_id})"
if detail:
message = detail
super().__init__(
message=message,
status_code=status.HTTP_404_NOT_FOUND,
code="VIDEO_NOT_FOUND",
)
class VideoNotReadyError(UploadException):
"""영상이 준비되지 않음"""
def __init__(self, video_id: int):
super().__init__(
message=f"영상이 아직 준비되지 않았습니다. (video_id: {video_id})",
status_code=status.HTTP_400_BAD_REQUEST,
code="VIDEO_NOT_READY",
)
class UploadFailedError(UploadException):
"""업로드 실패"""
def __init__(self, platform: str, message: str = ""):
error_message = f"{platform} 업로드에 실패했습니다."
if message:
error_message += f" ({message})"
super().__init__(
message=error_message,
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
code="UPLOAD_FAILED",
)
class UploadQuotaExceededError(UploadException):
"""업로드 할당량 초과"""
def __init__(self, platform: str):
super().__init__(
message=f"{platform} 일일 업로드 할당량이 초과되었습니다.",
status_code=status.HTTP_429_TOO_MANY_REQUESTS,
code="UPLOAD_QUOTA_EXCEEDED",
)
class UploadNotFoundError(UploadException):
"""업로드 기록을 찾을 수 없음"""
def __init__(self, upload_id: int):
super().__init__(
message=f"업로드 기록을 찾을 수 없습니다. (upload_id: {upload_id})",
status_code=status.HTTP_404_NOT_FOUND,
code="UPLOAD_NOT_FOUND",
)
# =============================================================================
# 플랫폼 API 관련 예외
# =============================================================================
class PlatformAPIError(SocialException):
"""플랫폼 API 호출 오류"""
def __init__(self, platform: str, message: str = ""):
error_message = f"{platform} API 호출 중 오류가 발생했습니다."
if message:
error_message += f" ({message})"
super().__init__(
message=error_message,
status_code=status.HTTP_502_BAD_GATEWAY,
code="PLATFORM_API_ERROR",
)
class RateLimitError(PlatformAPIError):
"""API 요청 한도 초과"""
def __init__(self, platform: str, retry_after: int | None = None):
message = f"{platform} API 요청 한도가 초과되었습니다."
if retry_after:
message += f" {retry_after}초 후에 다시 시도해주세요."
super().__init__(
platform=platform,
message=message,
)
self.retry_after = retry_after
self.code = "RATE_LIMIT_EXCEEDED"
class UnsupportedPlatformError(SocialException):
"""지원하지 않는 플랫폼"""
def __init__(self, platform: str):
super().__init__(
message=f"지원하지 않는 플랫폼입니다: {platform}",
status_code=status.HTTP_400_BAD_REQUEST,
code="UNSUPPORTED_PLATFORM",
)