Initial commit: Cloud Instances API

Multi-cloud VM instance database with Cloudflare Workers
- Linode, Vultr, AWS connector integration
- D1 database with regions, instances, pricing
- Query API with filtering, caching, pagination
- Cron-based auto-sync (daily + 6-hourly)
- Health monitoring endpoint

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
kappa
2026-01-21 20:17:07 +09:00
commit 95043049b4
32 changed files with 10151 additions and 0 deletions

409
src/types.ts Normal file
View File

@@ -0,0 +1,409 @@
/**
* Vault Credentials Types
*/
export interface VaultCredentials {
provider: string;
api_token: string;
}
/**
* Vault API Response Structure
*/
export interface VaultSecretResponse {
data: {
data: {
provider: string;
api_token: string;
};
metadata: {
created_time: string;
custom_metadata: null;
deletion_time: string;
destroyed: boolean;
version: number;
};
};
}
/**
* Cache Entry Structure
*/
export interface CacheEntry<T> {
data: T;
expiresAt: number;
}
// ============================================================
// Database Entity Types
// ============================================================
export interface Provider {
id: number;
name: string;
display_name: string;
api_base_url: string | null;
last_sync_at: string | null; // ISO 8601 datetime
sync_status: 'pending' | 'syncing' | 'success' | 'error';
sync_error: string | null;
created_at: string;
updated_at: string;
}
export interface Region {
id: number;
provider_id: number;
region_code: string;
region_name: string;
country_code: string | null; // ISO 3166-1 alpha-2
latitude: number | null;
longitude: number | null;
available: number; // SQLite boolean (0/1)
created_at: string;
updated_at: string;
}
export type InstanceFamily = 'general' | 'compute' | 'memory' | 'storage' | 'gpu';
export interface InstanceType {
id: number;
provider_id: number;
instance_id: string;
instance_name: string;
vcpu: number;
memory_mb: number;
storage_gb: number;
transfer_tb: number | null;
network_speed_gbps: number | null;
gpu_count: number;
gpu_type: string | null;
instance_family: InstanceFamily | null;
metadata: string | null; // JSON string
created_at: string;
updated_at: string;
}
export interface Pricing {
id: number;
instance_type_id: number;
region_id: number;
hourly_price: number;
monthly_price: number;
currency: string;
available: number; // SQLite boolean (0/1)
created_at: string;
updated_at: string;
}
export interface PriceHistory {
id: number;
pricing_id: number;
hourly_price: number;
monthly_price: number;
recorded_at: string;
}
// ============================================================
// Repository Input Types (for create/update operations)
// ============================================================
export type ProviderInput = Omit<Provider, 'id' | 'created_at' | 'updated_at'>;
export type RegionInput = Omit<Region, 'id' | 'created_at' | 'updated_at'>;
export type InstanceTypeInput = Omit<InstanceType, 'id' | 'created_at' | 'updated_at'>;
export type PricingInput = Omit<Pricing, 'id' | 'created_at' | 'updated_at'>;
// ============================================================
// Error Types
// ============================================================
export class RepositoryError extends Error {
constructor(
message: string,
public readonly code: string,
public readonly cause?: unknown
) {
super(message);
this.name = 'RepositoryError';
}
}
// Common error codes
export const ErrorCodes = {
NOT_FOUND: 'NOT_FOUND',
DUPLICATE: 'DUPLICATE',
CONSTRAINT_VIOLATION: 'CONSTRAINT_VIOLATION',
DATABASE_ERROR: 'DATABASE_ERROR',
TRANSACTION_FAILED: 'TRANSACTION_FAILED',
INVALID_INPUT: 'INVALID_INPUT',
} as const;
// ============================================================
// Pagination Types
// ============================================================
export interface PaginationOptions {
limit?: number;
offset?: number;
}
export interface PaginatedResult<T> {
data: T[];
total: number;
limit: number;
offset: number;
hasMore: boolean;
}
// ============================================================
// API Query and Response Types
// ============================================================
/**
* Query parameters for instance search and filtering
*/
export interface InstanceQueryParams {
/** Provider filter (provider ID or name) */
provider?: string;
/** Region code filter */
region_code?: string;
/** Instance family filter */
family?: InstanceFamily;
/** Minimum vCPU count */
min_vcpu?: number;
/** Maximum vCPU count */
max_vcpu?: number;
/** Minimum memory in MB */
min_memory?: number;
/** Maximum memory in MB */
max_memory?: number;
/** Minimum hourly price */
min_price?: number;
/** Maximum hourly price */
max_price?: number;
/** Filter for GPU instances */
has_gpu?: boolean;
/** CPU architecture filter */
architecture?: string;
/** Sort field (e.g., 'hourly_price', 'vcpu', 'memory_mb') */
sort_by?: string;
/** Sort order ('asc' or 'desc') */
sort_order?: 'asc' | 'desc';
/** Page number for pagination (1-indexed) */
page?: number;
/** Number of results per page */
limit?: number;
}
/**
* Combined instance data with pricing and relationships
*/
export interface InstanceData extends InstanceType {
/** Provider information */
provider: Provider;
/** Region information */
region: Region;
/** Current pricing information */
pricing: Pricing;
}
/**
* Paginated API response for instance queries
*/
export interface InstanceResponse {
/** Array of instance data */
data: InstanceData[];
/** Pagination metadata */
pagination: {
/** Current page number (1-indexed) */
current_page: number;
/** Total number of pages */
total_pages: number;
/** Number of results per page */
per_page: number;
/** Total number of results */
total_results: number;
/** Whether there is a next page */
has_next: boolean;
/** Whether there is a previous page */
has_previous: boolean;
};
/** Query execution metadata */
meta: {
/** Query execution time in milliseconds */
query_time_ms: number;
/** Applied filters summary */
filters_applied: Partial<InstanceQueryParams>;
};
}
// ============================================================
// Sync Report Types
// ============================================================
/**
* Synchronization report for a single provider
*/
export interface ProviderSyncResult {
/** Provider identifier */
provider: string;
/** Synchronization success status */
success: boolean;
/** Number of regions synced */
regions_synced: number;
/** Number of instances synced */
instances_synced: number;
/** Number of pricing records synced */
pricing_synced: number;
/** Sync duration in milliseconds */
duration_ms: number;
/** Error message if sync failed */
error?: string;
/** Detailed error information */
error_details?: any;
}
/**
* Complete synchronization report for all providers
*/
export interface SyncReport {
/** Overall sync success status */
success: boolean;
/** Sync start timestamp (ISO 8601) */
started_at: string;
/** Sync completion timestamp (ISO 8601) */
completed_at: string;
/** Total sync duration in milliseconds */
total_duration_ms: number;
/** Results for each provider */
providers: ProviderSyncResult[];
/** Summary statistics */
summary: {
/** Total number of providers synced */
total_providers: number;
/** Number of successful provider syncs */
successful_providers: number;
/** Number of failed provider syncs */
failed_providers: number;
/** Total regions synced across all providers */
total_regions: number;
/** Total instances synced across all providers */
total_instances: number;
/** Total pricing records synced across all providers */
total_pricing: number;
};
}
// ============================================================
// Health Check Types
// ============================================================
/**
* Health check response
*/
export interface HealthResponse {
/** Service health status */
status: 'healthy' | 'degraded' | 'unhealthy';
/** Service version */
version: string;
/** Response timestamp (ISO 8601) */
timestamp: string;
/** Database connection status */
database: {
/** Database connection status */
connected: boolean;
/** Database response time in milliseconds */
latency_ms?: number;
};
/** Uptime in seconds */
uptime_seconds: number;
/** Additional health metrics */
metrics?: {
/** Total number of instances in database */
total_instances?: number;
/** Total number of providers */
total_providers?: number;
/** Last successful sync timestamp (ISO 8601) */
last_sync_at?: string;
};
}
// ============================================================
// Cloudflare Worker Environment Types
// ============================================================
/**
* Cloudflare Worker environment bindings and variables
*/
export interface Env {
/** D1 Database binding */
DB: D1Database;
/** Vault server URL for credentials management */
VAULT_URL: string;
/** Vault authentication token */
VAULT_TOKEN: string;
/** Batch size for synchronization operations */
SYNC_BATCH_SIZE?: string;
/** Cache TTL in seconds */
CACHE_TTL_SECONDS?: string;
}
// ============================================================
// Synchronization Types
// ============================================================
/**
* Synchronization stage enumeration
*/
export enum SyncStage {
/** Initial stage before sync starts */
IDLE = 'idle',
/** Fetching provider credentials from Vault */
FETCH_CREDENTIALS = 'fetch_credentials',
/** Fetching regions from provider API */
FETCH_REGIONS = 'fetch_regions',
/** Fetching instance types from provider API */
FETCH_INSTANCES = 'fetch_instances',
/** Fetching pricing data from provider API */
FETCH_PRICING = 'fetch_pricing',
/** Normalizing and transforming data */
NORMALIZE_DATA = 'normalize_data',
/** Storing data in database */
STORE_DATA = 'store_data',
/** Sync completed successfully */
COMPLETED = 'completed',
/** Sync failed with error */
FAILED = 'failed',
}
/**
* Normalized data structure for batch database operations
*/
export interface NormalizedData {
/** Normalized region data ready for insertion */
regions: RegionInput[];
/** Normalized instance type data ready for insertion */
instances: InstanceTypeInput[];
/** Normalized pricing data ready for insertion */
pricing: PricingInput[];
}
// ============================================================
// Additional Utility Types
// ============================================================
/**
* Generic API error response
*/
export interface ApiError {
/** Error status code */
status: number;
/** Error type identifier */
error: string;
/** Human-readable error message */
message: string;
/** Additional error details */
details?: any;
/** Request timestamp (ISO 8601) */
timestamp: string;
/** Request path that caused the error */
path?: string;
}