""" NHN Cloud Object Storage API Module Object Storage 컨테이너 및 오브젝트 관리 """ import logging from typing import Optional, List import requests from .base import BaseAPI, NHNCloudEndpoints, Region, NHNCloudAPIError logger = logging.getLogger(__name__) class ApiStorageObject(BaseAPI): """NHN Cloud Object Storage API 클래스""" def __init__(self, region: str, token: str, storage_account: str): """ Args: region: 리전 (kr1: 판교, kr2: 평촌) token: API 인증 토큰 storage_account: 스토리지 계정 (AUTH_...) """ super().__init__(region, token) self.storage_account = storage_account if self.region == Region.KR1: self.storage_url = NHNCloudEndpoints.STORAGE_KR1 else: self.storage_url = NHNCloudEndpoints.STORAGE_KR2 def _get_url(self, container: Optional[str] = None, obj: Optional[str] = None) -> str: """URL 생성""" parts = [self.storage_url, self.storage_account] if container: parts.append(container) if obj: parts.append(obj) return "/".join(parts) def _get_headers(self, extra_headers: Optional[dict] = None) -> dict: """Object Storage 전용 헤더""" headers = {"X-Auth-Token": self.token} if extra_headers: headers.update(extra_headers) return headers # ==================== Container ==================== def get_container_list(self) -> List[str]: """컨테이너 목록 조회""" url = self._get_url() try: response = self._session.get( url, headers=self._get_headers(), timeout=self.DEFAULT_TIMEOUT, ) if response.status_code >= 400: self._handle_error(response) return [c for c in response.text.split("\n") if c] except requests.exceptions.RequestException as e: logger.error(f"컨테이너 목록 조회 실패: {e}") raise NHNCloudAPIError(f"컨테이너 목록 조회 실패: {e}") def create_container(self, container_name: str) -> dict: """ 컨테이너 생성 Args: container_name: 컨테이너 이름 Returns: dict: 생성 결과 """ url = self._get_url(container_name) try: response = self._session.put( url, headers=self._get_headers(), timeout=self.DEFAULT_TIMEOUT, ) logger.info(f"컨테이너 생성: {container_name}") return {"status": response.status_code, "container": container_name} except requests.exceptions.RequestException as e: logger.error(f"컨테이너 생성 실패: {e}") raise NHNCloudAPIError(f"컨테이너 생성 실패: {e}") def delete_container(self, container_name: str) -> dict: """컨테이너 삭제""" url = self._get_url(container_name) try: response = self._session.delete( url, headers=self._get_headers(), timeout=self.DEFAULT_TIMEOUT, ) logger.info(f"컨테이너 삭제: {container_name}") return {"status": response.status_code, "container": container_name} except requests.exceptions.RequestException as e: logger.error(f"컨테이너 삭제 실패: {e}") raise NHNCloudAPIError(f"컨테이너 삭제 실패: {e}") def set_container_public(self, container_name: str, is_public: bool = True) -> dict: """ 컨테이너 공개/비공개 설정 Args: container_name: 컨테이너 이름 is_public: 공개 여부 (True: 공개, False: 비공개) Returns: dict: 설정 결과 """ url = self._get_url(container_name) headers = self._get_headers() headers["X-Container-Read"] = ".r:*" if is_public else "" try: response = self._session.post( url, headers=headers, timeout=self.DEFAULT_TIMEOUT, ) logger.info(f"컨테이너 공개 설정: {container_name} -> {is_public}") return {"status": response.status_code, "public": is_public} except requests.exceptions.RequestException as e: logger.error(f"컨테이너 공개 설정 실패: {e}") raise NHNCloudAPIError(f"컨테이너 공개 설정 실패: {e}") # ==================== Object ==================== def get_object_list(self, container_name: str) -> List[str]: """오브젝트 목록 조회""" url = self._get_url(container_name) try: response = self._session.get( url, headers=self._get_headers(), timeout=self.DEFAULT_TIMEOUT, ) if response.status_code >= 400: self._handle_error(response) return [o for o in response.text.split("\n") if o] except requests.exceptions.RequestException as e: logger.error(f"오브젝트 목록 조회 실패: {e}") raise NHNCloudAPIError(f"오브젝트 목록 조회 실패: {e}") def upload_object( self, container_name: str, object_name: str, file_path: str, ) -> dict: """ 오브젝트 업로드 Args: container_name: 컨테이너 이름 object_name: 오브젝트 이름 file_path: 업로드할 파일 경로 Returns: dict: 업로드 결과 """ url = self._get_url(container_name, object_name) try: with open(file_path, "rb") as f: response = self._session.put( url, headers=self._get_headers(), data=f.read(), timeout=self.DEFAULT_TIMEOUT, ) logger.info(f"오브젝트 업로드: {container_name}/{object_name}") return {"status": response.status_code, "object": object_name} except FileNotFoundError: raise NHNCloudAPIError(f"파일을 찾을 수 없습니다: {file_path}") except requests.exceptions.RequestException as e: logger.error(f"오브젝트 업로드 실패: {e}") raise NHNCloudAPIError(f"오브젝트 업로드 실패: {e}") def upload_object_data( self, container_name: str, object_name: str, data: bytes, content_type: Optional[str] = None, ) -> dict: """ 데이터를 오브젝트로 업로드 Args: container_name: 컨테이너 이름 object_name: 오브젝트 이름 data: 업로드할 데이터 content_type: Content-Type 헤더 Returns: dict: 업로드 결과 """ url = self._get_url(container_name, object_name) headers = self._get_headers() if content_type: headers["Content-Type"] = content_type try: response = self._session.put( url, headers=headers, data=data, timeout=self.DEFAULT_TIMEOUT, ) logger.info(f"오브젝트 업로드: {container_name}/{object_name}") return {"status": response.status_code, "object": object_name} except requests.exceptions.RequestException as e: logger.error(f"오브젝트 업로드 실패: {e}") raise NHNCloudAPIError(f"오브젝트 업로드 실패: {e}") def download_object(self, container_name: str, object_name: str) -> bytes: """ 오브젝트 다운로드 Args: container_name: 컨테이너 이름 object_name: 오브젝트 이름 Returns: bytes: 오브젝트 데이터 """ url = self._get_url(container_name, object_name) try: response = self._session.get( url, headers=self._get_headers(), timeout=self.DEFAULT_TIMEOUT, ) if response.status_code >= 400: self._handle_error(response) return response.content except requests.exceptions.RequestException as e: logger.error(f"오브젝트 다운로드 실패: {e}") raise NHNCloudAPIError(f"오브젝트 다운로드 실패: {e}") def delete_object(self, container_name: str, object_name: str) -> dict: """오브젝트 삭제""" url = self._get_url(container_name, object_name) try: response = self._session.delete( url, headers=self._get_headers(), timeout=self.DEFAULT_TIMEOUT, ) logger.info(f"오브젝트 삭제: {container_name}/{object_name}") return {"status": response.status_code, "object": object_name} except requests.exceptions.RequestException as e: logger.error(f"오브젝트 삭제 실패: {e}") raise NHNCloudAPIError(f"오브젝트 삭제 실패: {e}") def get_object_info(self, container_name: str, object_name: str) -> dict: """오브젝트 메타데이터 조회""" url = self._get_url(container_name, object_name) try: response = self._session.head( url, headers=self._get_headers(), timeout=self.DEFAULT_TIMEOUT, ) return dict(response.headers) except requests.exceptions.RequestException as e: logger.error(f"오브젝트 정보 조회 실패: {e}") raise NHNCloudAPIError(f"오브젝트 정보 조회 실패: {e}")