- Remove KRW pricing calculations from all pricing tables - Simplify pricing table to store only wholesale USD prices - Simplify anvil_pricing to store only retail USD prices - Remove KRW environment variables (KRW_EXCHANGE_RATE, KRW_MARGIN_RATE) - Remove KRW functions from constants.ts - Update GPU/G8/VPU pricing repositories to match - Add Anvil tables and repositories for branded product support Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
278 lines
7.5 KiB
TypeScript
278 lines
7.5 KiB
TypeScript
/**
|
||
* 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,
|
||
/** 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,
|
||
} 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
|
||
*
|
||
* NOTE: localhost origin is included for development purposes.
|
||
* In production, filter allowed origins based on environment.
|
||
* Example: const allowedOrigins = CORS.ALLOWED_ORIGINS.filter(o => !o.includes('localhost'))
|
||
*/
|
||
export const CORS = {
|
||
/** Default CORS origin */
|
||
DEFAULT_ORIGIN: '*',
|
||
/** Allowed origins for CORS */
|
||
ALLOWED_ORIGINS: [
|
||
'https://anvil.it.com',
|
||
'https://cloud.anvil.it.com',
|
||
'https://hosting.anvil.it.com',
|
||
'http://localhost:3000', // DEVELOPMENT ONLY - exclude in production
|
||
] 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);
|
||
}
|