@peerbit/server 1.0.20 → 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 (49) hide show
  1. package/lib/esm/cli.js +107 -36
  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/index.d.ts +3 -1
  13. package/lib/esm/index.js +3 -1
  14. package/lib/esm/index.js.map +1 -1
  15. package/lib/esm/peerbit.d.ts +5 -0
  16. package/lib/esm/peerbit.js +26 -0
  17. package/lib/esm/peerbit.js.map +1 -0
  18. package/lib/esm/routes.d.ts +9 -0
  19. package/lib/esm/routes.js +18 -0
  20. package/lib/esm/routes.js.map +1 -0
  21. package/lib/esm/server.browser.d.ts +0 -0
  22. package/lib/esm/server.browser.js +3 -0
  23. package/lib/esm/server.browser.js.map +1 -0
  24. package/lib/esm/server.d.ts +15 -0
  25. package/lib/esm/server.js +356 -0
  26. package/lib/esm/server.js.map +1 -0
  27. package/lib/esm/types.d.ts +7 -0
  28. package/lib/esm/types.js +2 -0
  29. package/lib/esm/types.js.map +1 -0
  30. package/lib/ui/assets/config.browser-4ed993c7.js +1 -0
  31. package/lib/ui/assets/index-a8188422.js +53 -0
  32. package/lib/ui/index.html +1 -1
  33. package/package.json +16 -7
  34. package/src/cli.ts +118 -43
  35. package/src/client.ts +157 -16
  36. package/src/config.browser.ts +1 -0
  37. package/src/config.ts +80 -0
  38. package/src/index.ts +3 -1
  39. package/src/peerbit.ts +26 -0
  40. package/src/routes.ts +20 -0
  41. package/src/server.browser.ts +1 -0
  42. package/src/server.ts +430 -0
  43. package/src/types.ts +7 -0
  44. package/lib/esm/api.d.ts +0 -33
  45. package/lib/esm/api.js +0 -370
  46. package/lib/esm/api.js.map +0 -1
  47. package/lib/esm/package.json +0 -3
  48. package/lib/ui/assets/index-40169014.js +0 -80
  49. package/src/api.ts +0 -436
