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:
170
src/repositories/regions.ts
Normal file
170
src/repositories/regions.ts
Normal file
@@ -0,0 +1,170 @@
|
||||
/**
|
||||
* Regions Repository
|
||||
* Handles CRUD operations for provider regions
|
||||
*/
|
||||
|
||||
import { BaseRepository } from './base';
|
||||
import { Region, RegionInput, RepositoryError, ErrorCodes } from '../types';
|
||||
|
||||
export class RegionsRepository extends BaseRepository<Region> {
|
||||
protected tableName = 'regions';
|
||||
|
||||
/**
|
||||
* Find all regions for a specific provider
|
||||
*/
|
||||
async findByProvider(providerId: number): Promise<Region[]> {
|
||||
try {
|
||||
const result = await this.db
|
||||
.prepare('SELECT * FROM regions WHERE provider_id = ?')
|
||||
.bind(providerId)
|
||||
.all<Region>();
|
||||
|
||||
return result.results;
|
||||
} catch (error) {
|
||||
console.error('[RegionsRepository] findByProvider failed:', error);
|
||||
throw new RepositoryError(
|
||||
`Failed to find regions for provider: ${providerId}`,
|
||||
ErrorCodes.DATABASE_ERROR,
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a region by provider ID and region code
|
||||
*/
|
||||
async findByCode(providerId: number, code: string): Promise<Region | null> {
|
||||
try {
|
||||
const result = await this.db
|
||||
.prepare('SELECT * FROM regions WHERE provider_id = ? AND region_code = ?')
|
||||
.bind(providerId, code)
|
||||
.first<Region>();
|
||||
|
||||
return result || null;
|
||||
} catch (error) {
|
||||
console.error('[RegionsRepository] findByCode failed:', error);
|
||||
throw new RepositoryError(
|
||||
`Failed to find region by code: ${code}`,
|
||||
ErrorCodes.DATABASE_ERROR,
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk upsert regions for a provider
|
||||
* Uses batch operations for efficiency
|
||||
*/
|
||||
async upsertMany(providerId: number, regions: RegionInput[]): Promise<number> {
|
||||
if (regions.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
// Build upsert statements for each region
|
||||
const statements = regions.map((region) => {
|
||||
return this.db.prepare(
|
||||
`INSERT INTO regions (
|
||||
provider_id, region_code, region_name, country_code,
|
||||
latitude, longitude, available
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(provider_id, region_code)
|
||||
DO UPDATE SET
|
||||
region_name = excluded.region_name,
|
||||
country_code = excluded.country_code,
|
||||
latitude = excluded.latitude,
|
||||
longitude = excluded.longitude,
|
||||
available = excluded.available`
|
||||
).bind(
|
||||
providerId,
|
||||
region.region_code,
|
||||
region.region_name,
|
||||
region.country_code || null,
|
||||
region.latitude || null,
|
||||
region.longitude || null,
|
||||
region.available
|
||||
);
|
||||
});
|
||||
|
||||
const results = await this.executeBatch(statements);
|
||||
|
||||
// Count successful operations
|
||||
const successCount = results.reduce(
|
||||
(sum, result) => sum + (result.meta.changes ?? 0),
|
||||
0
|
||||
);
|
||||
|
||||
console.log(`[RegionsRepository] Upserted ${successCount} regions for provider ${providerId}`);
|
||||
return successCount;
|
||||
} catch (error) {
|
||||
console.error('[RegionsRepository] upsertMany failed:', error);
|
||||
throw new RepositoryError(
|
||||
`Failed to upsert regions for provider: ${providerId}`,
|
||||
ErrorCodes.TRANSACTION_FAILED,
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available regions only
|
||||
*/
|
||||
async findAvailable(providerId?: number): Promise<Region[]> {
|
||||
try {
|
||||
let query = 'SELECT * FROM regions WHERE available = 1';
|
||||
const params: any[] = [];
|
||||
|
||||
if (providerId !== undefined) {
|
||||
query += ' AND provider_id = ?';
|
||||
params.push(providerId);
|
||||
}
|
||||
|
||||
const result = await this.db
|
||||
.prepare(query)
|
||||
.bind(...params)
|
||||
.all<Region>();
|
||||
|
||||
return result.results;
|
||||
} catch (error) {
|
||||
console.error('[RegionsRepository] findAvailable failed:', error);
|
||||
throw new RepositoryError(
|
||||
'Failed to find available regions',
|
||||
ErrorCodes.DATABASE_ERROR,
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update region availability status
|
||||
*/
|
||||
async updateAvailability(id: number, available: boolean): Promise<Region> {
|
||||
try {
|
||||
const result = await this.db
|
||||
.prepare('UPDATE regions SET available = ? WHERE id = ? RETURNING *')
|
||||
.bind(available ? 1 : 0, id)
|
||||
.first<Region>();
|
||||
|
||||
if (!result) {
|
||||
throw new RepositoryError(
|
||||
`Region not found: ${id}`,
|
||||
ErrorCodes.NOT_FOUND
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('[RegionsRepository] updateAvailability failed:', error);
|
||||
|
||||
if (error instanceof RepositoryError) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw new RepositoryError(
|
||||
`Failed to update region availability: ${id}`,
|
||||
ErrorCodes.DATABASE_ERROR,
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user