Files
cloud-server/src/repositories/instances.ts
kappa 95043049b4 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>
2026-01-21 20:17:18 +09:00

239 lines
6.9 KiB
TypeScript

/**
* 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<InstanceType> {
protected tableName = 'instance_types';
/**
* Find all instance types for a specific provider
*/
async findByProvider(providerId: number): Promise<InstanceType[]> {
try {
const result = await this.db
.prepare('SELECT * FROM instance_types WHERE provider_id = ?')
.bind(providerId)
.all<InstanceType>();
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<InstanceType[]> {
try {
const result = await this.db
.prepare('SELECT * FROM instance_types WHERE instance_family = ?')
.bind(family)
.all<InstanceType>();
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<InstanceType | null> {
try {
const result = await this.db
.prepare('SELECT * FROM instance_types WHERE provider_id = ? AND instance_id = ?')
.bind(providerId, instanceId)
.first<InstanceType>();
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<number> {
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<InstanceType[]> {
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<InstanceType>();
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<InstanceType[]> {
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<InstanceType>();
return result.results;
} catch (error) {
console.error('[InstancesRepository] search failed:', error);
throw new RepositoryError(
'Failed to search instance types',
ErrorCodes.DATABASE_ERROR,
error
);
}
}
}