@ebowwa/hetzner 0.1.0 → 0.2.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 +721 -439
- package/auth.js +15 -11
- package/bootstrap/cloud-init.js +109 -65
- package/bootstrap/firewall.js +30 -17
- package/bootstrap/genesis.js +89 -71
- package/bootstrap/index.js +31 -7
- package/bootstrap/kernel-hardening.js +10 -6
- package/bootstrap/kernel-hardening.test.js +182 -0
- package/bootstrap/security-audit.js +10 -6
- package/bootstrap/ssh-hardening.js +10 -6
- package/client.js +180 -83
- package/config.js +4 -2
- package/errors.js +183 -108
- package/index.js +57 -12
- package/index.ts +4 -0
- package/onboarding/doppler.ts +116 -0
- package/onboarding/git.ts +133 -0
- package/onboarding/index.ts +18 -0
- package/onboarding/onboarding.ts +193 -0
- package/onboarding/tailscale.ts +159 -0
- package/onboarding/types.ts +115 -0
- package/package.json +6 -1
- package/pricing.js +216 -113
- package/schemas.js +322 -315
- package/server-status.js +122 -0
- package/servers.js +530 -287
- package/ssh-keys.js +153 -63
- package/ssh-setup.js +253 -0
- package/ssh-setup.ts +1 -1
- package/types.js +11 -8
- package/volumes.js +205 -82
package/errors.js
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* Hetzner Cloud API error types and utilities
|
|
3
4
|
*/
|
|
5
|
+
var __extends = (this && this.__extends) || (function () {
|
|
6
|
+
var extendStatics = function (d, b) {
|
|
7
|
+
extendStatics = Object.setPrototypeOf ||
|
|
8
|
+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
9
|
+
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
10
|
+
return extendStatics(d, b);
|
|
11
|
+
};
|
|
12
|
+
return function (d, b) {
|
|
13
|
+
if (typeof b !== "function" && b !== null)
|
|
14
|
+
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
15
|
+
extendStatics(d, b);
|
|
16
|
+
function __() { this.constructor = d; }
|
|
17
|
+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
18
|
+
};
|
|
19
|
+
})();
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.HetznerTimeoutError = exports.HetznerActionError = exports.HetznerServiceError = exports.HetznerConflictError = exports.HetznerInvalidInputError = exports.HetznerResourceLimitError = exports.HetznerResourceLockedError = exports.HetznerRateLimitError = exports.HetznerNotFoundError = exports.HetznerForbiddenError = exports.HetznerUnauthorizedError = exports.HetznerAPIError = exports.HetznerErrorCode = void 0;
|
|
22
|
+
exports.createHetznerError = createHetznerError;
|
|
23
|
+
exports.isRetryableError = isRetryableError;
|
|
24
|
+
exports.isRateLimitError = isRateLimitError;
|
|
25
|
+
exports.isResourceLockedError = isResourceLockedError;
|
|
26
|
+
exports.calculateRetryDelay = calculateRetryDelay;
|
|
27
|
+
exports.defaultErrorHandler = defaultErrorHandler;
|
|
4
28
|
// ============================================================================
|
|
5
29
|
// Error Codes
|
|
6
30
|
// ============================================================================
|
|
@@ -8,7 +32,7 @@
|
|
|
8
32
|
* Hetzner API error codes
|
|
9
33
|
* @see https://docs.hetzner.cloud/#errors
|
|
10
34
|
*/
|
|
11
|
-
|
|
35
|
+
var HetznerErrorCode;
|
|
12
36
|
(function (HetznerErrorCode) {
|
|
13
37
|
// Authentication errors
|
|
14
38
|
HetznerErrorCode["Unauthorized"] = "unauthorized";
|
|
@@ -40,162 +64,212 @@ export var HetznerErrorCode;
|
|
|
40
64
|
// Certificate errors
|
|
41
65
|
HetznerErrorCode["CertificateValidationFailed"] = "certificate_validation_failed";
|
|
42
66
|
HetznerErrorCode["CertificatePending"] = "certificate_pending";
|
|
43
|
-
})(HetznerErrorCode || (HetznerErrorCode = {}));
|
|
67
|
+
})(HetznerErrorCode || (exports.HetznerErrorCode = HetznerErrorCode = {}));
|
|
44
68
|
// ============================================================================
|
|
45
69
|
// Error Classes
|
|
46
70
|
// ============================================================================
|
|
47
71
|
/**
|
|
48
72
|
* Base Hetzner API error
|
|
49
73
|
*/
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
details
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
74
|
+
var HetznerAPIError = /** @class */ (function (_super) {
|
|
75
|
+
__extends(HetznerAPIError, _super);
|
|
76
|
+
function HetznerAPIError(message, code, details) {
|
|
77
|
+
var _this = _super.call(this, message) || this;
|
|
78
|
+
_this.code = code;
|
|
79
|
+
_this.details = details;
|
|
80
|
+
_this.name = "HetznerAPIError";
|
|
81
|
+
return _this;
|
|
58
82
|
}
|
|
59
|
-
|
|
83
|
+
return HetznerAPIError;
|
|
84
|
+
}(Error));
|
|
85
|
+
exports.HetznerAPIError = HetznerAPIError;
|
|
60
86
|
/**
|
|
61
87
|
* Authentication error (401)
|
|
62
88
|
*/
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
89
|
+
var HetznerUnauthorizedError = /** @class */ (function (_super) {
|
|
90
|
+
__extends(HetznerUnauthorizedError, _super);
|
|
91
|
+
function HetznerUnauthorizedError(message) {
|
|
92
|
+
if (message === void 0) { message = "Unauthorized: Invalid API token"; }
|
|
93
|
+
var _this = _super.call(this, message, HetznerErrorCode.Unauthorized) || this;
|
|
94
|
+
_this.name = "HetznerUnauthorizedError";
|
|
95
|
+
return _this;
|
|
67
96
|
}
|
|
68
|
-
|
|
97
|
+
return HetznerUnauthorizedError;
|
|
98
|
+
}(HetznerAPIError));
|
|
99
|
+
exports.HetznerUnauthorizedError = HetznerUnauthorizedError;
|
|
69
100
|
/**
|
|
70
101
|
* Forbidden error (403)
|
|
71
102
|
*/
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
103
|
+
var HetznerForbiddenError = /** @class */ (function (_super) {
|
|
104
|
+
__extends(HetznerForbiddenError, _super);
|
|
105
|
+
function HetznerForbiddenError(message) {
|
|
106
|
+
if (message === void 0) { message = "Forbidden: Insufficient permissions"; }
|
|
107
|
+
var _this = _super.call(this, message, HetznerErrorCode.Forbidden) || this;
|
|
108
|
+
_this.name = "HetznerForbiddenError";
|
|
109
|
+
return _this;
|
|
76
110
|
}
|
|
77
|
-
|
|
111
|
+
return HetznerForbiddenError;
|
|
112
|
+
}(HetznerAPIError));
|
|
113
|
+
exports.HetznerForbiddenError = HetznerForbiddenError;
|
|
78
114
|
/**
|
|
79
115
|
* Resource not found error (404)
|
|
80
116
|
*/
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
this.
|
|
117
|
+
var HetznerNotFoundError = /** @class */ (function (_super) {
|
|
118
|
+
__extends(HetznerNotFoundError, _super);
|
|
119
|
+
function HetznerNotFoundError(resource, id) {
|
|
120
|
+
var _this = _super.call(this, "".concat(resource, " with ID ").concat(id, " not found"), HetznerErrorCode.NotFound, { resource: resource, id: id }) || this;
|
|
121
|
+
_this.name = "HetznerNotFoundError";
|
|
122
|
+
return _this;
|
|
85
123
|
}
|
|
86
|
-
|
|
124
|
+
return HetznerNotFoundError;
|
|
125
|
+
}(HetznerAPIError));
|
|
126
|
+
exports.HetznerNotFoundError = HetznerNotFoundError;
|
|
87
127
|
/**
|
|
88
128
|
* Rate limit exceeded error (429)
|
|
89
129
|
*/
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
this.rateLimitInfo
|
|
95
|
-
|
|
130
|
+
var HetznerRateLimitError = /** @class */ (function (_super) {
|
|
131
|
+
__extends(HetznerRateLimitError, _super);
|
|
132
|
+
function HetznerRateLimitError(message, rateLimitInfo) {
|
|
133
|
+
if (message === void 0) { message = "Rate limit exceeded"; }
|
|
134
|
+
var _this = _super.call(this, message, HetznerErrorCode.RateLimitExceeded, rateLimitInfo) || this;
|
|
135
|
+
_this.rateLimitInfo = rateLimitInfo;
|
|
136
|
+
_this.name = "HetznerRateLimitError";
|
|
137
|
+
return _this;
|
|
96
138
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
139
|
+
Object.defineProperty(HetznerRateLimitError.prototype, "resetInMs", {
|
|
140
|
+
/**
|
|
141
|
+
* Get the number of milliseconds until the rate limit resets
|
|
142
|
+
*/
|
|
143
|
+
get: function () {
|
|
144
|
+
if (!this.rateLimitInfo)
|
|
145
|
+
return 60000; // Default to 1 minute
|
|
146
|
+
return Math.max(0, this.rateLimitInfo.reset * 1000 - Date.now());
|
|
147
|
+
},
|
|
148
|
+
enumerable: false,
|
|
149
|
+
configurable: true
|
|
150
|
+
});
|
|
151
|
+
Object.defineProperty(HetznerRateLimitError.prototype, "resetTime", {
|
|
152
|
+
/**
|
|
153
|
+
* Get a human-readable reset time
|
|
154
|
+
*/
|
|
155
|
+
get: function () {
|
|
156
|
+
if (!this.rateLimitInfo)
|
|
157
|
+
return "unknown";
|
|
158
|
+
return new Date(this.rateLimitInfo.reset * 1000).toISOString();
|
|
159
|
+
},
|
|
160
|
+
enumerable: false,
|
|
161
|
+
configurable: true
|
|
162
|
+
});
|
|
163
|
+
return HetznerRateLimitError;
|
|
164
|
+
}(HetznerAPIError));
|
|
165
|
+
exports.HetznerRateLimitError = HetznerRateLimitError;
|
|
114
166
|
/**
|
|
115
167
|
* Resource locked error
|
|
116
168
|
*/
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
169
|
+
var HetznerResourceLockedError = /** @class */ (function (_super) {
|
|
170
|
+
__extends(HetznerResourceLockedError, _super);
|
|
171
|
+
function HetznerResourceLockedError(resource, id, actionInProgress) {
|
|
172
|
+
var _this = _super.call(this, "".concat(resource, " ").concat(id, " is locked").concat(actionInProgress ? " by ".concat(actionInProgress) : ""), HetznerErrorCode.ResourceLocked, { resource: resource, id: id, actionInProgress: actionInProgress }) || this;
|
|
173
|
+
_this.actionInProgress = actionInProgress;
|
|
174
|
+
_this.name = "HetznerResourceLockedError";
|
|
175
|
+
return _this;
|
|
123
176
|
}
|
|
124
|
-
|
|
177
|
+
return HetznerResourceLockedError;
|
|
178
|
+
}(HetznerAPIError));
|
|
179
|
+
exports.HetznerResourceLockedError = HetznerResourceLockedError;
|
|
125
180
|
/**
|
|
126
181
|
* Resource limit exceeded error
|
|
127
182
|
*/
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
this.
|
|
183
|
+
var HetznerResourceLimitError = /** @class */ (function (_super) {
|
|
184
|
+
__extends(HetznerResourceLimitError, _super);
|
|
185
|
+
function HetznerResourceLimitError(resource, limit) {
|
|
186
|
+
var _this = _super.call(this, "Resource limit exceeded: ".concat(resource, " (limit: ").concat(limit, ")"), HetznerErrorCode.ResourceLimitExceeded, { resource: resource, limit: limit }) || this;
|
|
187
|
+
_this.name = "HetznerResourceLimitError";
|
|
188
|
+
return _this;
|
|
132
189
|
}
|
|
133
|
-
|
|
190
|
+
return HetznerResourceLimitError;
|
|
191
|
+
}(HetznerAPIError));
|
|
192
|
+
exports.HetznerResourceLimitError = HetznerResourceLimitError;
|
|
134
193
|
/**
|
|
135
194
|
* Invalid input error
|
|
136
195
|
*/
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
196
|
+
var HetznerInvalidInputError = /** @class */ (function (_super) {
|
|
197
|
+
__extends(HetznerInvalidInputError, _super);
|
|
198
|
+
function HetznerInvalidInputError(message, fields) {
|
|
199
|
+
var _this = _super.call(this, message, HetznerErrorCode.InvalidInput, { fields: fields }) || this;
|
|
200
|
+
_this.fields = fields;
|
|
201
|
+
_this.name = "HetznerInvalidInputError";
|
|
202
|
+
return _this;
|
|
143
203
|
}
|
|
144
|
-
|
|
204
|
+
return HetznerInvalidInputError;
|
|
205
|
+
}(HetznerAPIError));
|
|
206
|
+
exports.HetznerInvalidInputError = HetznerInvalidInputError;
|
|
145
207
|
/**
|
|
146
208
|
* Conflict error
|
|
147
209
|
*/
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
this.
|
|
210
|
+
var HetznerConflictError = /** @class */ (function (_super) {
|
|
211
|
+
__extends(HetznerConflictError, _super);
|
|
212
|
+
function HetznerConflictError(message, details) {
|
|
213
|
+
var _this = _super.call(this, message, HetznerErrorCode.Conflict, details) || this;
|
|
214
|
+
_this.name = "HetznerConflictError";
|
|
215
|
+
return _this;
|
|
152
216
|
}
|
|
153
|
-
|
|
217
|
+
return HetznerConflictError;
|
|
218
|
+
}(HetznerAPIError));
|
|
219
|
+
exports.HetznerConflictError = HetznerConflictError;
|
|
154
220
|
/**
|
|
155
221
|
* Service error (5xx)
|
|
156
222
|
*/
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
223
|
+
var HetznerServiceError = /** @class */ (function (_super) {
|
|
224
|
+
__extends(HetznerServiceError, _super);
|
|
225
|
+
function HetznerServiceError(message, statusCode) {
|
|
226
|
+
var _this = _super.call(this, message, HetznerErrorCode.ServiceError, { statusCode: statusCode }) || this;
|
|
227
|
+
_this.statusCode = statusCode;
|
|
228
|
+
_this.name = "HetznerServiceError";
|
|
229
|
+
return _this;
|
|
163
230
|
}
|
|
164
|
-
|
|
231
|
+
return HetznerServiceError;
|
|
232
|
+
}(HetznerAPIError));
|
|
233
|
+
exports.HetznerServiceError = HetznerServiceError;
|
|
165
234
|
/**
|
|
166
235
|
* Action failed error
|
|
167
236
|
*/
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
actionId
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
237
|
+
var HetznerActionError = /** @class */ (function (_super) {
|
|
238
|
+
__extends(HetznerActionError, _super);
|
|
239
|
+
function HetznerActionError(actionError, actionId) {
|
|
240
|
+
var _this = _super.call(this, "Action ".concat(actionId, " failed: ").concat(actionError.code, " - ").concat(actionError.message), actionError.code, { actionError: actionError, actionId: actionId }) || this;
|
|
241
|
+
_this.actionError = actionError;
|
|
242
|
+
_this.actionId = actionId;
|
|
243
|
+
_this.name = "HetznerActionError";
|
|
244
|
+
return _this;
|
|
176
245
|
}
|
|
177
|
-
|
|
246
|
+
return HetznerActionError;
|
|
247
|
+
}(HetznerAPIError));
|
|
248
|
+
exports.HetznerActionError = HetznerActionError;
|
|
178
249
|
/**
|
|
179
250
|
* Timeout error for action polling
|
|
180
251
|
*/
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
252
|
+
var HetznerTimeoutError = /** @class */ (function (_super) {
|
|
253
|
+
__extends(HetznerTimeoutError, _super);
|
|
254
|
+
function HetznerTimeoutError(actionId, timeout, lastProgress) {
|
|
255
|
+
var _this = _super.call(this, "Action ".concat(actionId, " timed out after ").concat(timeout, "ms (last progress: ").concat(lastProgress, "%)"), "timeout", { actionId: actionId, timeout: timeout, lastProgress: lastProgress }) || this;
|
|
256
|
+
_this.lastProgress = lastProgress;
|
|
257
|
+
_this.name = "HetznerTimeoutError";
|
|
258
|
+
return _this;
|
|
187
259
|
}
|
|
188
|
-
|
|
260
|
+
return HetznerTimeoutError;
|
|
261
|
+
}(HetznerAPIError));
|
|
262
|
+
exports.HetznerTimeoutError = HetznerTimeoutError;
|
|
189
263
|
// ============================================================================
|
|
190
264
|
// Error Factory
|
|
191
265
|
// ============================================================================
|
|
192
266
|
/**
|
|
193
267
|
* Parse Hetzner API error response and create appropriate error
|
|
194
268
|
*/
|
|
195
|
-
|
|
196
|
-
|
|
269
|
+
function createHetznerError(statusCode, body) {
|
|
270
|
+
var error = body.error;
|
|
197
271
|
if (!error) {
|
|
198
|
-
return new HetznerServiceError(
|
|
272
|
+
return new HetznerServiceError("HTTP ".concat(statusCode, ": ").concat(JSON.stringify(body)), statusCode);
|
|
199
273
|
}
|
|
200
274
|
switch (statusCode) {
|
|
201
275
|
case 401:
|
|
@@ -226,7 +300,7 @@ export function createHetznerError(statusCode, body) {
|
|
|
226
300
|
/**
|
|
227
301
|
* Check if an error is retryable
|
|
228
302
|
*/
|
|
229
|
-
|
|
303
|
+
function isRetryableError(error) {
|
|
230
304
|
if (error instanceof HetznerRateLimitError)
|
|
231
305
|
return true;
|
|
232
306
|
if (error instanceof HetznerResourceLockedError)
|
|
@@ -240,31 +314,32 @@ export function isRetryableError(error) {
|
|
|
240
314
|
/**
|
|
241
315
|
* Check if an error is a rate limit error
|
|
242
316
|
*/
|
|
243
|
-
|
|
317
|
+
function isRateLimitError(error) {
|
|
244
318
|
return error instanceof HetznerRateLimitError;
|
|
245
319
|
}
|
|
246
320
|
/**
|
|
247
321
|
* Check if an error is a resource locked error
|
|
248
322
|
*/
|
|
249
|
-
|
|
323
|
+
function isResourceLockedError(error) {
|
|
250
324
|
return error instanceof HetznerResourceLockedError;
|
|
251
325
|
}
|
|
252
326
|
/**
|
|
253
327
|
* Calculate retry delay with exponential backoff
|
|
254
328
|
*/
|
|
255
|
-
|
|
256
|
-
|
|
329
|
+
function calculateRetryDelay(attempt, baseDelay, maxDelay) {
|
|
330
|
+
if (baseDelay === void 0) { baseDelay = 1000; }
|
|
331
|
+
if (maxDelay === void 0) { maxDelay = 60000; }
|
|
332
|
+
var delay = baseDelay * Math.pow(2, attempt);
|
|
257
333
|
// Add jitter (±25%)
|
|
258
|
-
|
|
334
|
+
var jitter = delay * 0.25 * (Math.random() * 2 - 1);
|
|
259
335
|
return Math.min(maxDelay, delay + jitter);
|
|
260
336
|
}
|
|
261
337
|
/**
|
|
262
338
|
* Default error handler that logs to console
|
|
263
339
|
*/
|
|
264
|
-
|
|
265
|
-
console.error(
|
|
340
|
+
function defaultErrorHandler(error) {
|
|
341
|
+
console.error("[Hetzner API Error] ".concat(error.name, ": ").concat(error.message));
|
|
266
342
|
if (error.details) {
|
|
267
343
|
console.error("Details:", error.details);
|
|
268
344
|
}
|
|
269
345
|
}
|
|
270
|
-
//# sourceMappingURL=errors.js.map
|
package/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* Hetzner Cloud API client
|
|
3
4
|
* For server-side use only (requires API token)
|
|
@@ -6,23 +7,67 @@
|
|
|
6
7
|
* - Certificate actions: https://docs.hetzner.cloud/reference/cloud#certificate-actions
|
|
7
8
|
* - DNS operations
|
|
8
9
|
*/
|
|
10
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
13
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
14
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
15
|
+
}
|
|
16
|
+
Object.defineProperty(o, k2, desc);
|
|
17
|
+
}) : (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
o[k2] = m[k];
|
|
20
|
+
}));
|
|
21
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
22
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
23
|
+
};
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.ACTION_TIMEOUTS = exports.createProgressLogger = exports.waitForRateLimitReset = exports.formatRateLimitStatus = exports.isRateLimitLow = exports.parseRateLimitHeaders = exports.waitForActionAdaptive = exports.getAdaptivePollInterval = exports.getPollInterval = exports.getActionDescription = exports.formatActionProgress = exports.isActionError = exports.isActionSuccess = exports.isActionRunning = exports.getActionTimeout = exports.batchCheckActions = exports.waitForMultipleActionsWithLimit = exports.waitForMultipleActions = exports.waitForAction = exports.HETZNER_API_BASE = exports.resolveApiToken = exports.isAuthenticated = exports.getTokenFromCLI = exports.SSHKeyOperations = exports.ActionOperations = exports.VolumeOperations = exports.ServerOperations = exports.HetznerClient = void 0;
|
|
9
26
|
// Core exports
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
27
|
+
var client_js_1 = require("./client.js");
|
|
28
|
+
Object.defineProperty(exports, "HetznerClient", { enumerable: true, get: function () { return client_js_1.HetznerClient; } });
|
|
29
|
+
var servers_js_1 = require("./servers.js");
|
|
30
|
+
Object.defineProperty(exports, "ServerOperations", { enumerable: true, get: function () { return servers_js_1.ServerOperations; } });
|
|
31
|
+
var volumes_js_1 = require("./volumes.js");
|
|
32
|
+
Object.defineProperty(exports, "VolumeOperations", { enumerable: true, get: function () { return volumes_js_1.VolumeOperations; } });
|
|
33
|
+
var actions_js_1 = require("./actions.js");
|
|
34
|
+
Object.defineProperty(exports, "ActionOperations", { enumerable: true, get: function () { return actions_js_1.ActionOperations; } });
|
|
35
|
+
var ssh_keys_js_1 = require("./ssh-keys.js");
|
|
36
|
+
Object.defineProperty(exports, "SSHKeyOperations", { enumerable: true, get: function () { return ssh_keys_js_1.SSHKeyOperations; } });
|
|
14
37
|
// Auth
|
|
15
|
-
|
|
38
|
+
var auth_js_1 = require("./auth.js");
|
|
39
|
+
Object.defineProperty(exports, "getTokenFromCLI", { enumerable: true, get: function () { return auth_js_1.getTokenFromCLI; } });
|
|
40
|
+
Object.defineProperty(exports, "isAuthenticated", { enumerable: true, get: function () { return auth_js_1.isAuthenticated; } });
|
|
41
|
+
Object.defineProperty(exports, "resolveApiToken", { enumerable: true, get: function () { return auth_js_1.resolveApiToken; } });
|
|
16
42
|
// Config
|
|
17
|
-
|
|
43
|
+
var config_js_1 = require("./config.js");
|
|
44
|
+
Object.defineProperty(exports, "HETZNER_API_BASE", { enumerable: true, get: function () { return config_js_1.HETZNER_API_BASE; } });
|
|
18
45
|
// Types
|
|
19
|
-
|
|
46
|
+
__exportStar(require("./types.js"), exports);
|
|
20
47
|
// Schemas
|
|
21
|
-
|
|
48
|
+
__exportStar(require("./schemas.js"), exports);
|
|
22
49
|
// Errors
|
|
23
|
-
|
|
50
|
+
__exportStar(require("./errors.js"), exports);
|
|
24
51
|
// Action utilities
|
|
25
|
-
|
|
52
|
+
var actions_js_2 = require("./actions.js");
|
|
53
|
+
Object.defineProperty(exports, "waitForAction", { enumerable: true, get: function () { return actions_js_2.waitForAction; } });
|
|
54
|
+
Object.defineProperty(exports, "waitForMultipleActions", { enumerable: true, get: function () { return actions_js_2.waitForMultipleActions; } });
|
|
55
|
+
Object.defineProperty(exports, "waitForMultipleActionsWithLimit", { enumerable: true, get: function () { return actions_js_2.waitForMultipleActionsWithLimit; } });
|
|
56
|
+
Object.defineProperty(exports, "batchCheckActions", { enumerable: true, get: function () { return actions_js_2.batchCheckActions; } });
|
|
57
|
+
Object.defineProperty(exports, "getActionTimeout", { enumerable: true, get: function () { return actions_js_2.getActionTimeout; } });
|
|
58
|
+
Object.defineProperty(exports, "isActionRunning", { enumerable: true, get: function () { return actions_js_2.isActionRunning; } });
|
|
59
|
+
Object.defineProperty(exports, "isActionSuccess", { enumerable: true, get: function () { return actions_js_2.isActionSuccess; } });
|
|
60
|
+
Object.defineProperty(exports, "isActionError", { enumerable: true, get: function () { return actions_js_2.isActionError; } });
|
|
61
|
+
Object.defineProperty(exports, "formatActionProgress", { enumerable: true, get: function () { return actions_js_2.formatActionProgress; } });
|
|
62
|
+
Object.defineProperty(exports, "getActionDescription", { enumerable: true, get: function () { return actions_js_2.getActionDescription; } });
|
|
63
|
+
Object.defineProperty(exports, "getPollInterval", { enumerable: true, get: function () { return actions_js_2.getPollInterval; } });
|
|
64
|
+
Object.defineProperty(exports, "getAdaptivePollInterval", { enumerable: true, get: function () { return actions_js_2.getAdaptivePollInterval; } });
|
|
65
|
+
Object.defineProperty(exports, "waitForActionAdaptive", { enumerable: true, get: function () { return actions_js_2.waitForActionAdaptive; } });
|
|
66
|
+
Object.defineProperty(exports, "parseRateLimitHeaders", { enumerable: true, get: function () { return actions_js_2.parseRateLimitHeaders; } });
|
|
67
|
+
Object.defineProperty(exports, "isRateLimitLow", { enumerable: true, get: function () { return actions_js_2.isRateLimitLow; } });
|
|
68
|
+
Object.defineProperty(exports, "formatRateLimitStatus", { enumerable: true, get: function () { return actions_js_2.formatRateLimitStatus; } });
|
|
69
|
+
Object.defineProperty(exports, "waitForRateLimitReset", { enumerable: true, get: function () { return actions_js_2.waitForRateLimitReset; } });
|
|
70
|
+
Object.defineProperty(exports, "createProgressLogger", { enumerable: true, get: function () { return actions_js_2.createProgressLogger; } });
|
|
71
|
+
Object.defineProperty(exports, "ACTION_TIMEOUTS", { enumerable: true, get: function () { return actions_js_2.ACTION_TIMEOUTS; } });
|
|
26
72
|
// Bootstrap security modules
|
|
27
|
-
|
|
28
|
-
//# sourceMappingURL=index.js.map
|
|
73
|
+
__exportStar(require("./bootstrap/index.js"), exports);
|
package/index.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// Core exports
|
|
11
11
|
export { HetznerClient } from "./client.js";
|
|
12
12
|
export { ServerOperations } from "./servers.js";
|
|
13
|
+
export { VolumeOperations } from "./volumes.js";
|
|
13
14
|
export { ActionOperations } from "./actions.js";
|
|
14
15
|
export { SSHKeyOperations } from "./ssh-keys.js";
|
|
15
16
|
|
|
@@ -53,3 +54,6 @@ export {
|
|
|
53
54
|
|
|
54
55
|
// Bootstrap security modules
|
|
55
56
|
export * from "./bootstrap/index.js";
|
|
57
|
+
|
|
58
|
+
// Onboarding (Phase 2: post-boot configuration)
|
|
59
|
+
export * from "./onboarding/index.js";
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Doppler Onboarding
|
|
3
|
+
*
|
|
4
|
+
* Configure Doppler CLI service token on remote servers.
|
|
5
|
+
* This enables doppler run to inject secrets into processes.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { SSHOptions } from "@ebowwa/terminal/types";
|
|
9
|
+
|
|
10
|
+
export interface DopplerConfig {
|
|
11
|
+
token: string;
|
|
12
|
+
project?: string;
|
|
13
|
+
config?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Configure Doppler on a remote server
|
|
18
|
+
*
|
|
19
|
+
* @param host - Server IP or hostname
|
|
20
|
+
* @param config - Doppler configuration
|
|
21
|
+
* @param sshOptions - SSH options
|
|
22
|
+
* @returns Success status
|
|
23
|
+
*/
|
|
24
|
+
export async function onboardDoppler(
|
|
25
|
+
host: string,
|
|
26
|
+
config: DopplerConfig,
|
|
27
|
+
sshOptions: Partial<SSHOptions> = {}
|
|
28
|
+
): Promise<{ success: boolean; message: string }> {
|
|
29
|
+
const { execSSH } = await import("@ebowwa/terminal/client");
|
|
30
|
+
|
|
31
|
+
const { token, project = "seed", config: dopplerConfig = "prd" } = config;
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
// Step 1: Configure Doppler service token
|
|
35
|
+
const configureCmd = [
|
|
36
|
+
`doppler configure set token ${token}`,
|
|
37
|
+
`doppler configure set project ${project}`,
|
|
38
|
+
`doppler configure set config ${dopplerConfig}`,
|
|
39
|
+
].join(" && ");
|
|
40
|
+
|
|
41
|
+
await execSSH(configureCmd, {
|
|
42
|
+
host,
|
|
43
|
+
user: "root",
|
|
44
|
+
...sshOptions
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Step 2: Verify configuration by fetching a secret
|
|
48
|
+
const verifyCmd = `doppler secrets get --config ${dopplerConfig} --project ${project} --json 2>/dev/null | head -5`;
|
|
49
|
+
|
|
50
|
+
await execSSH(verifyCmd, {
|
|
51
|
+
host,
|
|
52
|
+
user: "root",
|
|
53
|
+
timeout: 10000,
|
|
54
|
+
...sshOptions
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
success: true,
|
|
59
|
+
message: `Doppler configured for project ${project}/${dopplerConfig}`
|
|
60
|
+
};
|
|
61
|
+
} catch (error) {
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
message: `Doppler configuration failed: ${error instanceof Error ? error.message : String(error)}`
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Check Doppler status on a remote server
|
|
71
|
+
*
|
|
72
|
+
* @param host - Server IP or hostname
|
|
73
|
+
* @param sshOptions - SSH options
|
|
74
|
+
* @returns Doppler status
|
|
75
|
+
*/
|
|
76
|
+
export async function checkDopplerStatus(
|
|
77
|
+
host: string,
|
|
78
|
+
sshOptions: Partial<SSHOptions> = {}
|
|
79
|
+
): Promise<{ configured: boolean; project?: string; config?: string; message: string }> {
|
|
80
|
+
const { execSSH } = await import("@ebowwa/terminal/client");
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
// Check if doppler is configured
|
|
84
|
+
const checkCmd = `
|
|
85
|
+
doppler configure get project 2>/dev/null || echo "NOT_CONFIGURED"
|
|
86
|
+
doppler configure get config 2>/dev/null || echo "NOT_CONFIGURED"
|
|
87
|
+
`;
|
|
88
|
+
|
|
89
|
+
const result = await execSSH(checkCmd, {
|
|
90
|
+
host,
|
|
91
|
+
user: "root",
|
|
92
|
+
timeout: 5000,
|
|
93
|
+
...sshOptions
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const lines = result.trim().split("\n");
|
|
97
|
+
const project = lines[0]?.trim() || "";
|
|
98
|
+
const config = lines[1]?.trim() || "";
|
|
99
|
+
|
|
100
|
+
const configured = project !== "NOT_CONFIGURED" && config !== "NOT_CONFIGURED";
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
configured,
|
|
104
|
+
project: configured ? project : undefined,
|
|
105
|
+
config: configured ? config : undefined,
|
|
106
|
+
message: configured
|
|
107
|
+
? `Doppler configured: ${project}/${config}`
|
|
108
|
+
: "Doppler not configured"
|
|
109
|
+
};
|
|
110
|
+
} catch (error) {
|
|
111
|
+
return {
|
|
112
|
+
configured: false,
|
|
113
|
+
message: `Could not check Doppler status: ${error instanceof Error ? error.message : String(error)}`
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|