@peerbit/server 1.0.19 → 1.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 (55) hide show
  1. package/lib/esm/cli.js +107 -42
  2. package/lib/esm/cli.js.map +1 -1
  3. package/lib/esm/client.d.ts +25 -2
  4. package/lib/esm/client.js +105 -16
  5. package/lib/esm/client.js.map +1 -1
  6. package/lib/esm/config.browser.d.ts +0 -0
  7. package/lib/esm/config.browser.js +3 -0
  8. package/lib/esm/config.browser.js.map +1 -0
  9. package/lib/esm/config.d.ts +3 -0
  10. package/lib/esm/config.js +71 -0
  11. package/lib/esm/config.js.map +1 -1
  12. package/lib/esm/domain.d.ts +3 -1
  13. package/lib/esm/domain.js +46 -30
  14. package/lib/esm/domain.js.map +1 -1
  15. package/lib/esm/index.d.ts +3 -1
  16. package/lib/esm/index.js +3 -1
  17. package/lib/esm/index.js.map +1 -1
  18. package/lib/esm/nginx-template.conf +0 -10
  19. package/lib/esm/peerbit.d.ts +5 -0
  20. package/lib/esm/peerbit.js +26 -0
  21. package/lib/esm/peerbit.js.map +1 -0
  22. package/lib/esm/routes.d.ts +9 -0
  23. package/lib/esm/routes.js +18 -0
  24. package/lib/esm/routes.js.map +1 -0
  25. package/lib/esm/server.browser.d.ts +0 -0
  26. package/lib/esm/server.browser.js +3 -0
  27. package/lib/esm/server.browser.js.map +1 -0
  28. package/lib/esm/server.d.ts +15 -0
  29. package/lib/esm/server.js +356 -0
  30. package/lib/esm/server.js.map +1 -0
  31. package/lib/esm/types.d.ts +7 -0
  32. package/lib/esm/types.js +2 -0
  33. package/lib/esm/types.js.map +1 -0
  34. package/lib/ui/assets/config.browser-4ed993c7.js +1 -0
  35. package/lib/ui/assets/index-a8188422.js +53 -0
  36. package/lib/ui/index.html +1 -1
  37. package/package.json +16 -8
  38. package/src/cli.ts +124 -48
  39. package/src/client.ts +157 -16
  40. package/src/config.browser.ts +1 -0
  41. package/src/config.ts +80 -0
  42. package/src/domain.ts +55 -35
  43. package/src/index.ts +3 -1
  44. package/src/nginx-template.conf +0 -10
  45. package/src/peerbit.ts +26 -0
  46. package/src/routes.ts +20 -0
  47. package/src/server.browser.ts +1 -0
  48. package/src/server.ts +430 -0
  49. package/src/types.ts +7 -0
  50. package/lib/esm/api.d.ts +0 -33
  51. package/lib/esm/api.js +0 -370
  52. package/lib/esm/api.js.map +0 -1
  53. package/lib/esm/package.json +0 -3
  54. package/lib/ui/assets/index-40169014.js +0 -80
  55. package/src/api.ts +0 -437
package/lib/ui/index.html CHANGED
@@ -23,7 +23,7 @@
23
23
  Learn how to configure a non-root public URL by running `npm run build`.
24
24
  -->
25
25
  <title>Peerbit</title>
26
- <script type="module" crossorigin src="/assets/index-40169014.js"></script>
26
+ <script type="module" crossorigin src="/assets/index-a8188422.js"></script>
27
27
  <link rel="stylesheet" href="/assets/index-5265c558.css">
28
28
  </head>
29
29
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peerbit/server",
3
- "version": "1.0.19",
3
+ "version": "1.1.0",
4
4
  "author": "dao.xyz",
