feat: add server lifecycle management and D1 logging

- Add start/stop/reboot endpoints for server power management
- Add D1-based logging system (logs table + db-logger utility)
- Add idempotency_key validation for order deduplication
- Extend VPS provider interface with lifecycle methods

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
kappa
2026-01-30 08:27:34 +09:00
parent f62712af37
commit 6385b5cab6
13 changed files with 767 additions and 7 deletions

View File

@@ -134,9 +134,99 @@ export class LinodeProvider extends VPSProviderBase {
}
}
async startServer(instanceId: string): Promise<{ success: boolean; error?: string }> {
const url = `${this.config.baseUrl}/linode/instances/${instanceId}/boot`;
try {
const response = await this.fetchWithRetry(url, {
method: 'POST',
headers: {
Authorization: `Bearer ${this.config.apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
});
if (!response.ok) {
const error = (await response.json()) as LinodeError;
return {
success: false,
error: error.errors?.[0]?.reason || 'Failed to start instance',
};
}
return { success: true };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Network error',
};
}
}
async stopServer(instanceId: string): Promise<{ success: boolean; error?: string }> {
const url = `${this.config.baseUrl}/linode/instances/${instanceId}/shutdown`;
try {
const response = await this.fetchWithRetry(url, {
method: 'POST',
headers: {
Authorization: `Bearer ${this.config.apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
});
if (!response.ok) {
const error = (await response.json()) as LinodeError;
return {
success: false,
error: error.errors?.[0]?.reason || 'Failed to stop instance',
};
}
return { success: true };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Network error',
};
}
}
async rebootServer(instanceId: string): Promise<{ success: boolean; error?: string }> {
const url = `${this.config.baseUrl}/linode/instances/${instanceId}/reboot`;
try {
const response = await this.fetchWithRetry(url, {
method: 'POST',
headers: {
Authorization: `Bearer ${this.config.apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
});
if (!response.ok) {
const error = (await response.json()) as LinodeError;
return {
success: false,
error: error.errors?.[0]?.reason || 'Failed to reboot instance',
};
}
return { success: true };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Network error',
};
}
}
async getServerStatus(
instanceId: string
): Promise<{ status: string; ipv4?: string; ipv6?: string }> {
): Promise<{ status: string; power_status?: string; ipv4?: string; ipv6?: string }> {
const url = `${this.config.baseUrl}/linode/instances/${instanceId}`;
try {
@@ -152,8 +242,10 @@ export class LinodeProvider extends VPSProviderBase {
}
const data = (await response.json()) as LinodeInstance;
// Linode: status is the power state (running, offline, etc.)
return {
status: data.status,
power_status: data.status, // For compatibility with Vultr logic
ipv4: data.ipv4?.[0],
ipv6: data.ipv6?.split('/')[0],
};