Files
msa-django-nhn/nhn/packages/storage.py
icurfer 8c7739ffad
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
Add NHN Cloud API integration with async task support
- NHN Cloud API packages: token, vpc, compute, nks, storage
- REST API endpoints with Swagger documentation
- Async task processing for long-running operations
- CORS configuration for frontend integration
- Enhanced logging for debugging API calls

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 01:29:21 +09:00

280 lines
9.8 KiB
Python

"""
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}")