Files
cloud-server/API.md
kappa abe052b538 feat: 코드 품질 개선 및 추천 API 구현
## 주요 변경사항

### 신규 기능
- POST /recommend: 기술 스택 기반 인스턴스 추천 API
- 아시아 리전 필터링 (Seoul, Tokyo, Osaka, Singapore)
- 매칭 점수 알고리즘 (메모리 40%, vCPU 30%, 가격 20%, 스토리지 10%)

### 보안 강화 (Security 9.0/10)
- API Key 인증 + constant-time 비교 (타이밍 공격 방어)
- Rate Limiting: KV 기반 분산 처리, fail-closed 정책
- IP Spoofing 방지 (CF-Connecting-IP만 신뢰)
- 요청 본문 10KB 제한
- CORS + 보안 헤더 (CSP, HSTS, X-Frame-Options)

### 성능 최적화 (Performance 9.0/10)
- Generator 패턴: AWS pricing 메모리 95% 감소
- D1 batch 쿼리: N+1 문제 해결
- 복합 인덱스 추가 (migrations/002)

### 코드 품질 (QA 9.0/10)
- 127개 테스트 (vitest)
- 구조화된 로깅 (민감정보 마스킹)
- 상수 중앙화 (constants.ts)
- 입력 검증 유틸리티 (utils/validation.ts)

### Vultr 연동 수정
- relay 서버 헤더: Authorization: Bearer → X-API-Key

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 11:57:35 +09:00

