/** * Instance Types Repository * Handles CRUD operations for VM instance types */ import { BaseRepository } from './base'; import { InstanceType, InstanceTypeInput, InstanceFamily, RepositoryError, ErrorCodes } from '../types'; export class InstancesRepository extends BaseRepository { protected tableName = 'instance_types'; /** * Find all instance types for a specific provider */ async findByProvider(providerId: number): Promise { try { const result = await this.db .prepare('SELECT * FROM instance_types WHERE provider_id = ?') .bind(providerId) .all(); return result.results; } catch (error) { console.error('[InstancesRepository] findByProvider failed:', error); throw new RepositoryError( `Failed to find instance types for provider: ${providerId}`, ErrorCodes.DATABASE_ERROR, error ); } } /** * Find instance types by family */ async findByFamily(family: InstanceFamily): Promise { try { const result = await this.db .prepare('SELECT * FROM instance_types WHERE instance_family = ?') .bind(family) .all(); return result.results; } catch (error) { console.error('[InstancesRepository] findByFamily failed:', error); throw new RepositoryError( `Failed to find instance types by family: ${family}`, ErrorCodes.DATABASE_ERROR, error ); } } /** * Find an instance type by provider ID and instance ID */ async findByInstanceId(providerId: number, instanceId: string): Promise { try { const result = await this.db .prepare('SELECT * FROM instance_types WHERE provider_id = ? AND instance_id = ?') .bind(providerId, instanceId) .first(); return result || null; } catch (error) { console.error('[InstancesRepository] findByInstanceId failed:', error); throw new RepositoryError( `Failed to find instance type: ${instanceId}`, ErrorCodes.DATABASE_ERROR, error ); } } /** * Bulk upsert instance types for a provider * Uses batch operations for efficiency */ async upsertMany(providerId: number, instances: InstanceTypeInput[]): Promise { if (instances.length === 0) { return 0; } try { // Build upsert statements for each instance type const statements = instances.map((instance) => { return this.db.prepare( `INSERT INTO instance_types ( provider_id, instance_id, instance_name, vcpu, memory_mb, storage_gb, transfer_tb, network_speed_gbps, gpu_count, gpu_type, instance_family, metadata ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(provider_id, instance_id) DO UPDATE SET instance_name = excluded.instance_name, vcpu = excluded.vcpu, memory_mb = excluded.memory_mb, storage_gb = excluded.storage_gb, transfer_tb = excluded.transfer_tb, network_speed_gbps = excluded.network_speed_gbps, gpu_count = excluded.gpu_count, gpu_type = excluded.gpu_type, instance_family = excluded.instance_family, metadata = excluded.metadata` ).bind( providerId, instance.instance_id, instance.instance_name, instance.vcpu, instance.memory_mb, instance.storage_gb, instance.transfer_tb || null, instance.network_speed_gbps || null, instance.gpu_count, instance.gpu_type || null, instance.instance_family || null, instance.metadata || null ); }); const results = await this.executeBatch(statements); // Count successful operations const successCount = results.reduce( (sum, result) => sum + (result.meta.changes ?? 0), 0 ); console.log(`[InstancesRepository] Upserted ${successCount} instance types for provider ${providerId}`); return successCount; } catch (error) { console.error('[InstancesRepository] upsertMany failed:', error); throw new RepositoryError( `Failed to upsert instance types for provider: ${providerId}`, ErrorCodes.TRANSACTION_FAILED, error ); } } /** * Find GPU instances only */ async findGpuInstances(providerId?: number): Promise { try { let query = 'SELECT * FROM instance_types WHERE gpu_count > 0'; const params: any[] = []; if (providerId !== undefined) { query += ' AND provider_id = ?'; params.push(providerId); } const result = await this.db .prepare(query) .bind(...params) .all(); return result.results; } catch (error) { console.error('[InstancesRepository] findGpuInstances failed:', error); throw new RepositoryError( 'Failed to find GPU instances', ErrorCodes.DATABASE_ERROR, error ); } } /** * Search instances by specifications */ async search(criteria: { providerId?: number; minVcpu?: number; maxVcpu?: number; minMemoryMb?: number; maxMemoryMb?: number; family?: InstanceFamily; hasGpu?: boolean; }): Promise { try { const conditions: string[] = []; const params: any[] = []; if (criteria.providerId !== undefined) { conditions.push('provider_id = ?'); params.push(criteria.providerId); } if (criteria.minVcpu !== undefined) { conditions.push('vcpu >= ?'); params.push(criteria.minVcpu); } if (criteria.maxVcpu !== undefined) { conditions.push('vcpu <= ?'); params.push(criteria.maxVcpu); } if (criteria.minMemoryMb !== undefined) { conditions.push('memory_mb >= ?'); params.push(criteria.minMemoryMb); } if (criteria.maxMemoryMb !== undefined) { conditions.push('memory_mb <= ?'); params.push(criteria.maxMemoryMb); } if (criteria.family !== undefined) { conditions.push('instance_family = ?'); params.push(criteria.family); } if (criteria.hasGpu !== undefined) { conditions.push(criteria.hasGpu ? 'gpu_count > 0' : 'gpu_count = 0'); } const whereClause = conditions.length > 0 ? ' WHERE ' + conditions.join(' AND ') : ''; const query = 'SELECT * FROM instance_types' + whereClause; const result = await this.db .prepare(query) .bind(...params) .all(); return result.results; } catch (error) { console.error('[InstancesRepository] search failed:', error); throw new RepositoryError( 'Failed to search instance types', ErrorCodes.DATABASE_ERROR, error ); } } }