Files
cloud-server/src/constants.ts
kappa 5a9362bf43 feat: P1 보안/성능 개선 및 마이그레이션 자동화
Security fixes:
- migrate.ts: SQL/Command Injection 방지 (spawnSync 사용)
- migrate.ts: Path Traversal 검증 추가
- api-tester.ts: API 키 마스킹 (4자만 노출)
- api-tester.ts: 최소 16자 키 길이 검증
- cache.ts: ReDoS 방지 (패턴 길이/와일드카드 제한)

Performance improvements:
- cache.ts: 순차 삭제 → 병렬 배치 처리 (50개씩)
- cache.ts: KV 등록 fire-and-forget (non-blocking)
- cache.ts: 메모리 제한 (5000키)
- cache.ts: 25초 실행 시간 가드
- cache.ts: 패턴 매칭 prefix 최적화

New features:
- 마이그레이션 자동화 시스템 (scripts/migrate.ts)
- KV 기반 캐시 인덱스 (invalidatePattern, clearAll)
- 글로벌 CacheService 싱글톤

Other:
- .env.example 추가, API 키 환경변수 처리
- CACHE_TTL.RECOMMENDATIONS (10분) 분리
- e2e-tester.ts JSON 파싱 에러 핸들링 개선

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 00:23:13 +09:00

