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 <noreply@anthropic.com>
This commit is contained in:
kappa
2026-01-28 10:21:27 +09:00
parent 006b938ad2
commit 7d9edc14a3
7 changed files with 51 additions and 18 deletions

View File

@@ -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

View File

@@ -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) {

View File

@@ -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,

View File

@@ -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;
}
/**

View File

@@ -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,
});
}

View File

@@ -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

View File

@@ -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"