package/src/api.ts DELETED
@@ -1,436 +0,0 @@
1
- import http from "http";
2
- import { fromBase64, toBase64 } from "@peerbit/crypto";
3
- import { serialize, deserialize } from "@dao-xyz/borsh";
4
- import { Program, Address, ProgramClient } from "@peerbit/program";
5
- import { multiaddr } from "@multiformats/multiaddr";
6
- import { waitFor } from "@peerbit/time";
7
- import { v4 as uuid } from "uuid";
8
- import { Libp2p } from "libp2p";
9
- import { getConfigDir, getCredentialsPath, NotFoundError } from "./config.js";
10
- import { setMaxListeners } from "events";
11
- import { create } from "./client.js";
12
-
13
- export const SSL_PORT = 9002;
14
- export const LOCAL_PORT = 8082;
15
-
16
- export const getPort = (protocol: string) => {
17
- if (protocol === "https:") {
18
- return SSL_PORT;
19
- }
20
-
21
- if (protocol === "http:") {
22
- return LOCAL_PORT;
23
- }
24
-
25
- throw new Error("Unsupported protocol: " + protocol);
26
- };
27
-
28
- const PEER_ID_PATH = "/peer/id";
29
- const ADDRESS_PATH = "/peer/address";
30
- const PROGRAM_PATH = "/program";
31
- const LIBRARY_PATH = "/library";
32
-
33
- export const checkExistPath = async (path: string) => {
34
- const fs = await import("fs");
35
-
36
- try {
37
- if (!fs.existsSync(path)) {
38
- fs.accessSync(path, fs.constants.W_OK); // will throw if fails
39
- return false;
40
- }
41
- return true;
42
- } catch (err: any) {
43
- if (err.message.indexOf("no such file")) {
44
- return false;
45
- }
46
- throw new Error("Can not access path");
47
- }
48
- };
49
- export const createPassword = async (): Promise<string> => {
50
- const fs = await import("fs");
51
- const configDir = await getConfigDir();
52
- const credentialsPath = await getCredentialsPath(configDir);
53
- if (await checkExistPath(credentialsPath)) {
54
- throw new Error(
55
- "Config path for credentials: " + credentialsPath + ", already exist"
56
- );
57
- }
58
- console.log(`Creating config folder ${configDir}`);
59
-
60
- fs.mkdirSync(configDir, { recursive: true });
61
- await waitFor(() => fs.existsSync(configDir));
62
-
63
- console.log(`Created config folder ${configDir}`);
64
-
65
- const password = uuid();
66
- fs.writeFileSync(
67
- credentialsPath,
68
- JSON.stringify({ username: "admin", password })
69
- );
70
- console.log(`Created credentials at ${credentialsPath}`);
71
- return password;
72
- };
73
-
74
- export const loadPassword = async (): Promise<string> => {
75
- const fs = await import("fs");
76
- const configDir = await getConfigDir();
77
- const credentialsPath = await getCredentialsPath(configDir);
78
- if (!(await checkExistPath(credentialsPath))) {
79
- throw new NotFoundError("Credentials file does not exist");
80
- }
81
- const password = JSON.parse(
82
- fs.readFileSync(credentialsPath, "utf-8")
83
- ).password;
84
- if (!password || password.length === 0) {
85
- throw new NotFoundError("Password not found");
86
- }
87
- return password;
88
- };
89
-
90
- export const loadOrCreatePassword = async (): Promise<string> => {
91
- try {
92
- return await loadPassword();
93
- } catch (error) {
94
- if (error instanceof NotFoundError) {
95
- return createPassword();
96
- }
97
- throw error;
98
- }
99
- };
100
- export const startServerWithNode = async (
101
- directory: string,
102
- domain?: string
103
- ) => {
104
- const peer = await create(directory, domain);
105
- const server = await startServer(peer);
106
- const printNodeInfo = async () => {
107
- console.log("Starting node with address(es): ");
108
- const id = await (await client()).peer.id.get();
109
- console.log("id: " + id);
110
- console.log("Addresses: ");
111
- for (const a of await (await client()).peer.addresses.get()) {
112
- console.log(a.toString());
113
- }
114
- };
115
-
116
- await printNodeInfo();
117
- const shutDownHook = async (
118
- controller: { stop: () => any },
119
- server: {
120
- close: () => void;
121
- }
122
- ) => {
123
- const { exit } = await import("process");
124
- process.on("SIGINT", async () => {
125
- console.log("Shutting down node");
126
- await server.close();
127
- await controller.stop();
128
- exit();
129
- });
130
- };
131
- await shutDownHook(peer, server);
132
- };
133
- export const startServer = async (
134
- client: ProgramClient,
135
- port: number = LOCAL_PORT
136
- ): Promise<http.Server> => {
137
- const notPeerBitError =
138
- "Client is just a Libp2p node, not a full Peerbit client. The command is not supported for this node type";
139
- const notSupportedError = "Not implemted";
140
-
141
- const password = await loadOrCreatePassword();
142
-
143
- const adminACL = (req: http.IncomingMessage): boolean => {
144
- const auth = req.headers["authorization"];
145
- if (!auth?.startsWith("Basic ")) {
146
- return false;
147
- }
148
- const credentials = auth?.substring("Basic ".length);
149
- const username = credentials.split(":")[0];
150
- if (username !== "admin") {
151
- return false;
152
- }
153
- if (password !== credentials.substring(username.length + 1)) {
154
- return false;
155
- }
156
- return true;
157
- };
158
-
159
- const getProgramFromPath = (
160
- req: http.IncomingMessage,
161
- pathIndex: number
162
- ): Program | undefined => {
163
- /* if (!req.url) {
164
- throw new Error("Missing url");
165
- }
166
- const url = new URL(req.url, "http://localhost:" + port);
167
- const path = url.pathname
168
- .substring(Math.min(1, url.pathname.length), url.pathname.length)
169
- .split("/");
170
- if (path.length <= pathIndex) {
171
- throw new Error("Invalid path");
172
- }
173
- const address = decodeURIComponent(path[pathIndex]);
174
- const p = client.programs.get(address);
175
- if (p) {
176
- return p.program;
177
- }
178
- return;
179
- */
180
- throw new Error("Not supported");
181
- };
182
- const getBody = (
183
- req: http.IncomingMessage,
184
- callback: (body: string) => void
185
- ) => {
186
- let body = "";
187
- req.on("data", function (d) {
188
- body += d;
189
- });
190
- req.on("end", function () {
191
- callback(body);
192
- });
193
- };
194
-
195
- const e404 = "404";
196
- const endpoints = (client: ProgramClient | Libp2p): http.RequestListener => {
197
- return async (req, res) => {
198
- res.setHeader("Access-Control-Allow-Origin", "*");
199
- res.setHeader("Access-Control-Request-Method", "*");
200
- res.setHeader("Access-Control-Allow-Headers", "*");
201
- res.setHeader("Access-Control-Allow-Methods", "*");
202
- const r404 = () => {
203
- res.writeHead(404);
204
- res.end(e404);
205
- };
206
-
207
- try {
208
- if (req.url) {
209
- if (
210
- !req.url.startsWith(PEER_ID_PATH) &&
211
- !req.url.startsWith(ADDRESS_PATH) &&
212
- !(await adminACL(req))
213
- ) {
214
- res.writeHead(401);
215
- res.end("Not authorized");
216
- return;
217
- } else if (req.url.startsWith(PROGRAM_PATH)) {
218
- if (true as any /* client instanceof Peerbit === false */) {
219
- res.writeHead(400);
220
- res.write(notSupportedError);
221
- res.end();
222
- } /* else {
223
- switch (req.method) {
224
- case "GET":
225
- try {
226
- const program = getProgramFromPath(req, 1);
227
- if (program) {
228
- res.writeHead(200);
229
- res.write(toBase64(serialize(program)));
230
- res.end();
231
- } else {
232
- res.writeHead(404);
233
- res.end();
234
- }
235
- } catch (error: any) {
236
- res.writeHead(404);
237
- res.end(error.message);
238
- }
239
- break;
240
-
241
- case "PUT":
242
- getBody(req, (body) => {
243
- try {
244
- const parsed = deserialize(fromBase64(body), Program);
245
- (client as Peerbit)
246
- .open(parsed)
247
- .then((program) => {
248
- res.writeHead(200);
249
- res.end(program.address.toString());
250
- })
251
- .catch((error) => {
252
- res.writeHead(400);
253
- res.end(
254
- "Failed to open program: " + error.toString()
255
- );
256
- });
257
- } catch (error) {
258
- res.writeHead(400);
259
- res.end("Invalid base64 program binary");
260
- }
261
- });
262
- break;
263
-
264
- default:
265
- r404();
266
- break;
267
- }
268
- } */
269
- } else if (req.url.startsWith(LIBRARY_PATH)) {
270
- const url = new URL(req.url, "http://localhost:" + port);
271
- switch (req.method) {
272
- case "PUT":
273
- getBody(req, (body) => {
274
- const name = body;
275
- if (name && name.length === 0) {
276
- res.writeHead(400);
277
- res.end("Invalid library: " + name);
278
- } else {
279
- import(/* webpackIgnore: true */ /* @vite-ignore */ name)
280
- .then(() => {
281
- res.writeHead(200);
282
- res.end();
283
- })
284
- .catch((e) => {
285
- res.writeHead(400);
286
- res.end(e.message.toString?.());
287
- });
288
- }
289
- });
290
- break;
291
-
292
- default:
293
- r404();
294
- break;
295
- }
296
- } else if (req.url.startsWith(PEER_ID_PATH)) {
297
- res.writeHead(200);
298
- res.end(client.peerId.toString());
299
- } else if (req.url.startsWith(ADDRESS_PATH)) {
300
- res.setHeader("Content-Type", "application/json");
301
- res.writeHead(200);
302
- const addresses = client.getMultiaddrs().map((x) => x.toString());
303
- res.end(JSON.stringify(addresses));
304
- } else {
305
- r404();
306
- }
307
- } else {
308
- r404();
309
- }
310
- } catch (error: any) {
311
- res.writeHead(500);
312
- console.error(error?.message);
313
- res.end("Unexpected error");
314
- }
315
- };
316
- };
317
-
318
- setMaxListeners(Infinity); // TODO make this better (lower and large enough)
319
- process.setMaxListeners(Infinity); // TODO make this better (lower and large enough)
320
-
321
- const server = http.createServer(endpoints(client));
322
- server.listen(port);
323
- server.on("error", (e) => {
324
- console.error("Server error: " + e?.message);
325
- import("fs").then((fs) => {
326
- fs.writeFile("error.log", JSON.stringify(e.message), function () {
327
- /* void */ 0;
328
- });
329
- });
330
- });
331
- console.log("API available at port", port);
332
- return server;
333
- };
334
-
335
- export const client = async (
336
- endpoint: string = "http://localhost:" + LOCAL_PORT
337
- ) => {
338
- const { default: axios } = await import("axios");
339
-
340
- const validateStatus = (status: number) => {
341
- return (status >= 200 && status < 300) || status == 404;
342
- };
343
-
344
- const throwIfNot200 = (resp: { status: number; data: any }) => {
345
- if (resp.status !== 200) {
346
- throw new Error(resp.data);
347
- }
348
- return resp;
349
- };
350
- const getBodyByStatus = <
351
- D extends { toString(): string },
352
- T extends { status: number; data: D }
353
- >(
354
- resp: T
355
- ): D | undefined => {
356
- if (resp.status === 404) {
357
- return;
358
- }
359
- if (resp.status == 200) {
360
- return resp.data;
361
- }
362
- throw new Error(
363
- typeof resp.data === "string" ? resp.data : resp.data.toString()
364
- );
365
- };
366
- const getId = async () =>
367
- throwIfNot200(await axios.get(endpoint + PEER_ID_PATH, { validateStatus }))
368
- .data;
369
- const getHeaders = async () => {
370
- const headers = {
371
- authorization: "Basic admin:" + (await loadPassword()),
372
- };
373
- return headers;
374
- };
375
- return {
376
- peer: {
377
- id: {
378
- get: getId,
379
- },
380
- addresses: {
381
- get: async () => {
382
- return (
383
- throwIfNot200(
384
- await axios.get(endpoint + ADDRESS_PATH, {
385
- validateStatus,
386
- headers: await getHeaders(),
387
- })
388
- ).data as string[]
389
- ).map((x) => multiaddr(x));
390
- },
391
- },
392
- },
393
- program: {
394
- get: async (address: Address | string): Promise<Program | undefined> => {
395
- const result = getBodyByStatus<string, any>(
396
- await axios.get(
397
- endpoint +
398
- PROGRAM_PATH +
399
- "/" +
400
- encodeURIComponent(address.toString()),
401
- { validateStatus, headers: await getHeaders() }
402
- )
403
- );
404
- return !result ? undefined : deserialize(fromBase64(result), Program);
405
- },
406
-
407
- /**
408
- * @param program Program, or base64 string representation
409
- * @param topic, topic
410
- * @returns
411
- */
412
- put: async (program: Program | string): Promise<Address> => {
413
- const base64 =
414
- program instanceof Program ? toBase64(serialize(program)) : program;
415
- const resp = throwIfNot200(
416
- await axios.put(endpoint + PROGRAM_PATH, base64, {
417
- validateStatus,
418
- headers: await getHeaders(),
419
- })
420
- );
421
- return resp.data as string;
422
- },
423
- },
424
- library: {
425
- put: async (name: string): Promise<void> => {
426
- throwIfNot200(
427
- await axios.put(endpoint + LIBRARY_PATH, name, {
428
- validateStatus,
429
- headers: await getHeaders(),
430
- })
431
- );
432
- return;
433
- },
434
- },
435
- };
436
- };