/** * 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 // ============================================================ /** * Mapping of user-facing sort field names to database column names * * This is the single source of truth for sort field validation and mapping. * Query aliases: it=instance_types, pr=pricing, p=providers, r=regions */ export const SORT_FIELD_MAP: Record = { price: 'pr.hourly_price', hourly_price: 'pr.hourly_price', monthly_price: 'pr.monthly_price', vcpu: 'it.vcpu', memory: 'it.memory_mb', memory_mb: 'it.memory_mb', memory_gb: 'it.memory_mb', // Note: memory_gb is converted to memory_mb at query level storage_gb: 'it.storage_gb', name: 'it.instance_name', instance_name: 'it.instance_name', provider: 'p.name', region: 'r.region_code', } as const; /** * Valid sort fields for instance queries (derived from SORT_FIELD_MAP) */ export const VALID_SORT_FIELDS = Object.keys(SORT_FIELD_MAP) as ReadonlyArray; export type ValidSortField = keyof typeof SORT_FIELD_MAP; /** * 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); }