5
5
  "repository": {
6
6
  "type": "git",
@@ -17,6 +17,12 @@
17
17
  "exports": {
18
18
  "import": "./lib/esm/index.js"
19
19
  },
20
+ "browser": {
21
+ "./lib/esm/server.js": "./lib/esm/server.browser.js",
22
+ "./server.js": "./lib/esm/server.browser.js",
23
+ "./lib/esm/config.js": "./lib/esm/config.browser.js",
24
+ "./config.js": "./lib/esm/config.browser.js"
25
+ },
20
26
  "files": [
21
27
  "lib",
22
28
  "src",
@@ -30,22 +36,21 @@
30
36
  "access": "public"
31
37
  },
32
38
  "engines": {
33
- "node": ">=16.15.1"
39
+ "node": ">=18"
34
40
  },
35
41
  "scripts": {
36
42
  "clean": "shx rm -rf lib/*",
37
43
  "build": "yarn clean && yarn build-lib && yarn build-ui",
38
44
  "build-lib": "tsc -p tsconfig.json",
39
45
  "build-ui": "cd ../frontend && yarn build && cd ../node",
40
- "postbuild": "cp src/nginx-template.conf lib/esm/ && echo '{\"type\":\"module\"} ' | node ../../../../node_modules/.bin/json > lib/esm/package.json && cp -r ../frontend/dist lib/ui",
46
+ "postbuild": "cp src/nginx-template.conf lib/esm/ && cp -r ../frontend/dist lib/ui",
41
47
  "test": "node ../../../node_modules/.bin/jest test -c ../../../jest.config.ts --runInBand --forceExit",
42
48
  "test:unit": "node ../../../node_modules/.bin/jest test -c ../../../jest.config.unit.ts --runInBand --forceExit",
43
49
  "test:integration": "node ../node_modules/.bin/jest test -c ../../../jest.config.integration.ts --runInBand --forceExit"
44
50
  },
45
51
  "devDependencies": {
46
- "@peerbit/string": "2.0.2",
47
52
  "@peerbit/test-lib": "^0.0.1",
48
- "@peerbit/test-utils": "1.0.14",
53
+ "@peerbit/test-utils": "1.0.15",
49
54
  "@types/yargs": "^17.0.24",
50
55
  "aws-sdk": "^2.1259.0",
51
56
  "dotenv": "^16.1.4"
@@ -54,8 +59,11 @@
54
59
  "@aws-sdk/client-route-53": "^3.345.0",
55
60
  "@dao-xyz/libp2p-noise": "^12.0.1",
56
61
  "axios": "^1.4.0",
57
- "peerbit": "1.1.7",
58
- "yargs": "^17.7.2"
62
+ "chalk": "^5.3.0",
63
+ "peerbit": "1.2.0",
64
+ "tar-stream": "^3.1.6",
65
+ "yargs": "^17.7.2",
66
+ "zlib": "^1.0.5"
59
67
  },
60
- "gitHead": "65a9c603f39ea3e8ba1cbec065b37de6ef5741c3"
68
+ "gitHead": "4752c301fb49b2f937b4039533ff5bc3537da3e0"
61
69
  }
package/src/cli.ts CHANGED
@@ -1,9 +1,15 @@
1
- import { createTestDomain, startCertbot } from "./domain.js";
2
- import { serialize } from "@dao-xyz/borsh";
3
- import { client, startServerWithNode } from "./api.js";
1
+ import {
2
+ createTestDomain,
3
+ getDomainFromConfig,
4
+ loadConfig,
5
+ startCertbot,
6
+ } from "./domain.js";
7
+ import { startServerWithNode } from "./server.js";
4
8
  import { createRecord } from "./aws.js";
5
- import { toBase64 } from "@peerbit/crypto";
6
9
  import { getConfigDir } from "./config.js";
10
+ import chalk from "chalk";
11
+ import { client } from "./client.js";
12
+ import { StartProgram } from "./types.js";
7
13
 
