diff --git a/src/services/sync.ts b/src/services/sync.ts index 1941ff8..ce5e49c 100644 --- a/src/services/sync.ts +++ b/src/services/sync.ts @@ -377,6 +377,21 @@ export class SyncOrchestrator { }); } + // Stage 8.5: Sync Anvil Transfer Pricing + let anvilTransferPricingCount = 0; + try { + anvilTransferPricingCount = await this.syncAnvilTransferPricing(provider); + if (anvilTransferPricingCount > 0) { + this.logger.info(`${provider} → SYNC_ANVIL_TRANSFER_PRICING`, { anvil_transfer_pricing: anvilTransferPricingCount }); + } + } catch (transferError) { + // Log error but don't fail the entire sync + this.logger.error('Anvil transfer pricing sync failed', { + provider, + error: transferError instanceof Error ? transferError.message : String(transferError) + }); + } + // Stage 9: Complete - Update provider status to success stage = SyncStage.COMPLETE; await this.repos.providers.updateSyncStatus(provider, 'success'); @@ -1121,6 +1136,84 @@ export class SyncOrchestrator { } } + /** + * Synchronize Anvil transfer pricing based on source provider + * + * Updates anvil_transfer_pricing table with retail transfer costs + * Formula: retail = cost × 1.21 (21% margin) + * + * Provider costs (per GB): + * - Linode: $0.005/GB + * - Vultr: $0.01/GB + * - AWS: $0.09/GB (Asia regions) + * + * @param provider - Source provider name (linode, vultr, aws) + * @returns Number of anvil_transfer_pricing records updated + */ + private async syncAnvilTransferPricing(provider: string): Promise { + this.logger.info('Starting Anvil transfer pricing sync', { provider }); + + try { + // Step 1: Define provider costs per GB (wholesale) + const providerCosts: Record = { + linode: 0.005, // $0.005/GB + vultr: 0.01, // $0.01/GB + aws: 0.09, // $0.09/GB (Asia regions) + }; + + const costPerGb = providerCosts[provider.toLowerCase()]; + if (!costPerGb) { + this.logger.info('No transfer pricing defined for provider', { provider }); + return 0; + } + + // Step 2: Find all anvil_regions sourced from this provider + const anvilRegionsResult = await this.repos.db + .prepare('SELECT id, source_region_id FROM anvil_regions WHERE source_provider = ?') + .bind(provider) + .all<{ id: number; source_region_id: number }>(); + + if (!anvilRegionsResult.success || anvilRegionsResult.results.length === 0) { + this.logger.info('No anvil_regions found for provider', { provider }); + return 0; + } + + const anvilRegions = anvilRegionsResult.results; + this.logger.info('Found anvil_regions for transfer pricing', { + provider, + count: anvilRegions.length + }); + + // Step 3: Calculate retail price (cost × 1.21 for 21% margin) + const retailPricePerGb = costPerGb * 1.21; + + // Step 4: Prepare upsert data for all regions + const transferPricingData = anvilRegions.map(region => ({ + anvil_region_id: region.id, + price_per_gb: retailPricePerGb, + })); + + // Step 5: Batch upsert using repository + const upsertCount = await this.repos.anvilTransferPricing.upsertMany(transferPricingData); + + this.logger.info('Anvil transfer pricing sync completed', { + provider, + cost_per_gb: costPerGb, + retail_price_per_gb: retailPricePerGb, + regions_updated: upsertCount, + }); + + return upsertCount; + + } catch (error) { + this.logger.error('Anvil transfer pricing sync failed', { + provider, + error: error instanceof Error ? error.message : String(error) + }); + throw error; + } + } + /** * Create connector for a specific provider *