@ebowwa/hetzner 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/actions.js +802 -0
- package/actions.ts +1053 -0
- package/auth.js +35 -0
- package/auth.ts +37 -0
- package/bootstrap/FIREWALL.md +326 -0
- package/bootstrap/KERNEL-HARDENING.md +258 -0
- package/bootstrap/SECURITY-INTEGRATION.md +281 -0
- package/bootstrap/TESTING.md +301 -0
- package/bootstrap/cloud-init.js +279 -0
- package/bootstrap/cloud-init.ts +394 -0
- package/bootstrap/firewall.js +279 -0
- package/bootstrap/firewall.ts +342 -0
- package/bootstrap/genesis.js +406 -0
- package/bootstrap/genesis.ts +518 -0
- package/bootstrap/index.js +35 -0
- package/bootstrap/index.ts +71 -0
- package/bootstrap/kernel-hardening.js +266 -0
- package/bootstrap/kernel-hardening.test.ts +230 -0
- package/bootstrap/kernel-hardening.ts +272 -0
- package/bootstrap/security-audit.js +118 -0
- package/bootstrap/security-audit.ts +124 -0
- package/bootstrap/ssh-hardening.js +182 -0
- package/bootstrap/ssh-hardening.ts +192 -0
- package/client.js +137 -0
- package/client.ts +177 -0
- package/config.js +5 -0
- package/config.ts +5 -0
- package/errors.js +270 -0
- package/errors.ts +371 -0
- package/index.js +28 -0
- package/index.ts +55 -0
- package/package.json +56 -0
- package/pricing.js +284 -0
- package/pricing.ts +422 -0
- package/schemas.js +660 -0
- package/schemas.ts +765 -0
- package/server-status.ts +81 -0
- package/servers.js +424 -0
- package/servers.ts +568 -0
- package/ssh-keys.js +90 -0
- package/ssh-keys.ts +122 -0
- package/ssh-setup.ts +218 -0
- package/types.js +96 -0
- package/types.ts +389 -0
- package/volumes.js +172 -0
- package/volumes.ts +229 -0
package/types.ts
ADDED
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hetzner Cloud API types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// Import shared status enums
|
|
7
|
+
// ============================================================================
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
EnvironmentStatus,
|
|
11
|
+
ActionStatus,
|
|
12
|
+
VolumeStatus,
|
|
13
|
+
} from "@ebowwa/codespaces-types/compile";
|
|
14
|
+
|
|
15
|
+
// Re-export for convenience
|
|
16
|
+
export { EnvironmentStatus, ActionStatus, VolumeStatus };
|
|
17
|
+
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// Server Types
|
|
20
|
+
// ============================================================================
|
|
21
|
+
|
|
22
|
+
export interface HetznerServer {
|
|
23
|
+
id: number;
|
|
24
|
+
name: string;
|
|
25
|
+
status: EnvironmentStatus;
|
|
26
|
+
image?: {
|
|
27
|
+
id: number;
|
|
28
|
+
name: string;
|
|
29
|
+
description: string;
|
|
30
|
+
type: "snapshot" | "backup" | "system";
|
|
31
|
+
} | null;
|
|
32
|
+
public_net: {
|
|
33
|
+
ipv4: {
|
|
34
|
+
ip: string;
|
|
35
|
+
blocked: boolean;
|
|
36
|
+
};
|
|
37
|
+
ipv6?: {
|
|
38
|
+
ip: string;
|
|
39
|
+
blocked: boolean;
|
|
40
|
+
};
|
|
41
|
+
floating_ips: Array<{
|
|
42
|
+
id: number;
|
|
43
|
+
ip: string;
|
|
44
|
+
}>;
|
|
45
|
+
firewalls: Array<{
|
|
46
|
+
id: number;
|
|
47
|
+
name: string;
|
|
48
|
+
status: "applied" | "pending";
|
|
49
|
+
}>;
|
|
50
|
+
};
|
|
51
|
+
server_type: {
|
|
52
|
+
id: number;
|
|
53
|
+
name: string;
|
|
54
|
+
description: string;
|
|
55
|
+
cores: number;
|
|
56
|
+
memory: number;
|
|
57
|
+
disk: number;
|
|
58
|
+
};
|
|
59
|
+
datacenter: {
|
|
60
|
+
id: number;
|
|
61
|
+
name: string;
|
|
62
|
+
description: string;
|
|
63
|
+
location: {
|
|
64
|
+
id: number;
|
|
65
|
+
name: string;
|
|
66
|
+
description: string;
|
|
67
|
+
country: string;
|
|
68
|
+
city: string;
|
|
69
|
+
latitude: number;
|
|
70
|
+
longitude: number;
|
|
71
|
+
network_zone: string;
|
|
72
|
+
};
|
|
73
|
+
supported_server_types?: Array<{
|
|
74
|
+
id: number;
|
|
75
|
+
name: string;
|
|
76
|
+
}> | null;
|
|
77
|
+
};
|
|
78
|
+
labels: Record<string, string>;
|
|
79
|
+
created: string;
|
|
80
|
+
protection: {
|
|
81
|
+
delete: boolean;
|
|
82
|
+
rebuild: boolean;
|
|
83
|
+
};
|
|
84
|
+
volumes: Array<{
|
|
85
|
+
id: number;
|
|
86
|
+
name: string;
|
|
87
|
+
size: number;
|
|
88
|
+
linux_device: string;
|
|
89
|
+
}>;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface CreateServerOptions {
|
|
93
|
+
name: string;
|
|
94
|
+
server_type?: string;
|
|
95
|
+
image?: string;
|
|
96
|
+
location?: string;
|
|
97
|
+
datacenter?: string;
|
|
98
|
+
ssh_keys?: Array<string | number>;
|
|
99
|
+
volumes?: number[];
|
|
100
|
+
labels?: Record<string, string>;
|
|
101
|
+
start_after_create?: boolean;
|
|
102
|
+
/** Cloud-init user data script for first-boot provisioning */
|
|
103
|
+
user_data?: string;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface UpdateServerOptions {
|
|
107
|
+
name?: string;
|
|
108
|
+
labels?: Record<string, string>;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ============================================================================
|
|
112
|
+
// Action Types
|
|
113
|
+
// ============================================================================
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Action command types from Hetzner Cloud API
|
|
117
|
+
*/
|
|
118
|
+
export enum ActionCommand {
|
|
119
|
+
// Server actions
|
|
120
|
+
CreateServer = "create_server",
|
|
121
|
+
DeleteServer = "delete_server",
|
|
122
|
+
StartServer = "start_server",
|
|
123
|
+
StopServer = "stop_server",
|
|
124
|
+
RebootServer = "reboot_server",
|
|
125
|
+
ResetServer = "reset_server",
|
|
126
|
+
ShutdownServer = "shutdown_server",
|
|
127
|
+
Poweroff = "poweroff",
|
|
128
|
+
ChangeServerType = "change_server_type",
|
|
129
|
+
RebuildServer = "rebuild_server",
|
|
130
|
+
EnableBackup = "enable_backup",
|
|
131
|
+
DisableBackup = "disable_backup",
|
|
132
|
+
CreateImage = "create_image",
|
|
133
|
+
ChangeDnsPtr = "change_dns_ptr",
|
|
134
|
+
AttachToNetwork = "attach_to_network",
|
|
135
|
+
DetachFromNetwork = "detach_from_network",
|
|
136
|
+
ChangeAliasIps = "change_alias_ips",
|
|
137
|
+
EnableRescue = "enable_rescue",
|
|
138
|
+
DisableRescue = "disable_rescue",
|
|
139
|
+
ChangeProtection = "change_protection",
|
|
140
|
+
|
|
141
|
+
// Volume actions
|
|
142
|
+
CreateVolume = "create_volume",
|
|
143
|
+
DeleteVolume = "delete_volume",
|
|
144
|
+
AttachVolume = "attach_volume",
|
|
145
|
+
DetachVolume = "detach_volume",
|
|
146
|
+
ResizeVolume = "resize_volume",
|
|
147
|
+
VolumeChangeProtection = "volume_change_protection",
|
|
148
|
+
|
|
149
|
+
// Network actions
|
|
150
|
+
AddSubnet = "add_subnet",
|
|
151
|
+
DeleteSubnet = "delete_subnet",
|
|
152
|
+
AddRoute = "add_route",
|
|
153
|
+
DeleteRoute = "delete_route",
|
|
154
|
+
ChangeIpRange = "change_ip_range",
|
|
155
|
+
NetworkChangeProtection = "network_change_protection",
|
|
156
|
+
|
|
157
|
+
// Floating IP actions
|
|
158
|
+
AssignFloatingIp = "assign_floating_ip",
|
|
159
|
+
UnassignFloatingIp = "unassign_floating_ip",
|
|
160
|
+
FloatingIpChangeDnsPtr = "floating_ip_change_dns_ptr",
|
|
161
|
+
FloatingIpChangeProtection = "floating_ip_change_protection",
|
|
162
|
+
|
|
163
|
+
// Load Balancer actions
|
|
164
|
+
CreateLoadBalancer = "create_load_balancer",
|
|
165
|
+
DeleteLoadBalancer = "delete_load_balancer",
|
|
166
|
+
AddTarget = "add_target",
|
|
167
|
+
RemoveTarget = "remove_target",
|
|
168
|
+
AddService = "add_service",
|
|
169
|
+
UpdateService = "update_service",
|
|
170
|
+
DeleteService = "delete_service",
|
|
171
|
+
LoadBalancerAttachToNetwork = "load_balancer_attach_to_network",
|
|
172
|
+
LoadBalancerDetachFromNetwork = "load_balancer_detach_from_network",
|
|
173
|
+
ChangeAlgorithm = "change_algorithm",
|
|
174
|
+
ChangeType = "change_type",
|
|
175
|
+
LoadBalancerChangeProtection = "load_balancer_change_protection",
|
|
176
|
+
|
|
177
|
+
// Certificate actions
|
|
178
|
+
IssueCertificate = "issue_certificate",
|
|
179
|
+
RetryCertificate = "retry_certificate",
|
|
180
|
+
|
|
181
|
+
// Firewall actions
|
|
182
|
+
SetFirewallRules = "set_firewall_rules",
|
|
183
|
+
ApplyFirewall = "apply_firewall",
|
|
184
|
+
RemoveFirewall = "remove_firewall",
|
|
185
|
+
FirewallChangeProtection = "firewall_change_protection",
|
|
186
|
+
|
|
187
|
+
// Image actions
|
|
188
|
+
ImageChangeProtection = "image_change_protection",
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Resource types that can be affected by actions
|
|
193
|
+
*/
|
|
194
|
+
export enum ResourceType {
|
|
195
|
+
Server = "server",
|
|
196
|
+
Volume = "volume",
|
|
197
|
+
Network = "network",
|
|
198
|
+
FloatingIp = "floating_ip",
|
|
199
|
+
LoadBalancer = "load_balancer",
|
|
200
|
+
Certificate = "certificate",
|
|
201
|
+
Firewall = "firewall",
|
|
202
|
+
Image = "image",
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Action resource reference
|
|
207
|
+
*/
|
|
208
|
+
export interface ActionResource {
|
|
209
|
+
id: number;
|
|
210
|
+
type: ResourceType;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Action error details
|
|
215
|
+
*/
|
|
216
|
+
export interface ActionError {
|
|
217
|
+
code: string;
|
|
218
|
+
message: string;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Base Hetzner Action type matching the API response
|
|
223
|
+
*
|
|
224
|
+
* The API returns actions with:
|
|
225
|
+
* - status: "running" | "success" | "error"
|
|
226
|
+
* - finished: string | null (null when running)
|
|
227
|
+
* - error: ActionError | null (null when not error)
|
|
228
|
+
*
|
|
229
|
+
* Note: Zod validation makes finished and error optional, but the API
|
|
230
|
+
* typically returns them. Use type guards for runtime checks.
|
|
231
|
+
*/
|
|
232
|
+
export interface HetznerAction {
|
|
233
|
+
id: number;
|
|
234
|
+
command: ActionCommand | string; // API returns string
|
|
235
|
+
status: ActionStatus;
|
|
236
|
+
started: string;
|
|
237
|
+
finished?: string | null;
|
|
238
|
+
progress: number;
|
|
239
|
+
resources: ActionResource[];
|
|
240
|
+
error?: ActionError | null;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Server action response (includes server reference)
|
|
245
|
+
*/
|
|
246
|
+
export interface ServerActionResponse {
|
|
247
|
+
action: HetznerAction;
|
|
248
|
+
server?: HetznerServer;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Create server response with actions
|
|
253
|
+
*/
|
|
254
|
+
export interface CreateServerResponse {
|
|
255
|
+
server: HetznerServer;
|
|
256
|
+
action: HetznerAction;
|
|
257
|
+
next_actions: HetznerAction[];
|
|
258
|
+
root_password: string | null;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// ============================================================================
|
|
262
|
+
// Volume Types
|
|
263
|
+
// ============================================================================
|
|
264
|
+
|
|
265
|
+
export interface HetznerVolume {
|
|
266
|
+
id: number;
|
|
267
|
+
name: string;
|
|
268
|
+
status: VolumeStatus;
|
|
269
|
+
server: number | null;
|
|
270
|
+
size: number;
|
|
271
|
+
linux_device: string | null;
|
|
272
|
+
format: string | null;
|
|
273
|
+
location: {
|
|
274
|
+
id: number;
|
|
275
|
+
name: string;
|
|
276
|
+
description: string;
|
|
277
|
+
country: string;
|
|
278
|
+
city: string;
|
|
279
|
+
latitude: number;
|
|
280
|
+
longitude: number;
|
|
281
|
+
} | null;
|
|
282
|
+
labels: Record<string, string>;
|
|
283
|
+
created: string;
|
|
284
|
+
protection: {
|
|
285
|
+
delete: boolean;
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export interface CreateVolumeOptions {
|
|
290
|
+
name: string;
|
|
291
|
+
size: number;
|
|
292
|
+
server?: number;
|
|
293
|
+
location?: string;
|
|
294
|
+
format?: string;
|
|
295
|
+
automount?: boolean;
|
|
296
|
+
labels?: Record<string, string>;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// ============================================================================
|
|
300
|
+
// Network Types
|
|
301
|
+
// ============================================================================
|
|
302
|
+
|
|
303
|
+
export interface HetznerNetwork {
|
|
304
|
+
id: number;
|
|
305
|
+
name: string;
|
|
306
|
+
ip_range: string;
|
|
307
|
+
subnets: Array<{
|
|
308
|
+
type: "server" | "cloud" | "vswitch";
|
|
309
|
+
ip_range: string;
|
|
310
|
+
network_zone: string;
|
|
311
|
+
gateway: string;
|
|
312
|
+
}>;
|
|
313
|
+
routes: Array<{
|
|
314
|
+
destination: string;
|
|
315
|
+
gateway: string;
|
|
316
|
+
}>;
|
|
317
|
+
servers: number[];
|
|
318
|
+
protection: {
|
|
319
|
+
delete: boolean;
|
|
320
|
+
};
|
|
321
|
+
labels: Record<string, string>;
|
|
322
|
+
created: string;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// ============================================================================
|
|
326
|
+
// SSH Key Types
|
|
327
|
+
// ============================================================================
|
|
328
|
+
|
|
329
|
+
export interface HetznerSSHKey {
|
|
330
|
+
id: number;
|
|
331
|
+
name: string;
|
|
332
|
+
fingerprint: string;
|
|
333
|
+
public_key: string;
|
|
334
|
+
labels: Record<string, string>;
|
|
335
|
+
created: string;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export interface CreateSSHKeyOptions {
|
|
339
|
+
name: string;
|
|
340
|
+
public_key: string;
|
|
341
|
+
labels?: Record<string, string>;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// ============================================================================
|
|
345
|
+
// Rate Limiting Types
|
|
346
|
+
// ============================================================================
|
|
347
|
+
|
|
348
|
+
export interface RateLimitInfo {
|
|
349
|
+
limit: number;
|
|
350
|
+
remaining: number;
|
|
351
|
+
reset: number; // Unix timestamp
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export interface RateLimitHeaders {
|
|
355
|
+
"RateLimit-Limit": string;
|
|
356
|
+
"RateLimit-Remaining": string;
|
|
357
|
+
"RateLimit-Reset": string;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// ============================================================================
|
|
361
|
+
// Polling Options
|
|
362
|
+
// ============================================================================
|
|
363
|
+
|
|
364
|
+
export interface ActionPollingOptions {
|
|
365
|
+
/** Polling interval in milliseconds (default: 2000) */
|
|
366
|
+
pollInterval?: number;
|
|
367
|
+
/** Maximum number of polling attempts (default: 60) */
|
|
368
|
+
maxRetries?: number;
|
|
369
|
+
/** Optional callback for progress updates */
|
|
370
|
+
onProgress?: (action: HetznerAction) => void;
|
|
371
|
+
/** Optional timeout in milliseconds */
|
|
372
|
+
timeout?: number;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// ============================================================================
|
|
376
|
+
// Import shared Hetzner types for local use
|
|
377
|
+
// ============================================================================
|
|
378
|
+
|
|
379
|
+
import type {
|
|
380
|
+
HetznerServerType,
|
|
381
|
+
HetznerLocation,
|
|
382
|
+
HetznerDatacenter,
|
|
383
|
+
} from "@ebowwa/codespaces-types/compile";
|
|
384
|
+
|
|
385
|
+
// ============================================================================
|
|
386
|
+
// Re-export shared Hetzner types
|
|
387
|
+
// ============================================================================
|
|
388
|
+
|
|
389
|
+
export type { HetznerServerType, HetznerLocation, HetznerDatacenter };
|
package/volumes.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hetzner Volume Operations
|
|
3
|
+
*
|
|
4
|
+
* Provides methods for managing Hetzner Cloud volumes.
|
|
5
|
+
* See: https://docs.hetzner.cloud/#volumes
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Volume operations for Hetzner Cloud API
|
|
9
|
+
*/
|
|
10
|
+
export class VolumeOperations {
|
|
11
|
+
client;
|
|
12
|
+
constructor(client) {
|
|
13
|
+
this.client = client;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* List all volumes
|
|
17
|
+
*
|
|
18
|
+
* @param options - List options (name, status, sort, etc.)
|
|
19
|
+
* @returns Array of volumes
|
|
20
|
+
*/
|
|
21
|
+
async list(options) {
|
|
22
|
+
const params = new URLSearchParams();
|
|
23
|
+
if (options?.name)
|
|
24
|
+
params.set("name", options.name);
|
|
25
|
+
if (options?.status)
|
|
26
|
+
params.set("status", options.status);
|
|
27
|
+
if (options?.sort)
|
|
28
|
+
params.set("sort", options.sort);
|
|
29
|
+
if (options?.label_selector)
|
|
30
|
+
params.set("label_selector", options.label_selector);
|
|
31
|
+
const endpoint = `/volumes${params.toString() ? `?${params}` : ""}`;
|
|
32
|
+
const response = await this.client.request(endpoint);
|
|
33
|
+
return response.volumes || [];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Get a specific volume by ID
|
|
37
|
+
*
|
|
38
|
+
* @param id - Volume ID
|
|
39
|
+
* @returns Volume details
|
|
40
|
+
*/
|
|
41
|
+
async get(id) {
|
|
42
|
+
const response = await this.client.request(`/volumes/${id}`);
|
|
43
|
+
return response.volume;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Create a new volume
|
|
47
|
+
*
|
|
48
|
+
* @param options - Volume creation options
|
|
49
|
+
* @returns Created volume with action info
|
|
50
|
+
*/
|
|
51
|
+
async create(options) {
|
|
52
|
+
const response = await this.client.request("/volumes", {
|
|
53
|
+
method: "POST",
|
|
54
|
+
body: JSON.stringify({
|
|
55
|
+
name: options.name,
|
|
56
|
+
size: options.size,
|
|
57
|
+
server: options.server,
|
|
58
|
+
location: options.location,
|
|
59
|
+
automount: options.automount ?? true,
|
|
60
|
+
format: options.format,
|
|
61
|
+
labels: options.labels,
|
|
62
|
+
}),
|
|
63
|
+
});
|
|
64
|
+
return response;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Delete a volume
|
|
68
|
+
*
|
|
69
|
+
* @param id - Volume ID
|
|
70
|
+
* @returns Action response
|
|
71
|
+
*/
|
|
72
|
+
async delete(id) {
|
|
73
|
+
const response = await this.client.request(`/volumes/${id}`, {
|
|
74
|
+
method: "DELETE",
|
|
75
|
+
});
|
|
76
|
+
return response.action;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Attach a volume to a server
|
|
80
|
+
*
|
|
81
|
+
* @param volumeId - Volume ID
|
|
82
|
+
* @param serverId - Server ID
|
|
83
|
+
* @param automount - Automatically mount the volume
|
|
84
|
+
* @returns Action response
|
|
85
|
+
*/
|
|
86
|
+
async attach(volumeId, serverId, automount = true) {
|
|
87
|
+
const response = await this.client.request(`/volumes/${volumeId}/actions/attach`, {
|
|
88
|
+
method: "POST",
|
|
89
|
+
body: JSON.stringify({
|
|
90
|
+
server: serverId,
|
|
91
|
+
automount,
|
|
92
|
+
}),
|
|
93
|
+
});
|
|
94
|
+
return response.action;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Detach a volume from a server
|
|
98
|
+
*
|
|
99
|
+
* @param volumeId - Volume ID
|
|
100
|
+
* @returns Action response
|
|
101
|
+
*/
|
|
102
|
+
async detach(volumeId) {
|
|
103
|
+
const response = await this.client.request(`/volumes/${volumeId}/actions/detach`, {
|
|
104
|
+
method: "POST",
|
|
105
|
+
});
|
|
106
|
+
return response.action;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Resize a volume
|
|
110
|
+
*
|
|
111
|
+
* @param volumeId - Volume ID
|
|
112
|
+
* @param size - New size in GB (must be larger than current)
|
|
113
|
+
* @returns Action response
|
|
114
|
+
*/
|
|
115
|
+
async resize(volumeId, size) {
|
|
116
|
+
const response = await this.client.request(`/volumes/${volumeId}/actions/resize`, {
|
|
117
|
+
method: "POST",
|
|
118
|
+
body: JSON.stringify({ size }),
|
|
119
|
+
});
|
|
120
|
+
return response.action;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Change volume protection
|
|
124
|
+
*
|
|
125
|
+
* @param volumeId - Volume ID
|
|
126
|
+
* @param deleteProtection - Enable delete protection
|
|
127
|
+
* @returns Action response
|
|
128
|
+
*/
|
|
129
|
+
async changeProtection(volumeId, deleteProtection) {
|
|
130
|
+
const response = await this.client.request(`/volumes/${volumeId}/actions/change_protection`, {
|
|
131
|
+
method: "POST",
|
|
132
|
+
body: JSON.stringify({
|
|
133
|
+
delete: deleteProtection,
|
|
134
|
+
}),
|
|
135
|
+
});
|
|
136
|
+
return response.action;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Update volume labels
|
|
140
|
+
*
|
|
141
|
+
* @param volumeId - Volume ID
|
|
142
|
+
* @param labels - New labels
|
|
143
|
+
* @returns Updated volume
|
|
144
|
+
*/
|
|
145
|
+
async updateLabels(volumeId, labels) {
|
|
146
|
+
const response = await this.client.request(`/volumes/${volumeId}`, {
|
|
147
|
+
method: "PUT",
|
|
148
|
+
body: JSON.stringify({ labels }),
|
|
149
|
+
});
|
|
150
|
+
return response.volume;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get volume pricing information
|
|
154
|
+
* Calculates monthly cost based on size (€0.008/GB per month)
|
|
155
|
+
*
|
|
156
|
+
* @param sizeInGB - Volume size in GB
|
|
157
|
+
* @returns Monthly and hourly pricing
|
|
158
|
+
*/
|
|
159
|
+
static calculatePrice(sizeInGB) {
|
|
160
|
+
const PRICE_PER_GB_MONTHLY = 0.008; // €0.008 per GB per month
|
|
161
|
+
const HOURS_PER_MONTH = 730; // Average
|
|
162
|
+
const monthly = sizeInGB * PRICE_PER_GB_MONTHLY;
|
|
163
|
+
const hourly = monthly / HOURS_PER_MONTH;
|
|
164
|
+
return {
|
|
165
|
+
size: sizeInGB,
|
|
166
|
+
monthly: Math.round(monthly * 100) / 100,
|
|
167
|
+
hourly: Math.round(hourly * 10000) / 10000,
|
|
168
|
+
currency: "EUR",
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=volumes.js.map
|