- Remove KRW pricing calculations from all pricing tables - Simplify pricing table to store only wholesale USD prices - Simplify anvil_pricing to store only retail USD prices - Remove KRW environment variables (KRW_EXCHANGE_RATE, KRW_MARGIN_RATE) - Remove KRW functions from constants.ts - Update GPU/G8/VPU pricing repositories to match - Add Anvil tables and repositories for branded product support Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
211 lines
5.7 KiB
TypeScript
211 lines
5.7 KiB
TypeScript
/**
|
|
* Anvil Regions Repository
|
|
* Handles CRUD operations for Anvil-branded regional datacenters
|
|
*/
|
|
|
|
import { BaseRepository } from './base';
|
|
import { AnvilRegion, AnvilRegionInput, RepositoryError, ErrorCodes } from '../types';
|
|
import { createLogger } from '../utils/logger';
|
|
|
|
export class AnvilRegionsRepository extends BaseRepository<AnvilRegion> {
|
|
protected tableName = 'anvil_regions';
|
|
protected logger = createLogger('[AnvilRegionsRepository]');
|
|
protected allowedColumns = [
|
|
'name',
|
|
'display_name',
|
|
'country_code',
|
|
'source_provider',
|
|
'source_region_code',
|
|
'source_region_id',
|
|
'active',
|
|
];
|
|
|
|
/**
|
|
* Find a region by name (e.g., "anvil-tyo1")
|
|
*/
|
|
async findByName(name: string): Promise<AnvilRegion | null> {
|
|
try {
|
|
const result = await this.db
|
|
.prepare('SELECT * FROM anvil_regions WHERE name = ?')
|
|
.bind(name)
|
|
.first<AnvilRegion>();
|
|
|
|
return result || null;
|
|
} catch (error) {
|
|
this.logger.error('findByName failed', {
|
|
name,
|
|
error: error instanceof Error ? error.message : 'Unknown error'
|
|
});
|
|
throw new RepositoryError(
|
|
`Failed to find Anvil region by name: ${name}`,
|
|
ErrorCodes.DATABASE_ERROR,
|
|
error
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Find all regions for a country
|
|
*/
|
|
async findByCountry(countryCode: string): Promise<AnvilRegion[]> {
|
|
try {
|
|
const result = await this.db
|
|
.prepare('SELECT * FROM anvil_regions WHERE country_code = ? ORDER BY name')
|
|
.bind(countryCode)
|
|
.all<AnvilRegion>();
|
|
|
|
return result.results;
|
|
} catch (error) {
|
|
this.logger.error('findByCountry failed', {
|
|
countryCode,
|
|
error: error instanceof Error ? error.message : 'Unknown error'
|
|
});
|
|
throw new RepositoryError(
|
|
`Failed to find Anvil regions for country: ${countryCode}`,
|
|
ErrorCodes.DATABASE_ERROR,
|
|
error
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Find region by source region ID
|
|
*/
|
|
async findBySourceRegion(sourceRegionId: number): Promise<AnvilRegion | null> {
|
|
try {
|
|
const result = await this.db
|
|
.prepare('SELECT * FROM anvil_regions WHERE source_region_id = ?')
|
|
.bind(sourceRegionId)
|
|
.first<AnvilRegion>();
|
|
|
|
return result || null;
|
|
} catch (error) {
|
|
this.logger.error('findBySourceRegion failed', {
|
|
sourceRegionId,
|
|
error: error instanceof Error ? error.message : 'Unknown error'
|
|
});
|
|
throw new RepositoryError(
|
|
`Failed to find Anvil region by source region: ${sourceRegionId}`,
|
|
ErrorCodes.DATABASE_ERROR,
|
|
error
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get all active regions
|
|
*/
|
|
async findActive(): Promise<AnvilRegion[]> {
|
|
try {
|
|
const result = await this.db
|
|
.prepare('SELECT * FROM anvil_regions WHERE active = 1 ORDER BY name')
|
|
.all<AnvilRegion>();
|
|
|
|
return result.results;
|
|
} catch (error) {
|
|
this.logger.error('findActive failed', {
|
|
error: error instanceof Error ? error.message : 'Unknown error'
|
|
});
|
|
throw new RepositoryError(
|
|
'Failed to find active Anvil regions',
|
|
ErrorCodes.DATABASE_ERROR,
|
|
error
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update region active status
|
|
*/
|
|
async updateActive(id: number, active: boolean): Promise<AnvilRegion> {
|
|
try {
|
|
const result = await this.db
|
|
.prepare('UPDATE anvil_regions SET active = ? WHERE id = ? RETURNING *')
|
|
.bind(active ? 1 : 0, id)
|
|
.first<AnvilRegion>();
|
|
|
|
if (!result) {
|
|
throw new RepositoryError(
|
|
`Anvil region not found: ${id}`,
|
|
ErrorCodes.NOT_FOUND
|
|
);
|
|
}
|
|
|
|
return result;
|
|
} catch (error) {
|
|
this.logger.error('updateActive failed', {
|
|
id,
|
|
active,
|
|
error: error instanceof Error ? error.message : 'Unknown error'
|
|
});
|
|
|
|
if (error instanceof RepositoryError) {
|
|
throw error;
|
|
}
|
|
|
|
throw new RepositoryError(
|
|
`Failed to update Anvil region active status: ${id}`,
|
|
ErrorCodes.DATABASE_ERROR,
|
|
error
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Bulk upsert regions
|
|
* Uses batch operations for efficiency
|
|
*/
|
|
async upsertMany(regions: AnvilRegionInput[]): Promise<number> {
|
|
if (regions.length === 0) {
|
|
return 0;
|
|
}
|
|
|
|
try {
|
|
const statements = regions.map((region) => {
|
|
return this.db.prepare(
|
|
`INSERT INTO anvil_regions (
|
|
name, display_name, country_code, source_provider,
|
|
source_region_code, source_region_id, active
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
ON CONFLICT(name)
|
|
DO UPDATE SET
|
|
display_name = excluded.display_name,
|
|
country_code = excluded.country_code,
|
|
source_provider = excluded.source_provider,
|
|
source_region_code = excluded.source_region_code,
|
|
source_region_id = excluded.source_region_id,
|
|
active = excluded.active`
|
|
).bind(
|
|
region.name,
|
|
region.display_name,
|
|
region.country_code,
|
|
region.source_provider,
|
|
region.source_region_code,
|
|
region.source_region_id,
|
|
region.active
|
|
);
|
|
});
|
|
|
|
const results = await this.executeBatch(statements);
|
|
|
|
const successCount = results.reduce(
|
|
(sum, result) => sum + (result.meta.changes ?? 0),
|
|
0
|
|
);
|
|
|
|
this.logger.info('Upserted Anvil regions', { count: successCount });
|
|
return successCount;
|
|
} catch (error) {
|
|
this.logger.error('upsertMany failed', {
|
|
count: regions.length,
|
|
error: error instanceof Error ? error.message : 'Unknown error'
|
|
});
|
|
throw new RepositoryError(
|
|
'Failed to upsert Anvil regions',
|
|
ErrorCodes.TRANSACTION_FAILED,
|
|
error
|
|
);
|
|
}
|
|
}
|
|
}
|