@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/server-status.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch server status from Hetzner API by IP address
|
|
3
|
+
* Used for network error detection to determine if server is actually running
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { HetznerClient } from "./client.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Cache for server status lookups
|
|
10
|
+
* Key: IP address, Value: { status, timestamp }
|
|
11
|
+
*/
|
|
12
|
+
const statusCache = new Map<string, { status: string; timestamp: number }>();
|
|
13
|
+
const CACHE_TTL = 30 * 1000; // 30 seconds
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get server status by IP address
|
|
17
|
+
* @param ip - The server IP address
|
|
18
|
+
* @param client - HetznerClient instance
|
|
19
|
+
* @returns Server status ("running", "stopped", etc.) or null if not found
|
|
20
|
+
*/
|
|
21
|
+
export async function getServerStatusByIP(
|
|
22
|
+
ip: string,
|
|
23
|
+
client?: HetznerClient
|
|
24
|
+
): Promise<string | null> {
|
|
25
|
+
// Check cache first
|
|
26
|
+
const cached = statusCache.get(ip);
|
|
27
|
+
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
|
|
28
|
+
return cached.status;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// If no client provided, create one (will use env vars)
|
|
32
|
+
const hetznerClient = client || new HetznerClient();
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
// List all servers and find matching IP
|
|
36
|
+
const servers = await hetznerClient.listServers();
|
|
37
|
+
|
|
38
|
+
// Find server with matching IPv4
|
|
39
|
+
const server = servers.find(
|
|
40
|
+
(s) => s.public_net.ipv4.ip === ip
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
if (server) {
|
|
44
|
+
const status = server.status;
|
|
45
|
+
// Cache the result
|
|
46
|
+
statusCache.set(ip, { status, timestamp: Date.now() });
|
|
47
|
+
return status;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Server not found
|
|
51
|
+
return null;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
// Log error but don't throw - we don't want to break SSH connection on API failure
|
|
54
|
+
console.warn(`[NetworkError] Failed to fetch server status for ${ip}:`, error);
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Clear the status cache
|
|
61
|
+
* Useful for testing or when you know server state has changed
|
|
62
|
+
*/
|
|
63
|
+
export function clearServerStatusCache(): void {
|
|
64
|
+
statusCache.clear();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get server status by IP with a fallback default
|
|
69
|
+
* @param ip - The server IP address
|
|
70
|
+
* @param client - HetznerClient instance (optional)
|
|
71
|
+
* @param defaultStatus - Default status to return if lookup fails (default: "unknown")
|
|
72
|
+
* @returns Server status or defaultStatus if not found/error
|
|
73
|
+
*/
|
|
74
|
+
export async function getServerStatusByIPWithDefault(
|
|
75
|
+
ip: string,
|
|
76
|
+
client?: HetznerClient,
|
|
77
|
+
defaultStatus: string = "unknown"
|
|
78
|
+
): Promise<string> {
|
|
79
|
+
const status = await getServerStatusByIP(ip, client);
|
|
80
|
+
return status ?? defaultStatus;
|
|
81
|
+
}
|
package/servers.js
ADDED
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hetzner server operations
|
|
3
|
+
*/
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { HetznerListServersResponseSchema, HetznerGetServerResponseSchema, HetznerCreateServerResponseSchema, HetznerActionSchema, } from "./schemas.js";
|
|
6
|
+
export class ServerOperations {
|
|
7
|
+
client;
|
|
8
|
+
constructor(client) {
|
|
9
|
+
this.client = client;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* List all servers
|
|
13
|
+
*/
|
|
14
|
+
async list() {
|
|
15
|
+
const response = await this.client.request("/servers");
|
|
16
|
+
// Validate response with Zod
|
|
17
|
+
const validated = HetznerListServersResponseSchema.safeParse(response);
|
|
18
|
+
if (!validated.success) {
|
|
19
|
+
console.warn('Hetzner list servers validation warning:', validated.error.issues);
|
|
20
|
+
return response.servers; // Return unvalidated data for backward compatibility
|
|
21
|
+
}
|
|
22
|
+
return validated.data.servers;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Get a specific server by ID
|
|
26
|
+
*/
|
|
27
|
+
async get(id) {
|
|
28
|
+
const response = await this.client.request(`/servers/${id}`);
|
|
29
|
+
// Validate response with Zod
|
|
30
|
+
const validated = HetznerGetServerResponseSchema.safeParse(response);
|
|
31
|
+
if (!validated.success) {
|
|
32
|
+
console.warn('Hetzner get server validation warning:', validated.error.issues);
|
|
33
|
+
return response.server; // Return unvalidated data for backward compatibility
|
|
34
|
+
}
|
|
35
|
+
return validated.data.server;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Create a new server
|
|
39
|
+
*
|
|
40
|
+
* @param options - Server creation options
|
|
41
|
+
* @returns Create server response including server, action, and next_actions
|
|
42
|
+
*/
|
|
43
|
+
async create(options) {
|
|
44
|
+
// Validate input with Zod
|
|
45
|
+
const createServerOptionsSchema = z.object({
|
|
46
|
+
name: z.string().min(1),
|
|
47
|
+
server_type: z.string().min(1).default("cpx11"),
|
|
48
|
+
image: z.string().min(1).default("ubuntu-24.04"),
|
|
49
|
+
location: z.string().min(1).optional(),
|
|
50
|
+
datacenter: z.string().min(1).optional(),
|
|
51
|
+
ssh_keys: z.array(z.union([z.string(), z.number()])).default([]),
|
|
52
|
+
volumes: z.array(z.number()).default([]),
|
|
53
|
+
labels: z.record(z.string(), z.any()).optional(),
|
|
54
|
+
start_after_create: z.boolean().default(true),
|
|
55
|
+
user_data: z.string().optional(),
|
|
56
|
+
});
|
|
57
|
+
const validatedOptions = createServerOptionsSchema.safeParse(options);
|
|
58
|
+
if (!validatedOptions.success) {
|
|
59
|
+
throw new Error(`Invalid server options: ${validatedOptions.error.issues.map(i => i.message).join(', ')}`);
|
|
60
|
+
}
|
|
61
|
+
// Ensure either location or datacenter, not both
|
|
62
|
+
if (validatedOptions.data.location && validatedOptions.data.datacenter) {
|
|
63
|
+
throw new Error('Cannot specify both location and datacenter');
|
|
64
|
+
}
|
|
65
|
+
const body = {
|
|
66
|
+
name: validatedOptions.data.name,
|
|
67
|
+
server_type: validatedOptions.data.server_type,
|
|
68
|
+
image: validatedOptions.data.image,
|
|
69
|
+
...(validatedOptions.data.location && { location: validatedOptions.data.location }),
|
|
70
|
+
...(validatedOptions.data.datacenter && { datacenter: { id: validatedOptions.data.datacenter } }),
|
|
71
|
+
ssh_keys: validatedOptions.data.ssh_keys,
|
|
72
|
+
volumes: validatedOptions.data.volumes,
|
|
73
|
+
...(validatedOptions.data.labels && { labels: validatedOptions.data.labels }),
|
|
74
|
+
start_after_create: validatedOptions.data.start_after_create,
|
|
75
|
+
...(validatedOptions.data.user_data && { user_data: validatedOptions.data.user_data }),
|
|
76
|
+
};
|
|
77
|
+
console.log('[Hetzner] Creating server with body:', JSON.stringify(body, null, 2));
|
|
78
|
+
const response = await this.client.request("/servers", {
|
|
79
|
+
method: "POST",
|
|
80
|
+
body: JSON.stringify(body),
|
|
81
|
+
});
|
|
82
|
+
// Validate response with Zod
|
|
83
|
+
const validatedResponse = HetznerCreateServerResponseSchema.safeParse(response);
|
|
84
|
+
if (!validatedResponse.success) {
|
|
85
|
+
console.warn('Hetzner create server validation warning:', validatedResponse.error.issues);
|
|
86
|
+
return response; // Return unvalidated data for backward compatibility
|
|
87
|
+
}
|
|
88
|
+
return validatedResponse.data;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Create a new server and wait for it to be ready
|
|
92
|
+
*
|
|
93
|
+
* This convenience method creates a server and waits for the initial action to complete.
|
|
94
|
+
*
|
|
95
|
+
* @param options - Server creation options
|
|
96
|
+
* @param onProgress - Optional progress callback
|
|
97
|
+
* @returns Server once ready
|
|
98
|
+
*/
|
|
99
|
+
async createAndWait(options, onProgress) {
|
|
100
|
+
const response = await this.create(options);
|
|
101
|
+
// Build wait options (only include onProgress if defined)
|
|
102
|
+
const waitOptions = onProgress !== undefined ? { onProgress } : {};
|
|
103
|
+
// Wait for the main create action to complete
|
|
104
|
+
if (response.action.status === 'running') {
|
|
105
|
+
await this.client.actions.waitFor(response.action.id, waitOptions);
|
|
106
|
+
}
|
|
107
|
+
// Wait for any next actions (e.g., start server)
|
|
108
|
+
if (response.next_actions.length > 0) {
|
|
109
|
+
for (const action of response.next_actions) {
|
|
110
|
+
await this.client.actions.waitFor(action.id, waitOptions);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Return the server object
|
|
114
|
+
const server = await this.get(response.server.id);
|
|
115
|
+
return server;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Delete a server
|
|
119
|
+
*
|
|
120
|
+
* @param id - Server ID
|
|
121
|
+
* @returns Action for server deletion
|
|
122
|
+
*/
|
|
123
|
+
async delete(id) {
|
|
124
|
+
// Validate ID with Zod
|
|
125
|
+
const serverIdSchema = z.number().int().positive();
|
|
126
|
+
const validatedId = serverIdSchema.safeParse(id);
|
|
127
|
+
if (!validatedId.success) {
|
|
128
|
+
throw new Error(`Invalid server ID: ${validatedId.error.issues.map(i => i.message).join(', ')}`);
|
|
129
|
+
}
|
|
130
|
+
const response = await this.client.request(`/servers/${validatedId.data}`, { method: "DELETE" });
|
|
131
|
+
const validated = HetznerActionSchema.safeParse(response.action);
|
|
132
|
+
if (!validated.success) {
|
|
133
|
+
console.warn('Hetzner delete server validation warning:', validated.error.issues);
|
|
134
|
+
return response.action;
|
|
135
|
+
}
|
|
136
|
+
return validated.data;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Delete a server and wait for completion
|
|
140
|
+
*
|
|
141
|
+
* @param id - Server ID
|
|
142
|
+
* @param onProgress - Optional progress callback
|
|
143
|
+
*/
|
|
144
|
+
async deleteAndWait(id, onProgress) {
|
|
145
|
+
const action = await this.delete(id);
|
|
146
|
+
await this.client.actions.waitFor(action.id, { onProgress });
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Power on a server
|
|
150
|
+
*
|
|
151
|
+
* @param id - Server ID
|
|
152
|
+
* @returns Action for server power on
|
|
153
|
+
*/
|
|
154
|
+
async powerOn(id) {
|
|
155
|
+
// Validate ID with Zod
|
|
156
|
+
const serverIdSchema = z.number().int().positive();
|
|
157
|
+
const validatedId = serverIdSchema.safeParse(id);
|
|
158
|
+
if (!validatedId.success) {
|
|
159
|
+
throw new Error(`Invalid server ID: ${validatedId.error.issues.map(i => i.message).join(', ')}`);
|
|
160
|
+
}
|
|
161
|
+
const response = await this.client.request(`/servers/${validatedId.data}/actions/poweron`, { method: "POST" });
|
|
162
|
+
const validated = HetznerActionSchema.safeParse(response.action);
|
|
163
|
+
if (!validated.success) {
|
|
164
|
+
console.warn('Hetzner power on validation warning:', validated.error.issues);
|
|
165
|
+
return response.action;
|
|
166
|
+
}
|
|
167
|
+
return validated.data;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Power on a server and wait for completion
|
|
171
|
+
*
|
|
172
|
+
* @param id - Server ID
|
|
173
|
+
* @param onProgress - Optional progress callback
|
|
174
|
+
*/
|
|
175
|
+
async powerOnAndWait(id, onProgress) {
|
|
176
|
+
const action = await this.powerOn(id);
|
|
177
|
+
return await this.client.actions.waitFor(action.id, { onProgress });
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Power off a server
|
|
181
|
+
*
|
|
182
|
+
* @param id - Server ID
|
|
183
|
+
* @returns Action for server power off
|
|
184
|
+
*/
|
|
185
|
+
async powerOff(id) {
|
|
186
|
+
// Validate ID with Zod
|
|
187
|
+
const serverIdSchema = z.number().int().positive();
|
|
188
|
+
const validatedId = serverIdSchema.safeParse(id);
|
|
189
|
+
if (!validatedId.success) {
|
|
190
|
+
throw new Error(`Invalid server ID: ${validatedId.error.issues.map(i => i.message).join(', ')}`);
|
|
191
|
+
}
|
|
192
|
+
const response = await this.client.request(`/servers/${validatedId.data}/actions/poweroff`, { method: "POST" });
|
|
193
|
+
const validated = HetznerActionSchema.safeParse(response.action);
|
|
194
|
+
if (!validated.success) {
|
|
195
|
+
console.warn('Hetzner power off validation warning:', validated.error.issues);
|
|
196
|
+
return response.action;
|
|
197
|
+
}
|
|
198
|
+
return validated.data;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Power off a server and wait for completion
|
|
202
|
+
*
|
|
203
|
+
* @param id - Server ID
|
|
204
|
+
* @param onProgress - Optional progress callback
|
|
205
|
+
*/
|
|
206
|
+
async powerOffAndWait(id, onProgress) {
|
|
207
|
+
const action = await this.powerOff(id);
|
|
208
|
+
return await this.client.actions.waitFor(action.id, { onProgress });
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Reboot a server
|
|
212
|
+
*
|
|
213
|
+
* @param id - Server ID
|
|
214
|
+
* @returns Action for server reboot
|
|
215
|
+
*/
|
|
216
|
+
async reboot(id) {
|
|
217
|
+
// Validate ID with Zod
|
|
218
|
+
const serverIdSchema = z.number().int().positive();
|
|
219
|
+
const validatedId = serverIdSchema.safeParse(id);
|
|
220
|
+
if (!validatedId.success) {
|
|
221
|
+
throw new Error(`Invalid server ID: ${validatedId.error.issues.map(i => i.message).join(', ')}`);
|
|
222
|
+
}
|
|
223
|
+
const response = await this.client.request(`/servers/${validatedId.data}/actions/reboot`, { method: "POST" });
|
|
224
|
+
const validated = HetznerActionSchema.safeParse(response.action);
|
|
225
|
+
if (!validated.success) {
|
|
226
|
+
console.warn('Hetzner reboot validation warning:', validated.error.issues);
|
|
227
|
+
return response.action;
|
|
228
|
+
}
|
|
229
|
+
return validated.data;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Reboot a server and wait for completion
|
|
233
|
+
*
|
|
234
|
+
* @param id - Server ID
|
|
235
|
+
* @param onProgress - Optional progress callback
|
|
236
|
+
*/
|
|
237
|
+
async rebootAndWait(id, onProgress) {
|
|
238
|
+
const action = await this.reboot(id);
|
|
239
|
+
return await this.client.actions.waitFor(action.id, { onProgress });
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Shutdown a server gracefully
|
|
243
|
+
*
|
|
244
|
+
* @param id - Server ID
|
|
245
|
+
* @returns Action for server shutdown
|
|
246
|
+
*/
|
|
247
|
+
async shutdown(id) {
|
|
248
|
+
// Validate ID with Zod
|
|
249
|
+
const serverIdSchema = z.number().int().positive();
|
|
250
|
+
const validatedId = serverIdSchema.safeParse(id);
|
|
251
|
+
if (!validatedId.success) {
|
|
252
|
+
throw new Error(`Invalid server ID: ${validatedId.error.issues.map(i => i.message).join(', ')}`);
|
|
253
|
+
}
|
|
254
|
+
const response = await this.client.request(`/servers/${validatedId.data}/actions/shutdown`, { method: "POST" });
|
|
255
|
+
const validated = HetznerActionSchema.safeParse(response.action);
|
|
256
|
+
if (!validated.success) {
|
|
257
|
+
console.warn('Hetzner shutdown validation warning:', validated.error.issues);
|
|
258
|
+
return response.action;
|
|
259
|
+
}
|
|
260
|
+
return validated.data;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Shutdown a server and wait for completion
|
|
264
|
+
*
|
|
265
|
+
* @param id - Server ID
|
|
266
|
+
* @param onProgress - Optional progress callback
|
|
267
|
+
*/
|
|
268
|
+
async shutdownAndWait(id, onProgress) {
|
|
269
|
+
const action = await this.shutdown(id);
|
|
270
|
+
return await this.client.actions.waitFor(action.id, { onProgress });
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Reset a server
|
|
274
|
+
*
|
|
275
|
+
* @param id - Server ID
|
|
276
|
+
* @returns Action for server reset
|
|
277
|
+
*/
|
|
278
|
+
async reset(id) {
|
|
279
|
+
// Validate ID with Zod
|
|
280
|
+
const serverIdSchema = z.number().int().positive();
|
|
281
|
+
const validatedId = serverIdSchema.safeParse(id);
|
|
282
|
+
if (!validatedId.success) {
|
|
283
|
+
throw new Error(`Invalid server ID: ${validatedId.error.issues.map(i => i.message).join(', ')}`);
|
|
284
|
+
}
|
|
285
|
+
const response = await this.client.request(`/servers/${validatedId.data}/actions/reset`, { method: "POST" });
|
|
286
|
+
const validated = HetznerActionSchema.safeParse(response.action);
|
|
287
|
+
if (!validated.success) {
|
|
288
|
+
console.warn('Hetzner reset validation warning:', validated.error.issues);
|
|
289
|
+
return response.action;
|
|
290
|
+
}
|
|
291
|
+
return validated.data;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Reset a server and wait for completion
|
|
295
|
+
*
|
|
296
|
+
* @param id - Server ID
|
|
297
|
+
* @param onProgress - Optional progress callback
|
|
298
|
+
*/
|
|
299
|
+
async resetAndWait(id, onProgress) {
|
|
300
|
+
const action = await this.reset(id);
|
|
301
|
+
return await this.client.actions.waitFor(action.id, { onProgress });
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Rebuild a server from an image
|
|
305
|
+
*
|
|
306
|
+
* @param id - Server ID
|
|
307
|
+
* @param image - Image ID or name
|
|
308
|
+
* @returns Action for server rebuild
|
|
309
|
+
*/
|
|
310
|
+
async rebuild(id, image) {
|
|
311
|
+
// Validate ID with Zod
|
|
312
|
+
const serverIdSchema = z.number().int().positive();
|
|
313
|
+
const validatedId = serverIdSchema.safeParse(id);
|
|
314
|
+
if (!validatedId.success) {
|
|
315
|
+
throw new Error(`Invalid server ID: ${validatedId.error.issues.map(i => i.message).join(', ')}`);
|
|
316
|
+
}
|
|
317
|
+
const response = await this.client.request(`/servers/${validatedId.data}/actions/rebuild`, {
|
|
318
|
+
method: "POST",
|
|
319
|
+
body: JSON.stringify({ image }),
|
|
320
|
+
});
|
|
321
|
+
const validated = HetznerActionSchema.safeParse(response.action);
|
|
322
|
+
if (!validated.success) {
|
|
323
|
+
console.warn('Hetzner rebuild validation warning:', validated.error.issues);
|
|
324
|
+
return response.action;
|
|
325
|
+
}
|
|
326
|
+
return validated.data;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Enable rescue mode for a server
|
|
330
|
+
*
|
|
331
|
+
* @param id - Server ID
|
|
332
|
+
* @param options - Rescue mode options
|
|
333
|
+
* @returns Action for enabling rescue mode
|
|
334
|
+
*/
|
|
335
|
+
async enableRescue(id, options) {
|
|
336
|
+
// Validate ID with Zod
|
|
337
|
+
const serverIdSchema = z.number().int().positive();
|
|
338
|
+
const validatedId = serverIdSchema.safeParse(id);
|
|
339
|
+
if (!validatedId.success) {
|
|
340
|
+
throw new Error(`Invalid server ID: ${validatedId.error.issues.map(i => i.message).join(', ')}`);
|
|
341
|
+
}
|
|
342
|
+
const response = await this.client.request(`/servers/${validatedId.data}/actions/enable_rescue`, {
|
|
343
|
+
method: "POST",
|
|
344
|
+
body: JSON.stringify(options || {}),
|
|
345
|
+
});
|
|
346
|
+
const validated = HetznerActionSchema.safeParse(response.action);
|
|
347
|
+
if (!validated.success) {
|
|
348
|
+
console.warn('Hetzner enable rescue validation warning:', validated.error.issues);
|
|
349
|
+
return response.action;
|
|
350
|
+
}
|
|
351
|
+
return validated.data;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Disable rescue mode for a server
|
|
355
|
+
*
|
|
356
|
+
* @param id - Server ID
|
|
357
|
+
* @returns Action for disabling rescue mode
|
|
358
|
+
*/
|
|
359
|
+
async disableRescue(id) {
|
|
360
|
+
// Validate ID with Zod
|
|
361
|
+
const serverIdSchema = z.number().int().positive();
|
|
362
|
+
const validatedId = serverIdSchema.safeParse(id);
|
|
363
|
+
if (!validatedId.success) {
|
|
364
|
+
throw new Error(`Invalid server ID: ${validatedId.error.issues.map(i => i.message).join(', ')}`);
|
|
365
|
+
}
|
|
366
|
+
const response = await this.client.request(`/servers/${validatedId.data}/actions/disable_rescue`, { method: "POST" });
|
|
367
|
+
const validated = HetznerActionSchema.safeParse(response.action);
|
|
368
|
+
if (!validated.success) {
|
|
369
|
+
console.warn('Hetzner disable rescue validation warning:', validated.error.issues);
|
|
370
|
+
return response.action;
|
|
371
|
+
}
|
|
372
|
+
return validated.data;
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Change server type
|
|
376
|
+
*
|
|
377
|
+
* @param id - Server ID
|
|
378
|
+
* @param serverType - New server type
|
|
379
|
+
* @param upgradeDisk - Whether to upgrade disk (default: false)
|
|
380
|
+
* @returns Action for changing server type
|
|
381
|
+
*/
|
|
382
|
+
async changeType(id, serverType, upgradeDisk = false) {
|
|
383
|
+
// Validate ID with Zod
|
|
384
|
+
const serverIdSchema = z.number().int().positive();
|
|
385
|
+
const validatedId = serverIdSchema.safeParse(id);
|
|
386
|
+
if (!validatedId.success) {
|
|
387
|
+
throw new Error(`Invalid server ID: ${validatedId.error.issues.map(i => i.message).join(', ')}`);
|
|
388
|
+
}
|
|
389
|
+
const response = await this.client.request(`/servers/${validatedId.data}/actions/change_type`, {
|
|
390
|
+
method: "POST",
|
|
391
|
+
body: JSON.stringify({ server_type: serverType, upgrade_disk: upgradeDisk }),
|
|
392
|
+
});
|
|
393
|
+
const validated = HetznerActionSchema.safeParse(response.action);
|
|
394
|
+
if (!validated.success) {
|
|
395
|
+
console.warn('Hetzner change type validation warning:', validated.error.issues);
|
|
396
|
+
return response.action;
|
|
397
|
+
}
|
|
398
|
+
return validated.data;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Get actions for a specific server
|
|
402
|
+
*
|
|
403
|
+
* @param id - Server ID
|
|
404
|
+
* @param options - Optional filters (status, sort, etc.)
|
|
405
|
+
* @returns Array of server actions
|
|
406
|
+
*/
|
|
407
|
+
async getActions(id, options) {
|
|
408
|
+
// Validate ID with Zod
|
|
409
|
+
const serverIdSchema = z.number().int().positive();
|
|
410
|
+
const validatedId = serverIdSchema.safeParse(id);
|
|
411
|
+
if (!validatedId.success) {
|
|
412
|
+
throw new Error(`Invalid server ID: ${validatedId.error.issues.map(i => i.message).join(', ')}`);
|
|
413
|
+
}
|
|
414
|
+
const params = new URLSearchParams();
|
|
415
|
+
if (options?.status)
|
|
416
|
+
params.append("status", options.status);
|
|
417
|
+
if (options?.sort)
|
|
418
|
+
params.append("sort", options.sort);
|
|
419
|
+
const query = params.toString();
|
|
420
|
+
const response = await this.client.request(`/servers/${validatedId.data}/actions${query ? `?${query}` : ""}`);
|
|
421
|
+
return response.actions;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
//# sourceMappingURL=servers.js.map
|