@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.
Files changed (46) hide show
  1. package/actions.js +802 -0
  2. package/actions.ts +1053 -0
  3. package/auth.js +35 -0
  4. package/auth.ts +37 -0
  5. package/bootstrap/FIREWALL.md +326 -0
  6. package/bootstrap/KERNEL-HARDENING.md +258 -0
  7. package/bootstrap/SECURITY-INTEGRATION.md +281 -0
  8. package/bootstrap/TESTING.md +301 -0
  9. package/bootstrap/cloud-init.js +279 -0
  10. package/bootstrap/cloud-init.ts +394 -0
  11. package/bootstrap/firewall.js +279 -0
  12. package/bootstrap/firewall.ts +342 -0
  13. package/bootstrap/genesis.js +406 -0
  14. package/bootstrap/genesis.ts +518 -0
  15. package/bootstrap/index.js +35 -0
  16. package/bootstrap/index.ts +71 -0
  17. package/bootstrap/kernel-hardening.js +266 -0
  18. package/bootstrap/kernel-hardening.test.ts +230 -0
  19. package/bootstrap/kernel-hardening.ts +272 -0
  20. package/bootstrap/security-audit.js +118 -0
  21. package/bootstrap/security-audit.ts +124 -0
  22. package/bootstrap/ssh-hardening.js +182 -0
  23. package/bootstrap/ssh-hardening.ts +192 -0
  24. package/client.js +137 -0
  25. package/client.ts +177 -0
  26. package/config.js +5 -0
  27. package/config.ts +5 -0
  28. package/errors.js +270 -0
  29. package/errors.ts +371 -0
  30. package/index.js +28 -0
  31. package/index.ts +55 -0
  32. package/package.json +56 -0
  33. package/pricing.js +284 -0
  34. package/pricing.ts +422 -0
  35. package/schemas.js +660 -0
  36. package/schemas.ts +765 -0
  37. package/server-status.ts +81 -0
  38. package/servers.js +424 -0
  39. package/servers.ts +568 -0
  40. package/ssh-keys.js +90 -0
  41. package/ssh-keys.ts +122 -0
  42. package/ssh-setup.ts +218 -0
  43. package/types.js +96 -0
  44. package/types.ts +389 -0
  45. package/volumes.js +172 -0
  46. package/volumes.ts +229 -0