691 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Cloud Instances API Documentation
## 개요
클라우드 인스턴스 가격 비교 및 기술 스택 기반 추천 API
- **Base URL**: `https://cloud-instances-api.kappa-d8e.workers.dev`
- **인증**: `X-API-Key` 헤더 필수
- **Providers**: Linode, Vultr, AWS
- **지원 리전**: 아시아 (서울, 도쿄, 오사카, 싱가포르, 홍콩)
---
## 인증
모든 API 요청에 `X-API-Key` 헤더 필요:
```http
X-API-Key: your-api-key
```
인증 실패 시:
```json
{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "API key is missing or invalid"
}
}
```
---
## 엔드포인트
### 1. Health Check
시스템 상태 및 provider 동기화 상태 확인
**Request**
```http
GET /health
```
**Response**
```json
{
"status": "healthy",
"timestamp": "2025-01-22T10:00:00.000Z",
"components": {
"database": {
"status": "healthy",
"latency_ms": 12
},
"providers": [
{
"name": "linode",
"status": "healthy",
"last_sync": "2025-01-22 09:30:00",
"sync_status": "success",
"regions_count": 11,
"instances_count": 45
},
{
"name": "vultr",
"status": "healthy",
"last_sync": "2025-01-22 09:28:00",
"sync_status": "success",
"regions_count": 8,
"instances_count": 32
},
{
"name": "aws",
"status": "degraded",
"last_sync": "2025-01-21 15:00:00",
"sync_status": "success",
"regions_count": 15,
"instances_count": 120,
"error": "Sync delayed"
}
]
},
"summary": {
"total_providers": 3,
"healthy_providers": 2,
"total_regions": 34,
"total_instances": 197
}
}
```
**상태 코드**
- `200`: 시스템 정상 (all components healthy)
- `503`: 시스템 문제 (degraded or unhealthy)
**Health Status**
- `healthy`: 정상 작동 (최근 24시간 이내 동기화)
- `degraded`: 부분 문제 (24-48시간 이내 동기화 또는 일부 에러)
- `unhealthy`: 심각한 문제 (48시간 이상 미동기화 또는 데이터베이스 연결 실패)
---
### 2. 인스턴스 목록 조회
조건에 맞는 인스턴스 조회 (필터링, 정렬, 페이지네이션 지원)
**Request**
```http
GET /instances?provider=linode&min_vcpu=2&max_price=50&sort_by=price&order=asc&limit=20
```
**Query Parameters**
| 파라미터 | 타입 | 필수 | 설명 | 기본값 |
|---------|------|------|------|--------|
| `provider` | string | ❌ | Provider 필터 (`linode`, `vultr`, `aws`) | - |
| `region` | string | ❌ | 리전 코드 필터 (예: `ap-northeast-1`) | - |
| `min_vcpu` | integer | ❌ | 최소 vCPU 수 | - |
| `max_vcpu` | integer | ❌ | 최대 vCPU 수 | - |
| `min_memory_gb` | number | ❌ | 최소 메모리 (GB) | - |
| `max_memory_gb` | number | ❌ | 최대 메모리 (GB) | - |
| `max_price` | number | ❌ | 최대 월 가격 (USD) | - |
| `instance_family` | string | ❌ | 인스턴스 패밀리 (`general`, `compute`, `memory`, `storage`, `gpu`) | - |
| `has_gpu` | boolean | ❌ | GPU 인스턴스 필터 (`true`, `false`) | - |
| `sort_by` | string | ❌ | 정렬 필드 (아래 참조) | - |
| `order` | string | ❌ | 정렬 순서 (`asc`, `desc`) | `asc` |
| `limit` | integer | ❌ | 결과 개수 (1-100) | 50 |
| `offset` | integer | ❌ | 결과 오프셋 (페이지네이션) | 0 |
**유효한 정렬 필드**
- `price` / `monthly_price` / `hourly_price`
- `vcpu`
- `memory_mb` / `memory_gb`
- `storage_gb`
- `instance_name`
- `provider`
- `region`
**Response**
```json
{
"success": true,
"data": {
"instances": [
{
"id": 123,
"instance_id": "g6-standard-2",
"instance_name": "Linode 4GB",
"vcpu": 2,
"memory_mb": 4096,
"storage_gb": 80,
"transfer_tb": 4,
"network_speed_gbps": 4,
"gpu_count": 0,
"gpu_type": null,
"instance_family": "general",
"provider": {
"id": 1,
"name": "linode",
"display_name": "Linode"
},
"region": {
"id": 5,
"region_code": "ap-northeast",
"region_name": "Tokyo (jp-tyo-3)",
"country_code": "JP"
},
"pricing": {
"hourly_price": 0.036,
"monthly_price": 24.0,
"currency": "USD",
"available": 1
}
}
],
"pagination": {
"total": 45,
"limit": 20,
"offset": 0,
"has_more": true
},
"metadata": {
"cached": false,
"last_sync": "2025-01-22T10:00:00.000Z",
"query_time_ms": 45,
"filters_applied": {
"provider": "linode",
"min_vcpu": 2,
"max_price": 50
}
}
}
}
```
**상태 코드**
- `200`: 성공
- `400`: 잘못된 파라미터
- `500`: 서버 에러
**캐시 동작**
- TTL: 5분 (300초)
- 캐시 히트 시 `metadata.cached: true`
- 캐시 헤더: `Cache-Control: public, max-age=300`
---
### 3. 데이터 동기화
Provider API에서 최신 데이터 가져오기
**Request**
```http
POST /sync
Content-Type: application/json
{
"providers": ["linode", "vultr", "aws"],
"force": false
}
```
**Request Body**
| 필드 | 타입 | 필수 | 설명 | 기본값 |
|------|------|------|------|--------|
| `providers` | string[] | ❌ | 동기화할 provider 목록 | `["linode"]` |
| `force` | boolean | ❌ | 강제 동기화 여부 (사용되지 않음) | `false` |
**Response**
```json
{
"success": true,
"data": {
"sync_id": "sync_1737545678901_abc123def",
"success": true,
"started_at": "2025-01-22T10:00:00.000Z",
"completed_at": "2025-01-22T10:02:15.000Z",
"total_duration_ms": 135000,
"providers": [
{
"provider": "linode",
"success": true,
"regions_synced": 11,
"instances_synced": 45,
"pricing_synced": 495,
"duration_ms": 45000
},
{
"provider": "vultr",
"success": true,
"regions_synced": 8,
"instances_synced": 32,
"pricing_synced": 256,
"duration_ms": 38000
},
{
"provider": "aws",
"success": false,
"regions_synced": 0,
"instances_synced": 0,
"pricing_synced": 0,
"duration_ms": 52000,
"error": "API authentication failed",
"error_details": {
"code": "CREDENTIALS_ERROR",
"message": "Invalid AWS credentials"
}
}
],
"summary": {
"total_providers": 3,
"successful_providers": 2,
"failed_providers": 1,
"total_regions": 19,
"total_instances": 77,
"total_pricing": 751
}
}
}
```
**상태 코드**
- `200`: 동기화 완료 (일부 실패 포함)
- `400`: 잘못된 요청 (잘못된 provider 이름 등)
- `500`: 서버 에러
**에러 케이스**
```json
{
"success": false,
"error": {
"code": "UNSUPPORTED_PROVIDERS",
"message": "Unsupported providers: digitalocean",
"details": {
"unsupported": ["digitalocean"],
"supported": ["linode", "vultr", "aws"]
}
}
}
```
---
### 4. 기술 스택 기반 인스턴스 추천
기술 스택과 규모에 맞는 최적 인스턴스 추천
**Request**
```http
POST /recommend
Content-Type: application/json
{
"stack": ["nginx", "php-fpm", "mysql"],
"scale": "medium",
"budget_max": 50
}
```
**Request Body**
| 필드 | 타입 | 필수 | 설명 |
|------|------|------|------|
| `stack` | string[] | ✅ | 기술 스택 목록 (아래 참조) |
| `scale` | string | ✅ | 배포 규모 (`small`, `medium`, `large`) |
| `budget_max` | number | ❌ | 월 최대 예산 (USD) |
**지원 기술 스택**
| 스택 | 최소 메모리 | 권장 메모리 |
|------|------------|------------|
| `nginx` | 128 MB | 256 MB |
| `php-fpm` | 512 MB | 1 GB |
| `mysql` | 1 GB | 2 GB |
| `mariadb` | 1 GB | 2 GB |
| `postgresql` | 1 GB | 2 GB |
| `redis` | 256 MB | 512 MB |
| `elasticsearch` | 2 GB | 4 GB |
| `nodejs` | 512 MB | 1 GB |
| `docker` | 1 GB | 2 GB |
| `mongodb` | 1 GB | 2 GB |
**스케일별 리소스 계산**
| 스케일 | 메모리 계산 | vCPU 계산 |
|--------|------------|-----------|
| `small` | 최소 사양 | 메모리 기반 (2GB당 1 vCPU) |
| `medium` | 권장 사양 | 메모리 기반 (2GB당 1 vCPU) |
| `large` | 권장 × 1.5배 | 메모리 기반 (2GB당 1 vCPU) |
- OS 오버헤드: **768 MB** (모든 스케일 공통)
- 최소 vCPU: **1개**
**Response**
```json
{
"success": true,
"data": {
"requirements": {
"min_memory_mb": 4096,
"min_vcpu": 2,
"breakdown": {
"nginx": "256MB",
"php-fpm": "1GB",
"mysql": "2GB",
"os_overhead": "768MB"
}
},
"recommendations": [
{
"rank": 1,
"provider": "linode",
"instance": "Linode 4GB",
"region": "Tokyo (jp-tyo-3)",
"specs": {
"vcpu": 2,
"memory_mb": 4096,
"storage_gb": 80
},
"price": {
"monthly": 24.0,
"hourly": 0.036
},
"match_score": 95,
"pros": [
"메모리 최적 적합",
"vCPU 적합",
"스토리지 80GB 포함"
],
"cons": []
},
{
"rank": 2,
"provider": "vultr",
"instance": "4GB Memory",
"region": "Seoul (icn)",
"specs": {
"vcpu": 2,
"memory_mb": 4096,
"storage_gb": 80
},
"price": {
"monthly": 24.0,
"hourly": 0.036
},
"match_score": 95,
"pros": [
"메모리 최적 적합",
"vCPU 적합",
"스토리지 80GB 포함"
],
"cons": []
},
{
"rank": 3,
"provider": "linode",
"instance": "Linode 8GB",
"region": "Tokyo (jp-tyo-3)",
"specs": {
"vcpu": 4,
"memory_mb": 8192,
"storage_gb": 160
},
"price": {
"monthly": 48.0,
"hourly": 0.072
},
"match_score": 75,
"pros": [
"vCPU 여유 (4 cores)",
"메모리 충분 (8GB)",
"스토리지 160GB 포함"
],
"cons": [
"예산 초과 ($48 > $50)"
]
}
]
},
"meta": {
"query_time_ms": 85
}
}
```
**매칭 스코어 계산**
| 조건 | 점수 |
|------|------|
| 메모리 정확히 일치 | 100점 |
| 메모리 20% 초과 | 90점 |
| 메모리 50% 초과 | 70점 |
| 메모리 부족 | 0점 (제외) |
| vCPU 일치 | +0점 |
| vCPU 부족 | -10점 |
| 예산 초과 | -20점 |
**상태 코드**
- `200`: 성공
- `400`: 잘못된 요청
- `500`: 서버 에러
**에러 케이스**
```json
{
"success": false,
"error": {
"code": "INVALID_STACK",
"message": "Unsupported stacks: mongodb-atlas",
"details": {
"invalid": ["mongodb-atlas"],
"supported": [
"nginx", "php-fpm", "mysql", "mariadb",
"postgresql", "redis", "elasticsearch",
"nodejs", "docker", "mongodb"
]
}
}
}
```
---
## 아시아 리전 목록
### Linode
- `ap-northeast` - Tokyo (jp-tyo-3)
- `ap-south` - Osaka (jp-osa-1)
- `ap-southeast` - Singapore (sg-sin-1)
### Vultr
- `icn` - Seoul
- `nrt` - Tokyo
- `itm` - Osaka
### AWS
- `ap-northeast-1` - Tokyo
- `ap-northeast-2` - Seoul
- `ap-northeast-3` - Osaka
- `ap-southeast-1` - Singapore
- `ap-east-1` - Hong Kong
---
## 에러 응답 형식
모든 에러는 아래 형식으로 응답:
```json
{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human readable error message",
"details": {
"additional": "error details"
}
}
}
```
**주요 에러 코드**
| 코드 | 설명 | 상태 코드 |
|------|------|----------|
| `UNAUTHORIZED` | 인증 실패 | 401 |
| `INVALID_PARAMETER` | 잘못된 파라미터 | 400 |
| `MISSING_PARAMETER` | 필수 파라미터 누락 | 400 |
| `INVALID_CONTENT_TYPE` | Content-Type이 application/json이 아님 | 400 |
| `INVALID_JSON` | JSON 파싱 실패 | 400 |
| `INVALID_STACK` | 지원하지 않는 기술 스택 | 400 |
| `EMPTY_STACK` | 스택 배열이 비어 있음 | 400 |
| `UNSUPPORTED_PROVIDERS` | 지원하지 않는 provider | 400 |
| `QUERY_FAILED` | 쿼리 실행 실패 | 500 |
| `INTERNAL_ERROR` | 서버 내부 에러 | 500 |
| `SYNC_FAILED` | 동기화 작업 실패 | 500 |
---
## Rate Limiting
| 엔드포인트 | 제한 |
|-----------|------|
| `/health` | 제한 없음 |
| `/instances` | 100 req/min |
| `/sync` | 10 req/min |
| `/recommend` | 60 req/min |
Rate limit 초과 시:
```json
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Please try again later.",
"details": {
"limit": 60,
"window": "1 minute",
"retry_after": 45
}
}
}
```
---
## 사용 예시
### 1. 워드프레스 서버 추천
```bash
curl -X POST "https://cloud-instances-api.kappa-d8e.workers.dev/recommend" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{
"stack": ["nginx", "php-fpm", "mysql"],
"scale": "medium"
}'
```
**예상 요구사항**
- 메모리: 4 GB (nginx 256MB + php-fpm 1GB + mysql 2GB + OS 768MB)
- vCPU: 2 cores
---
### 2. Node.js 앱 (예산 제한 있음)
```bash
curl -X POST "https://cloud-instances-api.kappa-d8e.workers.dev/recommend" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{
"stack": ["nginx", "nodejs", "redis"],
"scale": "small",
"budget_max": 20
}'
```
**예상 요구사항**
- 메모리: 1.6 GB (nginx 128MB + nodejs 512MB + redis 256MB + OS 768MB)
- vCPU: 1 core
- 예산: $20 이하
---
### 3. 대규모 전자상거래 플랫폼
```bash
curl -X POST "https://cloud-instances-api.kappa-d8e.workers.dev/recommend" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{
"stack": ["nginx", "nodejs", "postgresql", "redis", "elasticsearch"],
"scale": "large"
}'
```
**예상 요구사항**
- 메모리: 13.5 GB (nginx 384MB + nodejs 1.5GB + postgresql 3GB + redis 768MB + elasticsearch 6GB + OS 768MB)
- vCPU: 7 cores
---
### 4. Provider별 가격 비교
```bash
# Linode만 조회
curl -X GET "https://cloud-instances-api.kappa-d8e.workers.dev/instances?provider=linode&min_vcpu=2&max_price=30&sort_by=price&order=asc&limit=10" \
-H "X-API-Key: YOUR_API_KEY"
# Vultr만 조회
curl -X GET "https://cloud-instances-api.kappa-d8e.workers.dev/instances?provider=vultr&min_vcpu=2&max_price=30&sort_by=price&order=asc&limit=10" \
-H "X-API-Key: YOUR_API_KEY"
# 모든 provider 조회 (가격 순 정렬)
curl -X GET "https://cloud-instances-api.kappa-d8e.workers.dev/instances?min_vcpu=2&max_price=30&sort_by=price&order=asc&limit=30" \
-H "X-API-Key: YOUR_API_KEY"
```
---
### 5. 특정 리전에서 GPU 인스턴스 검색
```bash
curl -X GET "https://cloud-instances-api.kappa-d8e.workers.dev/instances?region=ap-northeast-1&has_gpu=true&sort_by=price&order=asc" \
-H "X-API-Key: YOUR_API_KEY"
```
---
### 6. 메모리 최적화 인스턴스 검색
```bash
curl -X GET "https://cloud-instances-api.kappa-d8e.workers.dev/instances?instance_family=memory&min_memory_gb=16&sort_by=price&order=asc" \
-H "X-API-Key: YOUR_API_KEY"
```
---
## 버전 정보
- **API Version**: 1.0.0
- **Last Updated**: 2025-01-22
- **Runtime**: Cloudflare Workers
- **Database**: Cloudflare D1 (SQLite)
---
## 지원 및 문의
- **GitHub**: https://github.com/your-org/cloud-instances-api
- **Email**: support@example.com
- **Documentation**: https://docs.example.com
---
## 변경 이력
### v1.0.0 (2025-01-22)
- ✅ 초기 릴리즈
-`/health` 엔드포인트 구현
-`/instances` 엔드포인트 구현 (필터링, 정렬, 페이지네이션)
-`/sync` 엔드포인트 구현 (Linode, Vultr, AWS)
-`/recommend` 엔드포인트 구현 (기술 스택 기반 추천)
- ✅ 캐시 시스템 구현 (5분 TTL)
- ✅ 아시아 리전 지원
- ✅ Rate limiting 구현