@peerbit/server 6.0.42 → 7.0.1
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/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +161 -352
- package/dist/src/cli.js.map +1 -1
- package/dist/src/client.d.ts +1 -1
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/client.js +2 -5
- package/dist/src/client.js.map +1 -1
- package/dist/src/docker.d.ts +70 -1
- package/dist/src/docker.d.ts.map +1 -1
- package/dist/src/docker.js +559 -14
- package/dist/src/docker.js.map +1 -1
- package/dist/src/domain-lease.d.ts +126 -0
- package/dist/src/domain-lease.d.ts.map +1 -0
- package/dist/src/domain-lease.js +800 -0
- package/dist/src/domain-lease.js.map +1 -0
- package/dist/src/domain.d.ts +50 -2
- package/dist/src/domain.d.ts.map +1 -1
- package/dist/src/domain.js +484 -67
- package/dist/src/domain.js.map +1 -1
- package/dist/src/hetzner.d.ts +4 -21
- package/dist/src/hetzner.d.ts.map +1 -1
- package/dist/src/hetzner.js +4 -116
- package/dist/src/hetzner.js.map +1 -1
- package/dist/src/nginx-template.conf +19 -1
- package/dist/src/remotes.browser.d.ts +4 -1
- package/dist/src/remotes.browser.d.ts.map +1 -1
- package/dist/src/remotes.browser.js +1 -2
- package/dist/src/remotes.browser.js.map +1 -1
- package/dist/src/remotes.d.ts +2 -1
- package/dist/src/remotes.d.ts.map +1 -1
- package/dist/src/remotes.js +1 -0
- package/dist/src/remotes.js.map +1 -1
- package/dist/src/server.d.ts.map +1 -1
- package/dist/src/server.js +3 -5
- package/dist/src/server.js.map +1 -1
- package/dist/test/api.spec.js +2 -1
- package/dist/test/api.spec.js.map +1 -1
- package/dist/test/cli.spec.js +48 -1
- package/dist/test/cli.spec.js.map +1 -1
- package/dist/test/docker.spec.d.ts +2 -0
- package/dist/test/docker.spec.d.ts.map +1 -0
- package/dist/test/docker.spec.js +822 -0
- package/dist/test/docker.spec.js.map +1 -0
- package/dist/test/domain-lease.spec.d.ts +2 -0
- package/dist/test/domain-lease.spec.d.ts.map +1 -0
- package/dist/test/domain-lease.spec.js +530 -0
- package/dist/test/domain-lease.spec.js.map +1 -0
- package/dist/test/domain.integration.spec.js +358 -75
- package/dist/test/domain.integration.spec.js.map +1 -1
- package/dist/test/remotes.spec.d.ts +2 -0
- package/dist/test/remotes.spec.d.ts.map +1 -0
- package/dist/test/remotes.spec.js +15 -0
- package/dist/test/remotes.spec.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/ui/assets/index-CjZwCewh.js +10 -0
- package/dist/ui/assets/index-e3g42MbC.js +9 -0
- package/dist/ui/index.html +1 -1
- package/package.json +14 -19
- package/src/cli.ts +204 -408
- package/src/client.ts +2 -6
- package/src/docker.ts +841 -20
- package/src/domain-lease.ts +1118 -0
- package/src/domain.ts +650 -90
- package/src/hetzner.ts +4 -184
- package/src/nginx-template.conf +19 -1
- package/src/remotes.browser.ts +7 -1
- package/src/remotes.ts +6 -1
- package/src/server.ts +3 -5
- package/dist/src/aws.browser.d.ts +0 -2
- package/dist/src/aws.browser.d.ts.map +0 -1
- package/dist/src/aws.browser.js +0 -3
- package/dist/src/aws.browser.js.map +0 -1
- package/dist/src/aws.d.ts +0 -30
- package/dist/src/aws.d.ts.map +0 -1
- package/dist/src/aws.js +0 -240
- package/dist/src/aws.js.map +0 -1
- package/dist/test/launch.spec.d.ts +0 -2
- package/dist/test/launch.spec.d.ts.map +0 -1
- package/dist/test/launch.spec.js +0 -23
- package/dist/test/launch.spec.js.map +0 -1
- package/dist/ui/assets/index-BBTKOM0z.js +0 -6
- package/dist/ui/assets/index-DVtj_GOf.js +0 -13
- package/src/aws.browser.ts +0 -1
- package/src/aws.ts +0 -327
package/src/cli.ts
CHANGED
|
@@ -7,17 +7,10 @@ import chalk from "chalk";
|
|
|
7
7
|
import fs from "fs";
|
|
8
8
|
import sodium from "libsodium-wrappers";
|
|
9
9
|
import path from "path";
|
|
10
|
-
import { exit } from "process";
|
|
11
10
|
import readline from "readline";
|
|
12
11
|
import Table from "tty-table";
|
|
13
12
|
import type { Argv } from "yargs";
|
|
14
|
-
import {
|
|
15
|
-
AWS_LINUX_ARM_AMIs,
|
|
16
|
-
createRecord,
|
|
17
|
-
launchNodes,
|
|
18
|
-
terminateNode,
|
|
19
|
-
} from "./aws.js";
|
|
20
|
-
import { createClient, waitForDomain } from "./client.js";
|
|
13
|
+
import { createClient } from "./client.js";
|
|
21
14
|
import {
|
|
22
15
|
getHomeConfigDir,
|
|
23
16
|
getKeypair,
|
|
@@ -25,17 +18,24 @@ import {
|
|
|
25
18
|
getRemotesPath,
|
|
26
19
|
} from "./config.js";
|
|
27
20
|
import {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
21
|
+
DNS_LEASE_ACCESS_TOKEN_ENV,
|
|
22
|
+
DNS_LEASE_SERVICE_URL_ENV,
|
|
23
|
+
DNS_LEASE_STATE_FILE_ENV,
|
|
24
|
+
getDnsLeaseStatePath,
|
|
25
|
+
provisionDnsLease,
|
|
26
|
+
readDnsLeaseState,
|
|
27
|
+
releaseDnsLease,
|
|
28
|
+
renewDnsLease,
|
|
29
|
+
startDnsLeaseRenewal,
|
|
30
|
+
} from "./domain-lease.js";
|
|
31
|
+
import { getDomainFromConfig, loadConfig, startCertbot } from "./domain.js";
|
|
32
|
+
import { terminateNode as terminateHetznerNode } from "./hetzner.js";
|
|
33
33
|
import {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
DEFAULT_REMOTE_GROUP,
|
|
35
|
+
type RemoteObject,
|
|
36
|
+
Remotes,
|
|
37
|
+
getRetiredAWSManagementError,
|
|
38
|
+
} from "./remotes.js";
|
|
39
39
|
import { LOCAL_API_PORT } from "./routes.js";
|
|
40
40
|
import { startServerWithNode } from "./server.js";
|
|
41
41
|
import type { InstallDependency, StartProgram } from "./types.js";
|
|
@@ -178,20 +178,43 @@ export const cli = async (args?: string[]) => {
|
|
|
178
178
|
"Set Libp2p listen port. Only modify this when testing locally, since NGINX config depends on the default value",
|
|
179
179
|
type: "number",
|
|
180
180
|
default: undefined,
|
|
181
|
+
})
|
|
182
|
+
.option("dns-lease-state-file", {
|
|
183
|
+
describe: `Managed DNS lease state file (or ${DNS_LEASE_STATE_FILE_ENV})`,
|
|
184
|
+
type: "string",
|
|
181
185
|
});
|
|
182
186
|
return yargs;
|
|
183
187
|
},
|
|
184
188
|
handler: async (args) => {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
189
|
+
let stopDnsLeaseRenewal: () => void = () => undefined;
|
|
190
|
+
try {
|
|
191
|
+
stopDnsLeaseRenewal = await startDnsLeaseRenewal({
|
|
192
|
+
statePath:
|
|
193
|
+
(args["dns-lease-state-file"] as string | undefined) ||
|
|
194
|
+
process.env[DNS_LEASE_STATE_FILE_ENV],
|
|
195
|
+
onError: (message) => console.warn(message),
|
|
196
|
+
});
|
|
197
|
+
} catch {
|
|
198
|
+
console.warn(
|
|
199
|
+
"Managed DNS lease renewal could not start; use 'peerbit domain lease renew' to inspect it",
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
try {
|
|
203
|
+
const started = await startServerWithNode({
|
|
204
|
+
directory: args.directory,
|
|
205
|
+
domain: await loadConfig().then((config) =>
|
|
206
|
+
config ? getDomainFromConfig(config) : undefined,
|
|
207
|
+
),
|
|
208
|
+
ports: { api: args["port-api"], node: args["port-node"] },
|
|
209
|
+
bootstrap: args.bootstrap,
|
|
210
|
+
newSession: args.reset,
|
|
211
|
+
grantAccess: args["grant-access"],
|
|
212
|
+
});
|
|
213
|
+
started.server.once("close", stopDnsLeaseRenewal);
|
|
214
|
+
} catch (error) {
|
|
215
|
+
stopDnsLeaseRenewal();
|
|
216
|
+
throw error;
|
|
217
|
+
}
|
|
195
218
|
},
|
|
196
219
|
})
|
|
197
220
|
.command({
|
|
@@ -214,103 +237,153 @@ export const cli = async (args?: string[]) => {
|
|
|
214
237
|
})
|
|
215
238
|
.command(
|
|
216
239
|
"domain",
|
|
217
|
-
"
|
|
240
|
+
"Configure a domain and certificate for this node",
|
|
218
241
|
(yargs) => {
|
|
219
242
|
yargs
|
|
220
243
|
.command({
|
|
221
|
-
command: "
|
|
222
|
-
describe:
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
244
|
+
command: "lease [operation]",
|
|
245
|
+
describe: "Manage a temporary peerchecker.com DNS lease",
|
|
246
|
+
builder: (leaseArgs: Argv) =>
|
|
247
|
+
leaseArgs
|
|
248
|
+
.positional("operation", {
|
|
249
|
+
choices: ["claim", "renew", "release", "status"] as const,
|
|
250
|
+
default: "claim",
|
|
251
|
+
describe: "Lease operation",
|
|
252
|
+
type: "string",
|
|
253
|
+
})
|
|
254
|
+
.option("service-url", {
|
|
255
|
+
describe: `Lease service URL (or ${DNS_LEASE_SERVICE_URL_ENV})`,
|
|
256
|
+
type: "string",
|
|
257
|
+
})
|
|
258
|
+
.option("access-token", {
|
|
259
|
+
describe: `Claim access token (prefer ${DNS_LEASE_ACCESS_TOKEN_ENV} to avoid shell history)`,
|
|
260
|
+
type: "string",
|
|
261
|
+
})
|
|
262
|
+
.option("address", {
|
|
263
|
+
describe: "Public IPv4 or IPv6 address for a new lease",
|
|
264
|
+
type: "string",
|
|
265
|
+
})
|
|
266
|
+
.option("email", {
|
|
267
|
+
describe: "Email for Let's Encrypt security messages",
|
|
268
|
+
type: "string",
|
|
269
|
+
})
|
|
270
|
+
.option("configure", {
|
|
271
|
+
default: true,
|
|
272
|
+
describe: "Configure NGINX and Let's Encrypt after claiming",
|
|
273
|
+
type: "boolean",
|
|
274
|
+
})
|
|
275
|
+
.option("wait", {
|
|
276
|
+
alias: "w",
|
|
277
|
+
default: true,
|
|
278
|
+
describe: "Wait for HTTPS setup to succeed",
|
|
279
|
+
type: "boolean",
|
|
280
|
+
})
|
|
281
|
+
.option("state-file", {
|
|
282
|
+
default: getDnsLeaseStatePath(),
|
|
283
|
+
describe: "Managed DNS lease state file",
|
|
284
|
+
type: "string",
|
|
285
|
+
}),
|
|
242
286
|
handler: async (args) => {
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
287
|
+
const operation = args.operation || "claim";
|
|
288
|
+
const statePath = args["state-file"] as string;
|
|
289
|
+
const serviceUrl =
|
|
290
|
+
(args["service-url"] as string | undefined) ||
|
|
291
|
+
process.env[DNS_LEASE_SERVICE_URL_ENV];
|
|
292
|
+
if (operation === "status") {
|
|
293
|
+
const state = readDnsLeaseState(statePath);
|
|
294
|
+
console.log(
|
|
295
|
+
JSON.stringify(
|
|
296
|
+
state
|
|
297
|
+
? {
|
|
298
|
+
address: state.address,
|
|
299
|
+
configuredAt: state.configuredAt,
|
|
300
|
+
domain: state.domain,
|
|
301
|
+
expiresAt: state.expiresAt,
|
|
302
|
+
status: state.status,
|
|
303
|
+
}
|
|
304
|
+
: { status: "none" },
|
|
305
|
+
undefined,
|
|
306
|
+
2,
|
|
307
|
+
),
|
|
308
|
+
);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (operation === "renew") {
|
|
312
|
+
const active = await renewDnsLease({ statePath, serviceUrl });
|
|
313
|
+
console.log(`DNS lease renewed until ${active.expiresAt}`);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
if (operation === "release") {
|
|
317
|
+
await releaseDnsLease({ statePath, serviceUrl });
|
|
318
|
+
console.log("DNS lease released");
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
if (args.configure && !args.email) {
|
|
322
|
+
throw new Error(
|
|
323
|
+
"--email is required when configuring the leased domain",
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
const active = await provisionDnsLease({
|
|
327
|
+
accessToken:
|
|
328
|
+
(args["access-token"] as string | undefined) ||
|
|
329
|
+
process.env[DNS_LEASE_ACCESS_TOKEN_ENV],
|
|
330
|
+
address: args.address as string | undefined,
|
|
331
|
+
configure: args.configure
|
|
332
|
+
? (domain) =>
|
|
333
|
+
startCertbot(domain, args.email as string, args.wait)
|
|
334
|
+
: undefined,
|
|
335
|
+
serviceUrl,
|
|
336
|
+
statePath,
|
|
337
|
+
});
|
|
338
|
+
console.log(`DNS lease active until ${active.expiresAt}`);
|
|
246
339
|
},
|
|
247
340
|
})
|
|
248
341
|
.command({
|
|
249
|
-
command: "
|
|
342
|
+
command: "configure <domain>",
|
|
250
343
|
describe:
|
|
251
|
-
"
|
|
344
|
+
"Configure NGINX and Let's Encrypt for a domain you control",
|
|
252
345
|
builder: {
|
|
253
346
|
domain: {
|
|
254
|
-
describe:
|
|
255
|
-
|
|
347
|
+
describe:
|
|
348
|
+
"Domain whose DNS record already points to this server",
|
|
256
349
|
type: "string",
|
|
257
350
|
demandOption: true,
|
|
258
351
|
},
|
|
259
|
-
|
|
260
|
-
describe:
|
|
261
|
-
alias: "hz",
|
|
262
|
-
type: "string",
|
|
263
|
-
require: true,
|
|
264
|
-
},
|
|
265
|
-
accessKeyId: {
|
|
266
|
-
describe: "Access key id of the AWS user",
|
|
267
|
-
alias: "ak",
|
|
268
|
-
type: "string",
|
|
269
|
-
},
|
|
270
|
-
region: {
|
|
271
|
-
describe: "AWS region",
|
|
272
|
-
alias: "r",
|
|
273
|
-
type: "string",
|
|
274
|
-
},
|
|
275
|
-
secretAccessKey: {
|
|
276
|
-
describe: "Secret key id of the AWS user",
|
|
277
|
-
alias: "sk",
|
|
278
|
-
type: "string",
|
|
279
|
-
},
|
|
280
|
-
outdir: {
|
|
281
|
-
describe: "Output path for Nginx config",
|
|
352
|
+
email: {
|
|
353
|
+
describe: "Email for Let's Encrypt security messages",
|
|
282
354
|
type: "string",
|
|
283
|
-
|
|
355
|
+
demandOption: true,
|
|
284
356
|
},
|
|
285
357
|
wait: {
|
|
286
358
|
alias: "w",
|
|
287
|
-
describe:
|
|
359
|
+
describe:
|
|
360
|
+
"Wait for HTTPS setup to succeed (use --no-wait to return early)",
|
|
288
361
|
type: "boolean",
|
|
289
|
-
default:
|
|
362
|
+
default: true,
|
|
290
363
|
},
|
|
291
364
|
},
|
|
292
365
|
handler: async (args) => {
|
|
293
|
-
if (
|
|
294
|
-
!!args.accessKeyId !== !!args.secretAccessKey ||
|
|
295
|
-
!!args.region !== !!args.secretAccessKey
|
|
296
|
-
) {
|
|
297
|
-
throw new Error(
|
|
298
|
-
"Expecting either all 'accessKeyId', 'region' and 'secretAccessKey' to be provided or none",
|
|
299
|
-
);
|
|
300
|
-
}
|
|
301
|
-
await createRecord({
|
|
302
|
-
domain: args.domain,
|
|
303
|
-
hostedZoneId: args.hostedZoneId,
|
|
304
|
-
region: args.region,
|
|
305
|
-
credentials: args.accessKeyId
|
|
306
|
-
? {
|
|
307
|
-
accessKeyId: args.accessKeyId,
|
|
308
|
-
secretAccessKey: args.secretAccessKey,
|
|
309
|
-
}
|
|
310
|
-
: undefined,
|
|
311
|
-
});
|
|
312
366
|
await startCertbot(args.domain, args.email, args.wait);
|
|
313
|
-
|
|
367
|
+
},
|
|
368
|
+
})
|
|
369
|
+
.command({
|
|
370
|
+
command: "test",
|
|
371
|
+
describe: false,
|
|
372
|
+
builder: (retiredArgs: Argv) => retiredArgs.strict(false),
|
|
373
|
+
handler: () => {
|
|
374
|
+
throw new Error(
|
|
375
|
+
"Automatic test domains have been retired. Point a domain you control to this server, then run 'peerbit domain configure <domain> --email <email>'.",
|
|
376
|
+
);
|
|
377
|
+
},
|
|
378
|
+
})
|
|
379
|
+
.command({
|
|
380
|
+
command: "aws",
|
|
381
|
+
describe: false,
|
|
382
|
+
builder: (retiredArgs: Argv) => retiredArgs.strict(false),
|
|
383
|
+
handler: () => {
|
|
384
|
+
throw new Error(
|
|
385
|
+
"Automatic AWS DNS configuration has been retired. Configure DNS with your provider, then run 'peerbit domain configure <domain> --email <email>'.",
|
|
386
|
+
);
|
|
314
387
|
},
|
|
315
388
|
})
|
|
316
389
|
.strict()
|
|
@@ -319,301 +392,21 @@ export const cli = async (args?: string[]) => {
|
|
|
319
392
|
)
|
|
320
393
|
.command("remote", "Handle remote nodes", (innerYargs) => {
|
|
321
394
|
innerYargs
|
|
322
|
-
.command(
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
alias: "c",
|
|
333
|
-
default: 1,
|
|
334
|
-
});
|
|
335
|
-
awsArgs.option("region", {
|
|
336
|
-
describe: "Region",
|
|
337
|
-
type: "string",
|
|
338
|
-
defaultDescription: "Region defined in ~.aws/config",
|
|
339
|
-
choices: Object.keys(AWS_LINUX_ARM_AMIs),
|
|
340
|
-
});
|
|
341
|
-
awsArgs.option("group", {
|
|
342
|
-
describe: "Remote group to launch nodes in",
|
|
343
|
-
type: "string",
|
|
344
|
-
alias: "g",
|
|
345
|
-
default: DEFAULT_REMOTE_GROUP,
|
|
346
|
-
});
|
|
347
|
-
awsArgs.option("size", {
|
|
348
|
-
describe: "Instance size",
|
|
349
|
-
type: "string",
|
|
350
|
-
alias: "s",
|
|
351
|
-
choices: [
|
|
352
|
-
"micro",
|
|
353
|
-
"small",
|
|
354
|
-
"medium",
|
|
355
|
-
"large",
|
|
356
|
-
"xlarge",
|
|
357
|
-
"2xlarge",
|
|
358
|
-
],
|
|
359
|
-
default: "micro",
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
awsArgs.option("name", {
|
|
363
|
-
describe: "Name prefix for spawned nodes",
|
|
364
|
-
type: "string",
|
|
365
|
-
alias: "n",
|
|
366
|
-
default: "peerbit-node",
|
|
367
|
-
});
|
|
368
|
-
awsArgs.option("grant-access", {
|
|
369
|
-
describe: "Grant access to public keys on start",
|
|
370
|
-
defaultDescription:
|
|
371
|
-
"The publickey of this device located in 'directory'",
|
|
372
|
-
type: "string",
|
|
373
|
-
array: true,
|
|
374
|
-
alias: "ga",
|
|
375
|
-
});
|
|
376
|
-
awsArgs.option("directory", {
|
|
377
|
-
describe: "Peerbit directory",
|
|
378
|
-
defaultDescription: "~.peerbit",
|
|
379
|
-
type: "string",
|
|
380
|
-
alias: "d",
|
|
381
|
-
default: getHomeConfigDir(),
|
|
382
|
-
});
|
|
383
|
-
awsArgs.option("email", {
|
|
384
|
-
describe: "Email for Let's security messages",
|
|
385
|
-
type: "string",
|
|
386
|
-
alias: "e",
|
|
387
|
-
demandOption: true,
|
|
388
|
-
});
|
|
389
|
-
awsArgs.option("server-version", {
|
|
390
|
-
describe:
|
|
391
|
-
"@peerbit/server version or tag to install on the instance (e.g. 5.7.0-58d3d09)",
|
|
392
|
-
type: "string",
|
|
393
|
-
alias: ["sv"],
|
|
394
|
-
});
|
|
395
|
-
return awsArgs;
|
|
396
|
-
},
|
|
397
|
-
handler: async (args) => {
|
|
398
|
-
const self = (
|
|
399
|
-
await getKeypair(args.directory)
|
|
400
|
-
).publicKey.toPeerId();
|
|
401
|
-
const accessGrant: PeerId[] =
|
|
402
|
-
args["grant-access"]?.length > 0
|
|
403
|
-
? (args["grant-access"] as string[]).map((x) =>
|
|
404
|
-
peerIdFromString(x),
|
|
405
|
-
)
|
|
406
|
-
: [];
|
|
407
|
-
accessGrant.push(self);
|
|
408
|
-
const nodes = await launchNodes({
|
|
409
|
-
email: args.email as string,
|
|
410
|
-
count: args.count,
|
|
411
|
-
namePrefix: args.name,
|
|
412
|
-
region: args.region,
|
|
413
|
-
grantAccess: accessGrant,
|
|
414
|
-
size: args.size,
|
|
415
|
-
serverVersion:
|
|
416
|
-
(args["server-version"] as string) || undefined,
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
console.log(
|
|
420
|
-
`Waiting for ${args.count} ${
|
|
421
|
-
args.count > 1 ? "nodes" : "node"
|
|
422
|
-
} to spawn. This might take a few minutes. You can watch the progress in your AWS console.`,
|
|
423
|
-
);
|
|
424
|
-
const twirlTimer = (function () {
|
|
425
|
-
const P = ["\\", "|", "/", "-"];
|
|
426
|
-
let x = 0;
|
|
427
|
-
return setInterval(function () {
|
|
428
|
-
process.stdout.write(
|
|
429
|
-
"\r" + "Loading: " + chalk.hex(colors[x])(P[x++]),
|
|
430
|
-
);
|
|
431
|
-
x &= 3;
|
|
432
|
-
}, 250);
|
|
433
|
-
})();
|
|
434
|
-
for (const node of nodes) {
|
|
435
|
-
try {
|
|
436
|
-
const domain = await waitForDomain(node.publicIp);
|
|
437
|
-
const remotes = new Remotes(getRemotesPath(args.directory));
|
|
438
|
-
remotes.add({
|
|
439
|
-
name: node.name,
|
|
440
|
-
address: domain,
|
|
441
|
-
group: args.group,
|
|
442
|
-
origin: {
|
|
443
|
-
type: "aws",
|
|
444
|
-
instanceId: node.instanceId,
|
|
445
|
-
region: node.region,
|
|
446
|
-
},
|
|
447
|
-
});
|
|
448
|
-
} catch (error: any) {
|
|
449
|
-
process.stdout.write("\r");
|
|
450
|
-
console.error(
|
|
451
|
-
`Error waiting for domain for ip: ${
|
|
452
|
-
node.publicIp
|
|
453
|
-
} to be available: ${error?.toString()}`,
|
|
454
|
-
);
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
process.stdout.write("\r");
|
|
458
|
-
clearInterval(twirlTimer);
|
|
459
|
-
console.log(`New nodes available (${nodes.length}):`);
|
|
460
|
-
for (const node of nodes) {
|
|
461
|
-
console.log(chalk.green(node.name));
|
|
462
|
-
}
|
|
463
|
-
},
|
|
464
|
-
})
|
|
465
|
-
.command({
|
|
466
|
-
command: "hetzner",
|
|
467
|
-
describe: "Spawn remote nodes on Hetzner Cloud",
|
|
468
|
-
builder: (hetznerArgs: Argv) => {
|
|
469
|
-
hetznerArgs.option("count", {
|
|
470
|
-
describe: "Amount of nodes to spawn",
|
|
471
|
-
defaultDescription: "One node",
|
|
472
|
-
type: "number",
|
|
473
|
-
alias: "c",
|
|
474
|
-
default: 1,
|
|
475
|
-
});
|
|
476
|
-
hetznerArgs.option("location", {
|
|
477
|
-
describe: "Location (e.g. fsn1, nbg1, hel1, ash, hil)",
|
|
478
|
-
type: "string",
|
|
479
|
-
alias: "l",
|
|
480
|
-
default: "fsn1",
|
|
481
|
-
});
|
|
482
|
-
hetznerArgs.option("group", {
|
|
483
|
-
describe: "Remote group to launch nodes in",
|
|
484
|
-
type: "string",
|
|
485
|
-
alias: "g",
|
|
486
|
-
default: DEFAULT_REMOTE_GROUP,
|
|
487
|
-
});
|
|
488
|
-
hetznerArgs.option("server-type", {
|
|
489
|
-
describe: "Server type",
|
|
490
|
-
type: "string",
|
|
491
|
-
alias: "t",
|
|
492
|
-
choices: [...HETZNER_SERVER_TYPES],
|
|
493
|
-
default: "cx11",
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
hetznerArgs.option("name", {
|
|
497
|
-
describe: "Name prefix for spawned nodes",
|
|
498
|
-
type: "string",
|
|
499
|
-
alias: "n",
|
|
500
|
-
default: "peerbit-node",
|
|
501
|
-
});
|
|
502
|
-
hetznerArgs.option("grant-access", {
|
|
503
|
-
describe: "Grant access to public keys on start",
|
|
504
|
-
defaultDescription:
|
|
505
|
-
"The publickey of this device located in 'directory'",
|
|
506
|
-
type: "string",
|
|
507
|
-
array: true,
|
|
508
|
-
alias: "ga",
|
|
509
|
-
});
|
|
510
|
-
hetznerArgs.option("directory", {
|
|
511
|
-
describe: "Peerbit directory",
|
|
512
|
-
defaultDescription: "~.peerbit",
|
|
513
|
-
type: "string",
|
|
514
|
-
alias: "d",
|
|
515
|
-
default: getHomeConfigDir(),
|
|
516
|
-
});
|
|
517
|
-
hetznerArgs.option("email", {
|
|
518
|
-
describe: "Email for Let's security messages",
|
|
519
|
-
type: "string",
|
|
520
|
-
alias: "e",
|
|
521
|
-
demandOption: true,
|
|
522
|
-
});
|
|
523
|
-
hetznerArgs.option("token", {
|
|
524
|
-
describe: "Hetzner Cloud API token (or set HCLOUD_TOKEN)",
|
|
525
|
-
type: "string",
|
|
526
|
-
alias: ["tok"],
|
|
527
|
-
});
|
|
528
|
-
hetznerArgs.option("image", {
|
|
529
|
-
describe: "Image to use (e.g. ubuntu-22.04)",
|
|
530
|
-
type: "string",
|
|
531
|
-
default: "ubuntu-22.04",
|
|
532
|
-
});
|
|
533
|
-
hetznerArgs.option("server-version", {
|
|
534
|
-
describe:
|
|
535
|
-
"@peerbit/server version or tag to install on the instance (e.g. 5.7.0-58d3d09)",
|
|
536
|
-
type: "string",
|
|
537
|
-
alias: ["sv"],
|
|
538
|
-
});
|
|
539
|
-
return hetznerArgs;
|
|
540
|
-
},
|
|
541
|
-
handler: async (args) => {
|
|
542
|
-
const self = (
|
|
543
|
-
await getKeypair(args.directory)
|
|
544
|
-
).publicKey.toPeerId();
|
|
545
|
-
const accessGrant: PeerId[] =
|
|
546
|
-
args["grant-access"]?.length > 0
|
|
547
|
-
? (args["grant-access"] as string[]).map((x) =>
|
|
548
|
-
peerIdFromString(x),
|
|
549
|
-
)
|
|
550
|
-
: [];
|
|
551
|
-
accessGrant.push(self);
|
|
552
|
-
const nodes = await launchHetznerNodes({
|
|
553
|
-
token: (args.token as string) || undefined,
|
|
554
|
-
email: args.email as string,
|
|
555
|
-
count: args.count,
|
|
556
|
-
namePrefix: args.name,
|
|
557
|
-
location: args.location,
|
|
558
|
-
serverType: args["server-type"],
|
|
559
|
-
grantAccess: accessGrant,
|
|
560
|
-
image: args.image,
|
|
561
|
-
serverVersion:
|
|
562
|
-
(args["server-version"] as string) || undefined,
|
|
563
|
-
});
|
|
564
|
-
|
|
565
|
-
console.log(
|
|
566
|
-
`Waiting for ${args.count} ${
|
|
567
|
-
args.count > 1 ? "nodes" : "node"
|
|
568
|
-
} to spawn. This might take a few minutes. You can watch the progress in your Hetzner Cloud console.`,
|
|
569
|
-
);
|
|
570
|
-
const twirlTimer = (function () {
|
|
571
|
-
const P = ["\\", "|", "/", "-"];
|
|
572
|
-
let x = 0;
|
|
573
|
-
return setInterval(function () {
|
|
574
|
-
process.stdout.write(
|
|
575
|
-
"\r" + "Loading: " + chalk.hex(colors[x])(P[x++]),
|
|
576
|
-
);
|
|
577
|
-
x &= 3;
|
|
578
|
-
}, 250);
|
|
579
|
-
})();
|
|
580
|
-
for (const node of nodes) {
|
|
581
|
-
try {
|
|
582
|
-
const domain = await waitForDomain(node.publicIp);
|
|
583
|
-
const remotes = new Remotes(getRemotesPath(args.directory));
|
|
584
|
-
remotes.add({
|
|
585
|
-
name: node.name,
|
|
586
|
-
address: domain,
|
|
587
|
-
group: args.group,
|
|
588
|
-
origin: {
|
|
589
|
-
type: "hetzner",
|
|
590
|
-
serverId: node.serverId,
|
|
591
|
-
location: node.location,
|
|
592
|
-
},
|
|
593
|
-
});
|
|
594
|
-
} catch (error: any) {
|
|
595
|
-
process.stdout.write("\r");
|
|
596
|
-
console.error(
|
|
597
|
-
`Error waiting for domain for ip: ${
|
|
598
|
-
node.publicIp
|
|
599
|
-
} to be available: ${error?.toString()}`,
|
|
600
|
-
);
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
process.stdout.write("\r");
|
|
604
|
-
clearInterval(twirlTimer);
|
|
605
|
-
console.log(`New nodes available (${nodes.length}):`);
|
|
606
|
-
for (const node of nodes) {
|
|
607
|
-
console.log(chalk.green(node.name));
|
|
608
|
-
}
|
|
609
|
-
},
|
|
610
|
-
})
|
|
611
|
-
.strict()
|
|
612
|
-
.demandCommand();
|
|
395
|
+
.command({
|
|
396
|
+
command: "spawn [provider]",
|
|
397
|
+
describe: false,
|
|
398
|
+
builder: (spawnArgs: Argv) =>
|
|
399
|
+
spawnArgs.positional("provider", { type: "string" }).strict(false),
|
|
400
|
+
handler: () => {
|
|
401
|
+
throw new Error(
|
|
402
|
+
"Automatic cloud provisioning has been retired. Provision a server with your provider, configure a domain you control, then register it with 'peerbit remote add <name> <address>'.",
|
|
403
|
+
);
|
|
404
|
+
},
|
|
613
405
|
})
|
|
614
406
|
.command({
|
|
615
407
|
command: "terminate [name...]",
|
|
616
|
-
describe:
|
|
408
|
+
describe:
|
|
409
|
+
"Terminate legacy Hetzner instances recorded by older releases",
|
|
617
410
|
builder: (killArgs: Argv) => {
|
|
618
411
|
killArgs.option("all", {
|
|
619
412
|
describe: "Kill all nodes",
|
|
@@ -644,19 +437,22 @@ export const cli = async (args?: string[]) => {
|
|
|
644
437
|
handler: async (args) => {
|
|
645
438
|
const remotes = new Remotes(getRemotesPath(args.directory));
|
|
646
439
|
const allRemotes = await remotes.all();
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
440
|
+
const selectedRemotes = allRemotes.filter(
|
|
441
|
+
(remote) => args.all || args.name.includes(remote.name),
|
|
442
|
+
);
|
|
443
|
+
const retiredAWSRemote = selectedRemotes.find(
|
|
444
|
+
(remote) => remote.origin?.type === "aws",
|
|
445
|
+
);
|
|
446
|
+
if (retiredAWSRemote?.origin?.type === "aws") {
|
|
447
|
+
throw getRetiredAWSManagementError(retiredAWSRemote.origin);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
for (const remote of selectedRemotes) {
|
|
451
|
+
if (remote.origin?.type === "hetzner") {
|
|
452
|
+
await terminateHetznerNode({
|
|
453
|
+
serverId: remote.origin.serverId,
|
|
454
|
+
token: (args.token as string) || undefined,
|
|
455
|
+
});
|
|
660
456
|
}
|
|
661
457
|
}
|
|
662
458
|
},
|