Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
- 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>
166 lines
5.8 KiB
Python
166 lines
5.8 KiB
Python
"""
|
|
NHN Cloud API Base Module
|
|
|
|
공통 기능을 제공하는 베이스 클래스
|
|
"""
|
|
|
|
import logging
|
|
from typing import Optional, Any
|
|
from dataclasses import dataclass
|
|
from enum import Enum
|
|
|
|
import requests
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class Region(str, Enum):
|
|
"""NHN Cloud 리전"""
|
|
KR1 = "kr1" # 판교
|
|
KR2 = "kr2" # 평촌
|
|
|
|
|
|
@dataclass
|
|
class NHNCloudEndpoints:
|
|
"""NHN Cloud API Endpoints"""
|
|
# Identity
|
|
IDENTITY = "https://api-identity-infrastructure.nhncloudservice.com"
|
|
|
|
# Compute
|
|
COMPUTE_KR1 = "https://kr1-api-instance-infrastructure.nhncloudservice.com"
|
|
COMPUTE_KR2 = "https://kr2-api-instance-infrastructure.nhncloudservice.com"
|
|
|
|
# Image
|
|
IMAGE_KR1 = "https://kr1-api-image-infrastructure.nhncloudservice.com"
|
|
IMAGE_KR2 = "https://kr2-api-image-infrastructure.nhncloudservice.com"
|
|
|
|
# Network (VPC)
|
|
NETWORK_KR1 = "https://kr1-api-network-infrastructure.nhncloudservice.com"
|
|
NETWORK_KR2 = "https://kr2-api-network-infrastructure.nhncloudservice.com"
|
|
|
|
# Kubernetes (NKS)
|
|
NKS_KR1 = "https://kr1-api-kubernetes-infrastructure.nhncloudservice.com"
|
|
NKS_KR2 = "https://kr2-api-kubernetes-infrastructure.nhncloudservice.com"
|
|
|
|
# Object Storage
|
|
STORAGE_KR1 = "https://kr1-api-object-storage.nhncloudservice.com/v1"
|
|
STORAGE_KR2 = "https://kr2-api-object-storage.nhncloudservice.com/v1"
|
|
|
|
|
|
class NHNCloudAPIError(Exception):
|
|
"""NHN Cloud API 에러"""
|
|
|
|
def __init__(self, message: str, code: Optional[int] = None, details: Optional[dict] = None):
|
|
self.message = message
|
|
self.code = code
|
|
self.details = details or {}
|
|
super().__init__(self.message)
|
|
|
|
|
|
class BaseAPI:
|
|
"""NHN Cloud API 베이스 클래스"""
|
|
|
|
DEFAULT_TIMEOUT = 30
|
|
|
|
def __init__(self, region: str, token: str):
|
|
self.region = Region(region.lower()) if isinstance(region, str) else region
|
|
self.token = token
|
|
self._session = requests.Session()
|
|
|
|
def _get_headers(self, extra_headers: Optional[dict] = None) -> dict:
|
|
"""기본 헤더 생성"""
|
|
headers = {
|
|
"X-Auth-Token": self.token,
|
|
"Content-Type": "application/json",
|
|
"Accept": "application/json",
|
|
}
|
|
if extra_headers:
|
|
headers.update(extra_headers)
|
|
return headers
|
|
|
|
def _request(
|
|
self,
|
|
method: str,
|
|
url: str,
|
|
params: Optional[dict] = None,
|
|
json_data: Optional[dict] = None,
|
|
headers: Optional[dict] = None,
|
|
timeout: Optional[int] = None,
|
|
) -> dict:
|
|
"""HTTP 요청 실행"""
|
|
# 토큰 앞 8자리만 로깅 (보안)
|
|
token_preview = self.token[:8] + "..." if self.token else "None"
|
|
logger.info(f"[BaseAPI] 요청 시작 - method={method}, url={url}, token={token_preview}")
|
|
if params:
|
|
logger.info(f"[BaseAPI] 요청 파라미터 - params={params}")
|
|
if json_data:
|
|
logger.info(f"[BaseAPI] 요청 바디 - json={json_data}")
|
|
|
|
try:
|
|
response = self._session.request(
|
|
method=method,
|
|
url=url,
|
|
params=params,
|
|
json=json_data,
|
|
headers=self._get_headers(headers),
|
|
timeout=timeout or self.DEFAULT_TIMEOUT,
|
|
)
|
|
|
|
logger.info(f"[BaseAPI] 응답 수신 - method={method}, url={url}, status_code={response.status_code}")
|
|
|
|
if response.status_code >= 400:
|
|
logger.error(f"[BaseAPI] 에러 응답 - status_code={response.status_code}, body={response.text[:500]}")
|
|
self._handle_error(response)
|
|
|
|
if response.text:
|
|
return response.json()
|
|
return {}
|
|
|
|
except requests.exceptions.Timeout:
|
|
logger.error(f"[BaseAPI] 타임아웃 - url={url}")
|
|
raise NHNCloudAPIError("요청 시간이 초과되었습니다.", code=408)
|
|
except requests.exceptions.ConnectionError as e:
|
|
logger.error(f"[BaseAPI] 연결 오류 - url={url}, error={e}")
|
|
raise NHNCloudAPIError("서버에 연결할 수 없습니다.", code=503)
|
|
except requests.exceptions.RequestException as e:
|
|
logger.error(f"[BaseAPI] 요청 오류 - url={url}, error={e}")
|
|
raise NHNCloudAPIError(f"요청 중 오류가 발생했습니다: {e}")
|
|
|
|
def _handle_error(self, response: requests.Response) -> None:
|
|
"""에러 응답 처리"""
|
|
try:
|
|
error_data = response.json()
|
|
if "error" in error_data:
|
|
error = error_data["error"]
|
|
raise NHNCloudAPIError(
|
|
message=error.get("message", "알 수 없는 오류"),
|
|
code=error.get("code", response.status_code),
|
|
details=error,
|
|
)
|
|
raise NHNCloudAPIError(
|
|
message=f"API 오류: {response.status_code}",
|
|
code=response.status_code,
|
|
details=error_data,
|
|
)
|
|
except ValueError:
|
|
raise NHNCloudAPIError(
|
|
message=f"API 오류: {response.status_code} - {response.text}",
|
|
code=response.status_code,
|
|
)
|
|
|
|
def _get(self, url: str, params: Optional[dict] = None, **kwargs) -> dict:
|
|
"""GET 요청"""
|
|
return self._request("GET", url, params=params, **kwargs)
|
|
|
|
def _post(self, url: str, json_data: Optional[dict] = None, **kwargs) -> dict:
|
|
"""POST 요청"""
|
|
return self._request("POST", url, json_data=json_data, **kwargs)
|
|
|
|
def _put(self, url: str, json_data: Optional[dict] = None, **kwargs) -> dict:
|
|
"""PUT 요청"""
|
|
return self._request("PUT", url, json_data=json_data, **kwargs)
|
|
|
|
def _delete(self, url: str, **kwargs) -> dict:
|
|
"""DELETE 요청"""
|
|
return self._request("DELETE", url, **kwargs)
|