package/volumes.ts ADDED
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Hetzner Volume Operations
3
+ *
4
+ * Provides methods for managing Hetzner Cloud volumes.
5
+ * See: https://docs.hetzner.cloud/#volumes
6
+ */
7
+
8
+ import type { HetznerClient } from "./client.js";
9
+ import type {
10
+ HetznerVolume,
11
+ CreateVolumeOptions,
12
+ } from "./types.js";
13
+ import {
14
+ HetznerListVolumesResponseSchema,
15
+ HetznerGetVolumeResponseSchema,
16
+ HetznerCreateVolumeResponseSchema,
17
+ HetznerActionResponseSchema,
18
+ } from "./schemas.js";
19
+
20
+ /**
21
+ * Volume operations for Hetzner Cloud API
22
+ */
23
+ export class VolumeOperations {
24
+ constructor(private client: HetznerClient) {}
25
+
26
+ /**
27
+ * List all volumes
28
+ *
29
+ * @param options - List options (name, status, sort, etc.)
30
+ * @returns Array of volumes
31
+ */
32
+ async list(options?: {
33
+ name?: string;
34
+ status?: string;
35
+ sort?: string;
36
+ label_selector?: string;
37
+ }): Promise<HetznerVolume[]> {
38
+ const params = new URLSearchParams();
39
+ if (options?.name) params.set("name", options.name);
40
+ if (options?.status) params.set("status", options.status);
41
+ if (options?.sort) params.set("sort", options.sort);
42
+ if (options?.label_selector) params.set("label_selector", options.label_selector);
43
+
44
+ const endpoint = `/volumes${params.toString() ? `?${params}` : ""}`;
45
+ const response = await this.client.request<typeof HetznerListVolumesResponseSchema._output>(
46
+ endpoint,
47
+ );
48
+
49
+ return response.volumes || [];
50
+ }
51
+
52
+ /**
53
+ * Get a specific volume by ID
54
+ *
55
+ * @param id - Volume ID
56
+ * @returns Volume details
57
+ */
58
+ async get(id: number): Promise<HetznerVolume> {
59
+ const response = await this.client.request<typeof HetznerGetVolumeResponseSchema._output>(
60
+ `/volumes/${id}`,
61
+ );
62
+ return response.volume;
63
+ }
64
+
65
+ /**
66
+ * Create a new volume
67
+ *
68
+ * @param options - Volume creation options
69
+ * @returns Created volume with action info
70
+ */
71
+ async create(options: CreateVolumeOptions): Promise<{
72
+ volume: HetznerVolume;
73
+ action: any;
74
+ next_actions: any[];
75
+ }> {
76
+ const response = await this.client.request<typeof HetznerCreateVolumeResponseSchema._output>(
77
+ "/volumes",
78
+ {
79
+ method: "POST",
80
+ body: JSON.stringify({
81
+ name: options.name,
82
+ size: options.size,
83
+ server: options.server,
84
+ location: options.location,
85
+ automount: options.automount ?? true,
86
+ format: options.format,
87
+ labels: options.labels,
88
+ }),
89
+ },
90
+ );
91
+ return response;
92
+ }
93
+
94
+ /**
95
+ * Delete a volume
96
+ *
97
+ * @param id - Volume ID
98
+ * @returns Action response
99
+ */
100
+ async delete(id: number): Promise<any> {
101
+ const response = await this.client.request<typeof HetznerActionResponseSchema._output>(
102
+ `/volumes/${id}`,
103
+ {
104
+ method: "DELETE",
105
+ },
106
+ );
107
+ return response.action;
108
+ }
109
+
110
+ /**
111
+ * Attach a volume to a server
112
+ *
113
+ * @param volumeId - Volume ID
114
+ * @param serverId - Server ID
115
+ * @param automount - Automatically mount the volume
116
+ * @returns Action response
117
+ */
118
+ async attach(
119
+ volumeId: number,
120
+ serverId: number,
121
+ automount: boolean = true,
122
+ ): Promise<any> {
123
+ const response = await this.client.request<typeof HetznerActionResponseSchema._output>(
124
+ `/volumes/${volumeId}/actions/attach`,
125
+ {
126
+ method: "POST",
127
+ body: JSON.stringify({
128
+ server: serverId,
129
+ automount,
130
+ }),
131
+ },
132
+ );
133
+ return response.action;
134
+ }
135
+
136
+ /**
137
+ * Detach a volume from a server
138
+ *
139
+ * @param volumeId - Volume ID
140
+ * @returns Action response
141
+ */
142
+ async detach(volumeId: number): Promise<any> {
143
+ const response = await this.client.request<typeof HetznerActionResponseSchema._output>(
144
+ `/volumes/${volumeId}/actions/detach`,
145
+ {
146
+ method: "POST",
147
+ },
148
+ );
149
+ return response.action;
150
+ }
151
+
152
+ /**
153
+ * Resize a volume
154
+ *
155
+ * @param volumeId - Volume ID
156
+ * @param size - New size in GB (must be larger than current)
157
+ * @returns Action response
158
+ */
159
+ async resize(volumeId: number, size: number): Promise<any> {
160
+ const response = await this.client.request<typeof HetznerActionResponseSchema._output>(
161
+ `/volumes/${volumeId}/actions/resize`,
162
+ {
163
+ method: "POST",
164
+ body: JSON.stringify({ size }),
165
+ },
166
+ );
167
+ return response.action;
168
+ }
169
+
170
+ /**
171
+ * Change volume protection
172
+ *
173
+ * @param volumeId - Volume ID
174
+ * @param deleteProtection - Enable delete protection
175
+ * @returns Action response
176
+ */
177
+ async changeProtection(volumeId: number, deleteProtection: boolean): Promise<any> {
178
+ const response = await this.client.request<typeof HetznerActionResponseSchema._output>(
179
+ `/volumes/${volumeId}/actions/change_protection`,
180
+ {
181
+ method: "POST",
182
+ body: JSON.stringify({
183
+ delete: deleteProtection,
184
+ }),
185
+ },
186
+ );
187
+ return response.action;
188
+ }
189
+
190
+ /**
191
+ * Update volume labels
192
+ *
193
+ * @param volumeId - Volume ID
194
+ * @param labels - New labels
195
+ * @returns Updated volume
196
+ */
197
+ async updateLabels(volumeId: number, labels: Record<string, string>): Promise<HetznerVolume> {
198
+ const response = await this.client.request<typeof HetznerGetVolumeResponseSchema._output>(
199
+ `/volumes/${volumeId}`,
200
+ {
201
+ method: "PUT",
202
+ body: JSON.stringify({ labels }),
203
+ },
204
+ );
205
+ return response.volume;
206
+ }
207
+
208
+ /**
209
+ * Get volume pricing information
210
+ * Calculates monthly cost based on size (€0.008/GB per month)
211
+ *
212
+ * @param sizeInGB - Volume size in GB
213
+ * @returns Monthly and hourly pricing
214
+ */
215
+ static calculatePrice(sizeInGB: number) {
216
+ const PRICE_PER_GB_MONTHLY = 0.008; // €0.008 per GB per month
217
+ const HOURS_PER_MONTH = 730; // Average
218
+
219
+ const monthly = sizeInGB * PRICE_PER_GB_MONTHLY;
220
+ const hourly = monthly / HOURS_PER_MONTH;
221
+
222
+ return {
223
+ size: sizeInGB,
224
+ monthly: Math.round(monthly * 100) / 100,
225
+ hourly: Math.round(hourly * 10000) / 10000,
226
+ currency: "EUR",
227
+ };
228
+ }
229
+ }