@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/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
|
+
}
|