8
14
  export const cli = async (args?: string[]) => {
9
15
  const yargs = await import("yargs");
@@ -25,18 +31,21 @@ export const cli = async (args?: string[]) => {
25
31
  type: "string",
26
32
  default: await getConfigDir(),
27
33
  },
28
- domain: {
29
- describe: "Domain to use when announcing Libp2p multiaddress",
30
- defaultDescription: "Test domain from public IP",
31
- type: "string",
32
- default: undefined,
34
+
35
+ bootstrap: {
36
+ describe: "Whether to connect to bootstap nodes on startup",
37
+ type: "boolean",
38
+ default: false,
33
39
  },
34
40
  },
35
41
  handler: async (args) => {
36
- await startServerWithNode(
37
- args.directory,
38
- args.domain ? args.domain : await createTestDomain()
39
- );
42
+ await startServerWithNode({
43
+ directory: args.directory,
44
+ domain: await loadConfig().then((config) =>
45
+ config ? getDomainFromConfig(config) : undefined
46
+ ),
47
+ bootstrap: args.bootstrap,
48
+ });
40
49
  },
41
50
  })
42
51
  .command("domain", "Setup a domain and certificate", (yargs) => {
@@ -147,6 +156,20 @@ export const cli = async (args?: string[]) => {
147
156
  .strict()
148
157
  .demandCommand();
149
158
  })
159
+ .command("network", "Manage network", (yargs) => {
160
+ yargs
161
+ .command({
162
+ command: "bootstrap",
163
+ describe: "Connect to bootstrap nodes",
164
+ handler: async () => {
165
+ const c = await client();
166
+ await c.network.bootstrap();
167
+ },
168
+ })
169
+ .strict()
170
+ .demandCommand();
171
+ })
172
+
150
173
  .command("topic", "Manage topics the node is listening to", (yargs) => {
151
174
  yargs
152
175
  .command({
@@ -183,8 +206,8 @@ export const cli = async (args?: string[]) => {
183
206
  .command("program", "Manage programs", (yargs) => {
184
207
  yargs
185
208
  .command({
186
- command: "get <address>",
187
- describe: "Get program manifest/serialized in base64",
209
+ command: "status <address>",
210
+ describe: "Is a program open",
188
211
  builder: (yargs: any) => {
189
212
  yargs.positional("address", {
190
213
  type: "string",
@@ -196,79 +219,132 @@ export const cli = async (args?: string[]) => {
196
219
 
197
220
  handler: async (args) => {
198
221
  const c = await client();
199
- const program = await c.program.get(args.address);
222
+ const program = await c.program.has(args.address);
200
223
  if (!program) {
201
- console.log("Program does not exist");
224
+ console.log(chalk.red("Closed"));
202
225
  } else {
203
- console.log(toBase64(serialize(program)));
226
+ console.log(chalk.green("Open"));
204
227
  }
205
228
  },
206
229
  })
207
230
  .command({
208
- command: "add <program>",
209
- describe: "Add program",
231
+ command: "drop <address>",
232
+ describe: "Drop a program",
210
233
  builder: (yargs: any) => {
211
- yargs.positional("program", {
234
+ yargs.positional("address", {
212
235
  type: "string",
213
- describe: "base64 serialized",
236
+ describe: "Program address",
214
237
  demandOption: true,
215
238
  });
216
239
  return yargs;
217
240
  },
241
+
218
242
  handler: async (args) => {
219
243
  const c = await client();
220
- const address = await c.program.put(args.program);
221
- console.log(address.toString());
244
+ await c.program.drop(args.address);
222
245
  },
223
246
  })
224
247
  .command({
225
- command: "import <library>",
226
- describe: "import a library that contains programs",
248
+ command: "close <address>",
249
+ describe: "Close a program",
227
250
  builder: (yargs: any) => {
228
- yargs.positional("library", {
229
- type: "array",
230
- describe:
231
- "Library name (will be loaded with js import(...)). Onlu libraries that are globally installed and can be imported",
251
+ yargs.positional("address", {
252
+ type: "string",
253
+ describe: "Program address",
232
254
  demandOption: true,
233
255
  });
234
256
  return yargs;
235
257
  },
258
+
236
259
  handler: async (args) => {
237
- for (const lib of args.library) {
238
- const importedLib = await import(
239
- /* webpackIgnore: true */ /* @vite-ignore */ lib
240
- );
241
- console.log("imported lib:", importedLib);
242
- }
260
+ const c = await client();
261
+ await c.program.close(args.address);
243
262
  },
244
263
  })
245
- .strict()
246
- .demandCommand();
247
- return yargs;
248
- })
249
- .command("library", "Manage libraries", (yargs) => {
250
- yargs
251
264
  .command({
252
- command: "add <library>",
253
- describe: "add a library that contains programs",
265
+ command: "list",
266
+ describe: "List all runniing programs",
267
+ aliases: "ls",
268
+ handler: async (args) => {
269
+ const c = await client();
270
+ const list = await c.program.list();
271
+
272
+ console.log(`Running programs (${list.length}):`);
273
+ list.forEach((p) => {
274
+ console.log(chalk.green(p));
275
+ });
276
+ },
277
+ })
278
+
279
+ .command({
280
+ command: "open [program]",
281
+ describe: "Open program",
254
282
  builder: (yargs: any) => {
255
- yargs.positional("library", {
283
+ yargs.positional("program", {
256
284
  type: "string",
257
- describe:
258
- "Library name (will be loaded with js import(...)). Onlu libraries that are globally installed and can be imported",
285
+ describe: "Identifier",
259
286
  demandOption: true,
260
287
  });
288
+ yargs.option("base64", {
289
+ type: "string",
290
+ describe: "Base64 encoded serialized",
291
+ alias: "b",
292
+ });
293
+ yargs.option("variant", {
294
+ type: "string",
295
+ describe: "Variant name",
296
+ alias: "v",
297
+ });
261
298
  return yargs;
262
299
  },
263
300
  handler: async (args) => {
264
301
  const c = await client();
265
- await c.library.put(args.library);
302
+ if (!args.base64 && !args.variant) {
303
+ throw new Error(
304
+ "Either base64 or variant argument needs to be provided"
305
+ );
306
+ }
307
+ let startArg: StartProgram;
308
+ if (args.base64) {
309
+ startArg = {
310
+ base64: args.base64,
311
+ };
312
+ } else {
313
+ startArg = {
314
+ variant: args.variant,
315
+ };
316
+ }
317
+ const address = await c.program.open(startArg);
318
+ console.log("Started program with address: ");
319
+ console.log(chalk.green(address.toString()));
266
320
  },
267
321
  })
268
322
  .strict()
269
323
  .demandCommand();
270
324
  return yargs;
271
325
  })
326
+ .command({
327
+ command: "install <package-spec>",
328
+ describe: "install and import a dependency",
329
+ builder: (yargs: any) => {
330
+ yargs.positional("package-spec", {
331
+ type: "string",
332
+ describe: "Installed dependency will be loaded with js import(...)",
333
+ demandOption: true,
334
+ });
335
+
336
+ return yargs;
337
+ },
338
+ handler: async (args) => {
339
+ const c = await client();
340
+ const newPrograms = await c.dependency.install(args["package-spec"]);
341
+
342
+ console.log(`New programs available (${newPrograms.length}):`);
343
+ newPrograms.forEach((p) => {
344
+ console.log(chalk.green(p));
345
+ });
346
+ },
347
+ })
272
348
  .help()
273
349
  .strict()
274
350
  .demandCommand().argv;
package/src/client.ts CHANGED
@@ -1,23 +1,164 @@
1
- import { DirectSub } from "@peerbit/pubsub";
2
- import { Peerbit } from "peerbit";
1
+ import { StartByBase64, StartByVariant, StartProgram } from "./types.js";
2
+ import {
3
+ ADDRESS_PATH,
4
+ BOOTSTRAP_PATH,
5
+ INSTALL_PATH,
6
+ LOCAL_PORT,
7
+ PEER_ID_PATH,
8
+ PROGRAMS_PATH,
9
+ PROGRAM_PATH,
10
+ } from "./routes.js";
11
+ import { Address } from "@peerbit/program";
12
+ import { multiaddr } from "@multiformats/multiaddr";
3
13
 
4
- export const create = (directory: string, domain?: string) => {
5
- return Peerbit.create({
6
- libp2p: {
14
+ export const client = async (
15
+ endpoint: string = "http://localhost:" + LOCAL_PORT
16
+ ) => {
17
+ const { default: axios } = await import("axios");
18
+
19
+ const validateStatus = (status: number) => {
20
+ return (status >= 200 && status < 300) || status == 404;
21
+ };
22
+
23
+ const throwIfNot200 = (resp: { status: number; data: any }) => {
24
+ if (resp.status !== 200) {
25
+ throw new Error(resp.data);
26
+ }
27
+ return resp;
28
+ };
29
+ const getBodyByStatus = <
30
+ D extends { toString(): string },
31
+ T extends { status: number; data: D }
32
+ >(
33
+ resp: T
34
+ ): D | undefined => {
35
+ if (resp.status === 404) {
36
+ return;
37
+ }
38
+ if (resp.status == 200) {
39
+ return resp.data;
40
+ }
41
+ throw new Error(
42
+ typeof resp.data === "string" ? resp.data : resp.data.toString()
43
+ );
44
+ };
45
+ const getId = async () =>
46
+ throwIfNot200(await axios.get(endpoint + PEER_ID_PATH, { validateStatus }))
47
+ .data;
48
+
49
+ const getHeaders = async () => {
50
+ const config = await import("./config.js");
51
+ const headers = {
52
+ authorization: "Basic admin:" + (await config.loadPassword()),
53
+ };
54
+ return headers;
55
+ };
56
+ return {
57
+ peer: {
58
+ id: {
59
+ get: getId,
60
+ },
7
61
  addresses: {
8
- announce: domain
9
- ? [`/dns4/${domain}/tcp/8001`, `/dns4/${domain}/tcp/8002/ws`]
10
- : undefined,
11
- listen: ["/ip4/127.0.0.1/tcp/8001", "/ip4/127.0.0.1/tcp/8002/ws"],
62
+ get: async () => {
63
+ return (
64
+ throwIfNot200(
65
+ await axios.get(endpoint + ADDRESS_PATH, {
66
+ validateStatus,
67
+ })
68
+ ).data as string[]
69
+ ).map((x) => multiaddr(x));
70
+ },
71
+ },
72
+ },
73
+ program: {
74
+ has: async (address: Address | string): Promise<boolean> => {
75
+ const result = await axios.head(
76
+ endpoint +
77
+ PROGRAM_PATH +
78
+ "/" +
79
+ encodeURIComponent(address.toString()),
80
+ { validateStatus, headers: await getHeaders() }
81
+ );
82
+ if (result.status !== 200 && result.status !== 404) {
83
+ throw new Error(result.data);
84
+ }
85
+ return result.status === 200 ? true : false;
86
+ },
87
+
88
+ open: async (program: StartProgram): Promise<Address> => {
89
+ const resp = throwIfNot200(
90
+ await axios.put(endpoint + PROGRAM_PATH, JSON.stringify(program), {
91
+ validateStatus,
92
+ headers: await getHeaders(),
93
+ })
94
+ );
95
+ return resp.data as string;
12
96
  },
13
- connectionManager: {
14
- maxConnections: Infinity,
15
- minConnections: 0,
97
+
98
+ close: async (address: string): Promise<void> => {
99
+ throwIfNot200(
100
+ await axios.delete(
101
+ endpoint +
102
+ PROGRAM_PATH +
103
+ "/" +
104
+ encodeURIComponent(address.toString()),
105
+ {
106
+ validateStatus,
107
+ headers: await getHeaders(),
108
+ }
109
+ )
110
+ );
111
+ },
112
+
113
+ drop: async (address: string): Promise<void> => {
114
+ throwIfNot200(
115
+ await axios.delete(
116
+ endpoint +
117
+ PROGRAM_PATH +
118
+ "/" +
119
+ encodeURIComponent(address.toString()) +
120
+ "?delete=true",
121
+ {
122
+ validateStatus,
123
+ headers: await getHeaders(),
124
+ }
125
+ )
126
+ );
127
+ },
128
+
129
+ list: async (): Promise<string[]> => {
130
+ const resp = throwIfNot200(
131
+ await axios.get(endpoint + PROGRAMS_PATH, {
132
+ validateStatus,
133
+ headers: await getHeaders(),
134
+ })
135
+ );
136
+ return resp.data as string[];
137
+ },
138
+ },
139
+ dependency: {
140
+ install: async (name: string): Promise<string[]> => {
141
+ const resp = await axios.put(endpoint + INSTALL_PATH, name, {
142
+ validateStatus,
143
+ headers: await getHeaders(),
144
+ });
145
+ if (resp.status !== 200) {
146
+ throw new Error(
147
+ typeof resp.data === "string" ? resp.data : resp.data.toString()
148
+ );
149
+ }
150
+ return resp.data;
16
151
  },
17
- services: {
18
- pubsub: (c) => new DirectSub(c, { canRelayMessage: true }),
152
+ },
153
+ network: {
154
+ bootstrap: async (): Promise<void> => {
155
+ throwIfNot200(
156
+ await axios.post(endpoint + BOOTSTRAP_PATH, undefined, {
157
+ validateStatus,
158
+ headers: await getHeaders(),
159
+ })
160
+ );
19
161
  },
20
162
  },
21
- directory,
22
- });
163
+ };
23
164
  };
@@ -0,0 +1 @@
1
+ /// Not supported
package/src/config.ts CHANGED
@@ -17,4 +17,84 @@ export const getKeysPath = async (configDir: string): Promise<string> => {
17
17
  return path.join(configDir, "keys");
18
18
  };
19
19
 
20
+ export const checkExistPath = async (path: string) => {
21
+ const fs = await import("fs");
22
+
23
+ try {
24
+ if (!fs.existsSync(path)) {
25
+ fs.accessSync(path, fs.constants.W_OK); // will throw if fails
26
+ return false;
27
+ }
28
+ return true;
29
+ } catch (err: any) {
30
+ if (err.message.indexOf("no such file")) {
31
+ return false;
32
+ }
33
+ throw new Error("Can not access path");
34
+ }
35
+ };
36
+
37
+ export const loadPassword = async (): Promise<string> => {
38
+ const fs = await import("fs");
39
+ const configDir = await getConfigDir();
40
+ const credentialsPath = await getCredentialsPath(configDir);
41
+ if (!(await checkExistPath(credentialsPath))) {
42
+ throw new NotFoundError("Credentials file does not exist");
43
+ }
44
+ const password = JSON.parse(
45
+ fs.readFileSync(credentialsPath, "utf-8")
46
+ ).password;
47
+ if (!password || password.length === 0) {
48
+ throw new NotFoundError("Password not found");
49
+ }
50
+ return password;
51
+ };
52
+
53
+ export const getPackageName = async (path: string): Promise<string> => {
54
+ const pathLib = await import("path");
55
+ const tar = await import("tar-stream");
56
+ const zlib = await import("zlib");
57
+ const urlLib = await import("url");
58
+ const fs = await import("fs");
59
+
60
+ if (!fs.existsSync(path)) {
61
+ throw new Error("File does not exist");
62
+ }
63
+ return new Promise((resolve, reject) => {
64
+ try {
65
+ const extract = tar.extract();
66
+ let data = "";
67
+
68
+ extract.on("entry", function (header, stream, cb) {
69
+ stream.on("data", function (chunk) {
70
+ if (header.name == "package/package.json") data += chunk;
71
+ });
72
+
73
+ stream.on("end", function () {
74
+ cb();
75
+ });
76
+
77
+ stream.resume();
78
+ });
79
+
80
+ extract.on("finish", function () {
81
+ const name = JSON.parse(data)?.name;
82
+ if (!name) {
83
+ reject(new Error("Could not find name from package.json file"));
84
+ } else {
85
+ resolve(name);
86
+ }
87
+ });
88
+
89
+ extract.on("error", (e) => {
90
+ reject(e);
91
+ });
92
+
93
+ fs.createReadStream(path).pipe(zlib.createGunzip()).pipe(extract);
94
+ } catch (error) {
95
+ reject(error);
96
+ }
97
+ });
98
+ };
99
+
20
100
  export class NotFoundError extends Error {}