285 lines
7.7 KiB
TypeScript
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 Server API - Constants
*
* Centralized constants for the cloud server API.
* All magic numbers and repeated constants should be defined here.
*/
// ============================================================
// Provider Configuration
// ============================================================
/**
* Supported cloud providers
*/
export const SUPPORTED_PROVIDERS = ['linode', 'vultr', 'aws'] as const;
export type SupportedProvider = typeof SUPPORTED_PROVIDERS[number];
// ============================================================
// Cache Configuration
// ============================================================
/**
* Cache TTL values in seconds
*/
export const CACHE_TTL = {
/** Cache TTL for instance queries (5 minutes) */
INSTANCES: 300,
/** Cache TTL for health checks (30 seconds) */
HEALTH: 30,
/** Cache TTL for pricing data (1 hour) */
PRICING: 3600,
/** Cache TTL for recommendation results (10 minutes) */
RECOMMENDATIONS: 600,
/** Default cache TTL (5 minutes) */
DEFAULT: 300,
} as const;
/**
* Cache TTL values in milliseconds
*/
export const CACHE_TTL_MS = {
/** Cache TTL for instance queries (5 minutes) */
INSTANCES: 5 * 60 * 1000,
/** Cache TTL for health checks (30 seconds) */
HEALTH: 30 * 1000,
/** Cache TTL for pricing data (1 hour) */
PRICING: 60 * 60 * 1000,
} as const;
// ============================================================
// Rate Limiting Configuration
// ============================================================
/**
* Rate limiting defaults
*/
export const RATE_LIMIT_DEFAULTS = {
/** Time window in milliseconds (1 minute) */
WINDOW_MS: 60 * 1000,
/** Maximum requests per window for /instances endpoint */
MAX_REQUESTS_INSTANCES: 100,
/** Maximum requests per window for /sync endpoint */
MAX_REQUESTS_SYNC: 10,
/** Maximum requests per window for /recommend endpoint */
MAX_REQUESTS_RECOMMEND: 50,
} as const;
// ============================================================
// Pagination Configuration
// ============================================================
/**
* Pagination defaults
*/
export const PAGINATION = {
/** Default page number (1-indexed) */
DEFAULT_PAGE: 1,
/** Default number of results per page */
DEFAULT_LIMIT: 50,
/** Maximum number of results per page */
MAX_LIMIT: 100,
/** Default offset for pagination */
DEFAULT_OFFSET: 0,
} as const;
// ============================================================
// HTTP Status Codes
// ============================================================
/**
* HTTP status codes used throughout the API
*/
export const HTTP_STATUS = {
/** 200 - OK */
OK: 200,
/** 201 - Created */
CREATED: 201,
/** 204 - No Content */
NO_CONTENT: 204,
/** 400 - Bad Request */
BAD_REQUEST: 400,
/** 401 - Unauthorized */
UNAUTHORIZED: 401,
/** 404 - Not Found */
NOT_FOUND: 404,
/** 413 - Payload Too Large */
PAYLOAD_TOO_LARGE: 413,
/** 429 - Too Many Requests */
TOO_MANY_REQUESTS: 429,
/** 500 - Internal Server Error */
INTERNAL_ERROR: 500,
/** 503 - Service Unavailable */
SERVICE_UNAVAILABLE: 503,
} as const;
// ============================================================
// Database Configuration
// ============================================================
/**
* Database table names
*/
export const TABLES = {
PROVIDERS: 'providers',
REGIONS: 'regions',
INSTANCE_TYPES: 'instance_types',
PRICING: 'pricing',
PRICE_HISTORY: 'price_history',
} as const;
// ============================================================
// Query Configuration
// ============================================================
/**
* Valid sort fields for instance queries
*/
export const VALID_SORT_FIELDS = [
'price',
'hourly_price',
'monthly_price',
'vcpu',
'memory_mb',
'memory_gb',
'storage_gb',
'instance_name',
'provider',
'region',
] as const;
export type ValidSortField = typeof VALID_SORT_FIELDS[number];
/**
* Valid sort orders
*/
export const SORT_ORDERS = ['asc', 'desc'] as const;
export type SortOrder = typeof SORT_ORDERS[number];
/**
* Valid instance families
*/
export const INSTANCE_FAMILIES = ['general', 'compute', 'memory', 'storage', 'gpu'] as const;
export type InstanceFamily = typeof INSTANCE_FAMILIES[number];
// ============================================================
// CORS Configuration
// ============================================================
/**
* CORS configuration
*
* Security: Explicit allowed origins only. No wildcard fallback.
* Development origins are separated and only used in development environment.
*/
export const CORS = {
/** Default CORS origin - explicit production origin */
DEFAULT_ORIGIN: 'https://anvil.it.com',
/** Allowed production origins for CORS */
ALLOWED_ORIGINS: [
'https://anvil.it.com',
'https://cloud.anvil.it.com',
'https://hosting.anvil.it.com',
] as string[],
/** Development origins - only used when ENVIRONMENT === 'development' */
DEVELOPMENT_ORIGINS: [
'http://localhost:3000',
'http://127.0.0.1:3000',
] as string[],
/** Max age for CORS preflight cache (24 hours) */
MAX_AGE: '86400',
} as const;
// ============================================================
// Timeout Configuration
// ============================================================
/**
* Timeout values in milliseconds
*/
export const TIMEOUTS = {
/** Request timeout for AWS API calls (15 seconds) */
AWS_REQUEST: 15000,
/** Default API request timeout (30 seconds) */
DEFAULT_REQUEST: 30000,
} as const;
// ============================================================
// Validation Constants
// ============================================================
/**
* Validation rules
*/
export const VALIDATION = {
/** Minimum memory in MB */
MIN_MEMORY_MB: 1,
/** Minimum vCPU count */
MIN_VCPU: 1,
/** Minimum price in USD */
MIN_PRICE: 0,
} as const;
// ============================================================
// Request Security Configuration
// ============================================================
/**
* Request security limits
*/
export const REQUEST_LIMITS = {
/** Maximum request body size in bytes (10KB) */
MAX_BODY_SIZE: 10 * 1024,
} as const;
// ============================================================
// USD Retail Pricing Configuration
// ============================================================
/**
* Default USD retail pricing configuration
*
* These defaults are used to calculate retail prices from wholesale prices.
* Calculation formula:
* Retail = Wholesale × Margin (1.1) × VAT (1.1)
* Retail = Wholesale × 1.21
*/
export const USD_RETAIL_DEFAULTS = {
/** Margin multiplier (10% margin) */
MARGIN_MULTIPLIER: 1.1,
/** VAT multiplier (10% VAT) */
VAT_MULTIPLIER: 1.1,
/** Total multiplier (margin × VAT) */
TOTAL_MULTIPLIER: 1.21,
} as const;
/**
* Calculate USD retail hourly price from wholesale price
* Applies margin and VAT
*
* @param wholesale - Wholesale hourly price in USD
* @returns Retail price in USD, rounded to 4 decimal places
*
* @example
* calculateRetailHourly(0.0075) // Returns 0.0091 (with defaults)
* calculateRetailHourly(0.144) // Returns 0.1742 (with defaults)
*/
export function calculateRetailHourly(wholesale: number): number {
return Math.round(wholesale * USD_RETAIL_DEFAULTS.TOTAL_MULTIPLIER * 10000) / 10000;
}
/**
* Calculate USD retail monthly price from wholesale price
* Applies margin and VAT
*
* @param wholesale - Wholesale monthly price in USD
* @returns Retail price in USD, rounded to nearest $1
*
* @example
* calculateRetailMonthly(5) // Returns 6 (with defaults)
* calculateRetailMonthly(96) // Returns 116 (with defaults)
*/
export function calculateRetailMonthly(wholesale: number): number {
return Math.round(wholesale * USD_RETAIL_DEFAULTS.TOTAL_MULTIPLIER);
}