From 7d9edc14a31d9b6f7c3677b0f0882a4a3c95f70d Mon Sep 17 00:00:00 2001 From: kappa Date: Wed, 28 Jan 2026 10:21:27 +0900 Subject: [PATCH] feat: add configurable VPS provider API URLs for emulator testing - Add LINODE_API_URL and VULTR_API_URL environment variables - Update LinodeProvider and VultrProvider to accept optional baseUrl - Update ProvisioningService to pass API URLs to providers - Add source_provider and source_region_code to PricingWithProvider type - Use source_provider (linode/vultr) instead of provider_name (Anvil) - Improve error handling for non-JSON responses in LinodeProvider Co-Authored-By: Claude Opus 4.5 --- src/handlers/provision.ts | 4 ++- src/handlers/queue.ts | 4 ++- src/services/linode-provider.ts | 38 ++++++++++++++++++++-------- src/services/provisioning-service.ts | 8 +++--- src/services/vultr-provider.ts | 6 +++-- src/types.ts | 3 +++ wrangler.toml | 6 +++++ 7 files changed, 51 insertions(+), 18 deletions(-) diff --git a/src/handlers/provision.ts b/src/handlers/provision.ts index f58df3e..5b3c0bc 100644 --- a/src/handlers/provision.ts +++ b/src/handlers/provision.ts @@ -114,7 +114,9 @@ export async function handleProvision( env.DB, // cloud-instances-db env.USER_DB, // telegram-conversations env.LINODE_API_KEY, - env.VULTR_API_KEY + env.VULTR_API_KEY, + env.LINODE_API_URL, + env.VULTR_API_URL ); // Provision server diff --git a/src/handlers/queue.ts b/src/handlers/queue.ts index ba4faac..bc91b32 100644 --- a/src/handlers/queue.ts +++ b/src/handlers/queue.ts @@ -19,7 +19,9 @@ export async function handleProvisionQueue( env.DB, env.USER_DB, env.LINODE_API_KEY, - env.VULTR_API_KEY + env.VULTR_API_KEY, + env.LINODE_API_URL, + env.VULTR_API_URL ); for (const message of batch.messages) { diff --git a/src/services/linode-provider.ts b/src/services/linode-provider.ts index f2bb6d5..7fd1868 100644 --- a/src/services/linode-provider.ts +++ b/src/services/linode-provider.ts @@ -26,10 +26,12 @@ interface LinodeError { } export class LinodeProvider extends VPSProviderBase { - constructor(apiKey: string, timeout: number = 30000) { + static readonly DEFAULT_BASE_URL = 'https://api.linode.com/v4'; + + constructor(apiKey: string, baseUrl?: string, timeout: number = 30000) { super({ apiKey, - baseUrl: 'https://api.linode.com/v4', + baseUrl: baseUrl || LinodeProvider.DEFAULT_BASE_URL, timeout, }); } @@ -58,18 +60,32 @@ export class LinodeProvider extends VPSProviderBase { body: JSON.stringify(body), }); + // Read response text first to handle both JSON and HTML errors + const responseText = await response.text(); + if (!response.ok) { - const error = (await response.json()) as LinodeError; - return { - success: false, - error: { - code: `LINODE_${response.status}`, - message: error.errors?.[0]?.reason || 'Unknown error', - }, - }; + // Try to parse as JSON, but handle HTML responses gracefully + try { + const error = JSON.parse(responseText) as LinodeError; + return { + success: false, + error: { + code: `LINODE_${response.status}`, + message: error.errors?.[0]?.reason || 'Unknown error', + }, + }; + } catch { + return { + success: false, + error: { + code: `LINODE_${response.status}`, + message: `Non-JSON response: ${responseText.substring(0, 200)}`, + }, + }; + } } - const data = (await response.json()) as LinodeInstance; + const data = JSON.parse(responseText) as LinodeInstance; return { success: true, diff --git a/src/services/provisioning-service.ts b/src/services/provisioning-service.ts index 1a66629..342c4e7 100644 --- a/src/services/provisioning-service.ts +++ b/src/services/provisioning-service.ts @@ -20,12 +20,14 @@ export class ProvisioningService { db: D1Database, // cloud-instances-db: pricing, providers userDb: D1Database, // telegram-conversations: users, deposits, orders linodeApiKey?: string, - vultrApiKey?: string + vultrApiKey?: string, + linodeApiUrl?: string, // Optional: for testing with emulator + vultrApiUrl?: string // Optional: for testing with emulator ) { this.env = env; this.repo = new ProvisioningRepository(db, userDb); - this.linodeProvider = linodeApiKey ? new LinodeProvider(linodeApiKey) : null; - this.vultrProvider = vultrApiKey ? new VultrProvider(vultrApiKey) : null; + this.linodeProvider = linodeApiKey ? new LinodeProvider(linodeApiKey, linodeApiUrl) : null; + this.vultrProvider = vultrApiKey ? new VultrProvider(vultrApiKey, vultrApiUrl) : null; } /** diff --git a/src/services/vultr-provider.ts b/src/services/vultr-provider.ts index 608fbcf..e31e64b 100644 --- a/src/services/vultr-provider.ts +++ b/src/services/vultr-provider.ts @@ -36,10 +36,12 @@ interface VultrError { } export class VultrProvider extends VPSProviderBase { - constructor(apiKey: string, timeout: number = 30000) { + static readonly DEFAULT_BASE_URL = 'https://api.vultr.com/v2'; + + constructor(apiKey: string, baseUrl?: string, timeout: number = 30000) { super({ apiKey, - baseUrl: 'https://api.vultr.com/v2', + baseUrl: baseUrl || VultrProvider.DEFAULT_BASE_URL, timeout, }); } diff --git a/src/types.ts b/src/types.ts index 80ded0b..e880d43 100644 --- a/src/types.ts +++ b/src/types.ts @@ -12,6 +12,9 @@ export interface Env { // VPS Provider API Keys LINODE_API_KEY?: string; VULTR_API_KEY?: string; + // VPS Provider API URLs (for testing with emulators) + LINODE_API_URL?: string; // Default: https://api.linode.com/v4 + VULTR_API_URL?: string; // Default: https://api.vultr.com/v2 // Provision API security PROVISION_API_KEY?: string; // Required for /api/provision/* endpoints // Queue for async provisioning diff --git a/wrangler.toml b/wrangler.toml index de99d4c..6531b1a 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -39,3 +39,9 @@ queue = "provision-queue" max_batch_size = 1 max_retries = 3 dead_letter_queue = "provision-queue-dlq" + +# VPS Provider API URLs (for testing with emulators) +# Comment out or remove for production to use default URLs +[vars] +LINODE_API_URL = "https://linode.actions.it.com/v4" +VULTR_API_URL = "https://vultr.actions.it.com/v2"