@peerbit/server 1.1.2 → 3.0.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/lib/esm/cli.js +518 -144
- package/lib/esm/cli.js.map +1 -1
- package/lib/esm/client.d.ts +11 -3
- package/lib/esm/client.js +61 -27
- package/lib/esm/client.js.map +1 -1
- package/lib/esm/config.d.ts +8 -5
- package/lib/esm/config.js +44 -19
- package/lib/esm/config.js.map +1 -1
- package/lib/esm/peerbit.d.ts +4 -0
- package/lib/esm/peerbit.js +15 -2
- package/lib/esm/peerbit.js.map +1 -1
- package/lib/esm/remotes.browser.d.ts +0 -0
- package/lib/esm/remotes.browser.js +3 -0
- package/lib/esm/remotes.browser.js.map +1 -0
- package/lib/esm/remotes.d.ts +16 -0
- package/lib/esm/remotes.js +51 -0
- package/lib/esm/remotes.js.map +1 -0
- package/lib/esm/routes.d.ts +3 -0
- package/lib/esm/routes.js +3 -0
- package/lib/esm/routes.js.map +1 -1
- package/lib/esm/server.d.ts +14 -4
- package/lib/esm/server.js +297 -144
- package/lib/esm/server.js.map +1 -1
- package/lib/esm/session.d.ts +19 -0
- package/lib/esm/session.js +49 -0
- package/lib/esm/session.js.map +1 -0
- package/lib/esm/signes-request.d.ts +5 -0
- package/lib/esm/signes-request.js +54 -0
- package/lib/esm/signes-request.js.map +1 -0
- package/lib/esm/trust.browser.d.ts +0 -0
- package/lib/esm/trust.browser.js +3 -0
- package/lib/esm/trust.browser.js.map +1 -0
- package/lib/esm/trust.d.ts +9 -0
- package/lib/esm/trust.js +36 -0
- package/lib/esm/trust.js.map +1 -0
- package/lib/esm/types.d.ts +10 -0
- package/lib/ui/assets/index-cac7195d.js +77 -0
- package/lib/ui/index.html +1 -1
- package/package.json +9 -5
- package/src/cli.ts +705 -271
- package/src/client.ts +105 -30
- package/src/config.ts +52 -25
- package/src/peerbit.ts +27 -3
- package/src/remotes.browser.ts +1 -0
- package/src/remotes.ts +63 -0
- package/src/routes.ts +3 -1
- package/src/server.ts +381 -190
- package/src/session.ts +69 -0
- package/src/signes-request.ts +84 -0
- package/src/trust.browser.ts +1 -0
- package/src/trust.ts +39 -0
- package/src/types.ts +13 -0
- package/lib/ui/assets/config.browser-4ed993c7.js +0 -1
- package/lib/ui/assets/index-a8188422.js +0 -53
package/src/server.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import http from "http";
|
|
2
|
-
import { fromBase64 } from "@peerbit/crypto";
|
|
2
|
+
import { Ed25519Keypair, fromBase64, sha256Base64Sync } from "@peerbit/crypto";
|
|
3
3
|
import { deserialize } from "@dao-xyz/borsh";
|
|
4
4
|
import {
|
|
5
5
|
Program,
|
|
@@ -10,90 +10,130 @@ import {
|
|
|
10
10
|
import { waitFor } from "@peerbit/time";
|
|
11
11
|
import { v4 as uuid } from "uuid";
|
|
12
12
|
import {
|
|
13
|
-
checkExistPath,
|
|
14
13
|
getHomeConfigDir,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
NotFoundError,
|
|
14
|
+
getNodePath,
|
|
15
|
+
getKeypair,
|
|
16
|
+
getTrustPath,
|
|
19
17
|
} from "./config.js";
|
|
20
18
|
import { setMaxListeners } from "events";
|
|
21
19
|
import { create } from "./peerbit.js";
|
|
22
20
|
import { Peerbit } from "peerbit";
|
|
23
21
|
import { getSchema } from "@dao-xyz/borsh";
|
|
24
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
InstallDependency,
|
|
24
|
+
StartByBase64,
|
|
25
|
+
StartByVariant,
|
|
26
|
+
StartProgram,
|
|
27
|
+
} from "./types.js";
|
|
25
28
|
import {
|
|
26
29
|
ADDRESS_PATH,
|
|
27
30
|
BOOTSTRAP_PATH,
|
|
31
|
+
TERMINATE_PATH,
|
|
28
32
|
INSTALL_PATH,
|
|
29
33
|
LOCAL_PORT,
|
|
30
34
|
PEER_ID_PATH,
|
|
31
35
|
PROGRAMS_PATH,
|
|
32
36
|
PROGRAM_PATH,
|
|
37
|
+
RESTART_PATH,
|
|
38
|
+
TRUST_PATH,
|
|
33
39
|
} from "./routes.js";
|
|
34
|
-
import {
|
|
35
|
-
|
|
40
|
+
import { Session } from "./session.js";
|
|
41
|
+
import fs from "fs";
|
|
42
|
+
import { exit } from "process";
|
|
43
|
+
import { spawn, fork, execSync } from "child_process";
|
|
44
|
+
import tmp from "tmp";
|
|
45
|
+
import path from "path";
|
|
46
|
+
import { base58btc } from "multiformats/bases/base58";
|
|
36
47
|
import { dirname } from "path";
|
|
37
48
|
import { fileURLToPath } from "url";
|
|
49
|
+
import { Level } from "level";
|
|
50
|
+
import { MemoryLevel } from "memory-level";
|
|
51
|
+
import { Trust } from "./trust.js";
|
|
52
|
+
import { getBody, verifyRequest } from "./signes-request.js";
|
|
53
|
+
import { cli } from "./cli.js";
|
|
38
54
|
|
|
39
55
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
40
56
|
|
|
41
|
-
export const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
console.log(`Creating config folder ${configDir}`);
|
|
51
|
-
|
|
52
|
-
fs.mkdirSync(configDir, { recursive: true });
|
|
53
|
-
await waitFor(() => fs.existsSync(configDir));
|
|
54
|
-
|
|
55
|
-
console.log(`Created config folder ${configDir}`);
|
|
56
|
-
|
|
57
|
-
const password = uuid();
|
|
58
|
-
fs.writeFileSync(
|
|
59
|
-
credentialsPath,
|
|
60
|
-
JSON.stringify({ username: "admin", password })
|
|
61
|
-
);
|
|
62
|
-
console.log(`Created credentials at ${credentialsPath}`);
|
|
63
|
-
return password;
|
|
57
|
+
export const stopAndWait = (server: http.Server) => {
|
|
58
|
+
let closed = false;
|
|
59
|
+
server.on("close", () => {
|
|
60
|
+
closed = true;
|
|
61
|
+
});
|
|
62
|
+
server.close();
|
|
63
|
+
return waitFor(() => closed);
|
|
64
64
|
};
|
|
65
65
|
|
|
66
|
-
export const loadOrCreatePassword = async (): Promise<string> => {
|
|
67
|
-
try {
|
|
68
|
-
return await loadPassword();
|
|
69
|
-
} catch (error) {
|
|
70
|
-
if (error instanceof NotFoundError) {
|
|
71
|
-
return createPassword();
|
|
72
|
-
}
|
|
73
|
-
throw error;
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
66
|
export const startServerWithNode = async (properties: {
|
|
77
|
-
directory
|
|
67
|
+
directory: string;
|
|
78
68
|
domain?: string;
|
|
79
69
|
bootstrap?: boolean;
|
|
70
|
+
newSession?: boolean;
|
|
71
|
+
ports?: {
|
|
72
|
+
node: number;
|
|
73
|
+
api: number;
|
|
74
|
+
};
|
|
75
|
+
restart?: () => void;
|
|
80
76
|
}) => {
|
|
77
|
+
if (!fs.existsSync(properties.directory)) {
|
|
78
|
+
fs.mkdirSync(properties.directory, { recursive: true });
|
|
79
|
+
}
|
|
80
|
+
const keypair = await getKeypair(properties.directory);
|
|
81
81
|
const peer = await create({
|
|
82
|
-
directory:
|
|
82
|
+
directory:
|
|
83
|
+
properties.directory != null
|
|
84
|
+
? getNodePath(properties.directory)
|
|
85
|
+
: undefined,
|
|
83
86
|
domain: properties.domain,
|
|
87
|
+
listenPort: properties.ports?.node,
|
|
88
|
+
peerId: await keypair.toPeerId(),
|
|
84
89
|
});
|
|
85
90
|
|
|
86
91
|
if (properties.bootstrap) {
|
|
87
92
|
await peer.bootstrap();
|
|
88
93
|
}
|
|
94
|
+
const sessionDirectory =
|
|
95
|
+
properties.directory != null
|
|
96
|
+
? path.join(properties.directory, "session")
|
|
97
|
+
: undefined;
|
|
98
|
+
|
|
99
|
+
const session = new Session(
|
|
100
|
+
sessionDirectory
|
|
101
|
+
? new Level<string, Uint8Array>(sessionDirectory, {
|
|
102
|
+
valueEncoding: "view",
|
|
103
|
+
keyEncoding: "utf-8",
|
|
104
|
+
})
|
|
105
|
+
: new MemoryLevel({ valueEncoding: "view", keyEncoding: "utf-8" })
|
|
106
|
+
);
|
|
107
|
+
if (!properties.newSession) {
|
|
108
|
+
for (const [string] of await session.imports.all()) {
|
|
109
|
+
await import(string);
|
|
110
|
+
}
|
|
111
|
+
for (const [address] of await session.programs.all()) {
|
|
112
|
+
// TODO args
|
|
113
|
+
try {
|
|
114
|
+
await peer.open(address, { timeout: 3000 });
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.error(error);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
} else {
|
|
120
|
+
await session.clear();
|
|
121
|
+
}
|
|
89
122
|
|
|
90
|
-
const server = await
|
|
123
|
+
const server = await startApiServer(peer, {
|
|
124
|
+
port: properties.ports?.api,
|
|
125
|
+
configDirectory:
|
|
126
|
+
properties.directory != null
|
|
127
|
+
? path.join(properties.directory, "server")
|
|
128
|
+
: undefined || getHomeConfigDir(),
|
|
129
|
+
session,
|
|
130
|
+
});
|
|
91
131
|
const printNodeInfo = async () => {
|
|
92
132
|
console.log("Starting node with address(es): ");
|
|
93
|
-
const id =
|
|
133
|
+
const id = peer.peerId.toString();
|
|
94
134
|
console.log("id: " + id);
|
|
95
135
|
console.log("Addresses: ");
|
|
96
|
-
for (const a of
|
|
136
|
+
for (const a of peer.getMultiaddrs()) {
|
|
97
137
|
console.log(a.toString());
|
|
98
138
|
}
|
|
99
139
|
};
|
|
@@ -101,27 +141,33 @@ export const startServerWithNode = async (properties: {
|
|
|
101
141
|
await printNodeInfo();
|
|
102
142
|
const shutDownHook = async (
|
|
103
143
|
controller: { stop: () => any },
|
|
104
|
-
server:
|
|
105
|
-
close: () => void;
|
|
106
|
-
}
|
|
144
|
+
server: http.Server
|
|
107
145
|
) => {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
146
|
+
["SIGTERM", "SIGINT", "SIGUSR1", "SIGUSR2"].forEach((code) => {
|
|
147
|
+
process.on(code, async () => {
|
|
148
|
+
if (server.listening) {
|
|
149
|
+
console.log("Shutting down node");
|
|
150
|
+
await stopAndWait(server);
|
|
151
|
+
await waitFor(() => closed);
|
|
152
|
+
await controller.stop();
|
|
153
|
+
}
|
|
154
|
+
exit();
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
process.on("exit", async () => {
|
|
158
|
+
if (server.listening) {
|
|
159
|
+
console.log("Shutting down node");
|
|
160
|
+
await stopAndWait(server);
|
|
161
|
+
await waitFor(() => closed);
|
|
162
|
+
await controller.stop();
|
|
163
|
+
}
|
|
114
164
|
});
|
|
115
165
|
};
|
|
116
166
|
await shutDownHook(peer, server);
|
|
117
167
|
return { server, node: peer };
|
|
118
168
|
};
|
|
119
169
|
|
|
120
|
-
const
|
|
121
|
-
client: Peerbit,
|
|
122
|
-
req: http.IncomingMessage,
|
|
123
|
-
pathIndex: number
|
|
124
|
-
): Program | undefined => {
|
|
170
|
+
const getPathValue = (req: http.IncomingMessage, pathIndex: number): string => {
|
|
125
171
|
if (!req.url) {
|
|
126
172
|
throw new Error("Missing url");
|
|
127
173
|
}
|
|
@@ -133,42 +179,90 @@ const getProgramFromPath = (
|
|
|
133
179
|
throw new Error("Invalid path");
|
|
134
180
|
}
|
|
135
181
|
const address = decodeURIComponent(path[pathIndex]);
|
|
136
|
-
return
|
|
182
|
+
return address;
|
|
137
183
|
};
|
|
184
|
+
function findPeerbitProgramFolder(inputDirectory: string): string | null {
|
|
185
|
+
let currentDir = path.resolve(inputDirectory);
|
|
186
|
+
|
|
187
|
+
while (currentDir !== "/") {
|
|
188
|
+
// Stop at the root directory
|
|
189
|
+
const nodeModulesPath = path.join(currentDir, "node_modules");
|
|
190
|
+
const packageJsonPath = path.join(
|
|
191
|
+
nodeModulesPath,
|
|
192
|
+
"@peerbit",
|
|
193
|
+
"program",
|
|
194
|
+
"package.json"
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
198
|
+
const packageJsonContent = fs.readFileSync(packageJsonPath, "utf-8");
|
|
199
|
+
const packageData = JSON.parse(packageJsonContent);
|
|
200
|
+
|
|
201
|
+
if (packageData.name === "@peerbit/program") {
|
|
202
|
+
return currentDir;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
currentDir = path.dirname(currentDir);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
138
211
|
|
|
139
|
-
export const
|
|
212
|
+
export const startApiServer = async (
|
|
140
213
|
client: ProgramClient,
|
|
141
|
-
|
|
214
|
+
properties: {
|
|
215
|
+
configDirectory: string;
|
|
216
|
+
session?: Session;
|
|
217
|
+
port?: number;
|
|
218
|
+
}
|
|
142
219
|
): Promise<http.Server> => {
|
|
143
|
-
const
|
|
220
|
+
const port = properties?.port ?? LOCAL_PORT;
|
|
221
|
+
if (!fs.existsSync(properties.configDirectory)) {
|
|
222
|
+
fs.mkdirSync(properties.configDirectory, { recursive: true });
|
|
223
|
+
}
|
|
144
224
|
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
225
|
+
const trust = new Trust(getTrustPath(properties.configDirectory));
|
|
226
|
+
|
|
227
|
+
const restart = async () => {
|
|
228
|
+
await client.stop();
|
|
229
|
+
await stopAndWait(server);
|
|
230
|
+
|
|
231
|
+
// We filter out the reset command, since restarting means that we want to resume something
|
|
232
|
+
spawn(
|
|
233
|
+
process.argv.shift()!,
|
|
234
|
+
[
|
|
235
|
+
...process.execArgv,
|
|
236
|
+
...process.argv.filter((x) => x !== "--reset" && x !== "-r"),
|
|
237
|
+
],
|
|
238
|
+
{
|
|
239
|
+
cwd: process.cwd(),
|
|
240
|
+
detached: true,
|
|
241
|
+
stdio: "inherit",
|
|
242
|
+
gid: process.getgid!(),
|
|
243
|
+
}
|
|
244
|
+
);
|
|
245
|
+
process.exit(0);
|
|
159
246
|
};
|
|
247
|
+
if (!client.peerId.equals(await client.identity.publicKey.toPeerId())) {
|
|
248
|
+
throw new Error("Expecting node identity to equal peerId");
|
|
249
|
+
}
|
|
160
250
|
|
|
161
|
-
const
|
|
162
|
-
req
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
body
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
251
|
+
const getVerifiedBody = async (req: http.IncomingMessage) => {
|
|
252
|
+
const body = await getBody(req);
|
|
253
|
+
const result = await verifyRequest(
|
|
254
|
+
req.headers,
|
|
255
|
+
req.method!,
|
|
256
|
+
req.url!,
|
|
257
|
+
body
|
|
258
|
+
);
|
|
259
|
+
if (result.equals(client.identity.publicKey)) {
|
|
260
|
+
return body;
|
|
261
|
+
}
|
|
262
|
+
if (trust.isTrusted(result.hashcode())) {
|
|
263
|
+
return body;
|
|
264
|
+
}
|
|
265
|
+
throw new Error("Not trusted");
|
|
172
266
|
};
|
|
173
267
|
|
|
174
268
|
const e404 = "404";
|
|
@@ -186,15 +280,20 @@ export const startServer = async (
|
|
|
186
280
|
|
|
187
281
|
try {
|
|
188
282
|
if (req.url) {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
283
|
+
let body: string;
|
|
284
|
+
try {
|
|
285
|
+
body =
|
|
286
|
+
req.url.startsWith(PEER_ID_PATH) ||
|
|
287
|
+
req.url.startsWith(ADDRESS_PATH)
|
|
288
|
+
? await getBody(req)
|
|
289
|
+
: await getVerifiedBody(req);
|
|
290
|
+
} catch (error: any) {
|
|
194
291
|
res.writeHead(401);
|
|
195
|
-
res.end("Not authorized");
|
|
292
|
+
res.end("Not authorized: " + error.toString());
|
|
196
293
|
return;
|
|
197
|
-
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (req.url.startsWith(PROGRAMS_PATH)) {
|
|
198
297
|
if (client instanceof Peerbit === false) {
|
|
199
298
|
res.writeHead(400);
|
|
200
299
|
res.end("Server node is not running a native client");
|
|
@@ -203,9 +302,8 @@ export const startServer = async (
|
|
|
203
302
|
switch (req.method) {
|
|
204
303
|
case "GET":
|
|
205
304
|
try {
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
]);
|
|
305
|
+
const ref = (client as Peerbit).handler?.items?.keys() || [];
|
|
306
|
+
const keys = JSON.stringify([...ref]);
|
|
209
307
|
res.setHeader("Content-Type", "application/json");
|
|
210
308
|
res.writeHead(200);
|
|
211
309
|
res.end(keys);
|
|
@@ -228,7 +326,9 @@ export const startServer = async (
|
|
|
228
326
|
switch (req.method) {
|
|
229
327
|
case "HEAD":
|
|
230
328
|
try {
|
|
231
|
-
const program =
|
|
329
|
+
const program = (client as Peerbit).handler?.items.get(
|
|
330
|
+
getPathValue(req, 1)
|
|
331
|
+
);
|
|
232
332
|
if (program) {
|
|
233
333
|
res.writeHead(200);
|
|
234
334
|
res.end();
|
|
@@ -247,13 +347,22 @@ export const startServer = async (
|
|
|
247
347
|
const url = new URL(req.url, "http://localhost:" + 1234);
|
|
248
348
|
const queryData = url.searchParams.get("delete");
|
|
249
349
|
|
|
250
|
-
const program =
|
|
350
|
+
const program = (client as Peerbit).handler?.items.get(
|
|
351
|
+
getPathValue(req, 1)
|
|
352
|
+
);
|
|
251
353
|
if (program) {
|
|
354
|
+
let closed = false;
|
|
252
355
|
if (queryData === "true") {
|
|
253
|
-
await program.drop();
|
|
356
|
+
closed = await program.drop();
|
|
254
357
|
} else {
|
|
255
|
-
await program.close();
|
|
358
|
+
closed = await program.close();
|
|
359
|
+
}
|
|
360
|
+
if (closed) {
|
|
361
|
+
await properties?.session?.programs.remove(
|
|
362
|
+
program.address
|
|
363
|
+
);
|
|
256
364
|
}
|
|
365
|
+
|
|
257
366
|
res.writeHead(200);
|
|
258
367
|
res.end();
|
|
259
368
|
} else {
|
|
@@ -266,45 +375,54 @@ export const startServer = async (
|
|
|
266
375
|
}
|
|
267
376
|
break;
|
|
268
377
|
case "PUT":
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
const startArguments: StartProgram = JSON.parse(body);
|
|
378
|
+
try {
|
|
379
|
+
const startArguments: StartProgram = JSON.parse(body);
|
|
272
380
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
);
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
program = new P();
|
|
287
|
-
} else {
|
|
288
|
-
program = deserialize(
|
|
289
|
-
fromBase64((startArguments as StartByBase64).base64),
|
|
290
|
-
Program
|
|
381
|
+
let program: Program;
|
|
382
|
+
if ((startArguments as StartByVariant).variant) {
|
|
383
|
+
const P = getProgramFromVariant(
|
|
384
|
+
(startArguments as StartByVariant).variant
|
|
385
|
+
);
|
|
386
|
+
if (!P) {
|
|
387
|
+
res.writeHead(400);
|
|
388
|
+
res.end(
|
|
389
|
+
"Missing program with variant: " +
|
|
390
|
+
(startArguments as StartByVariant).variant
|
|
291
391
|
);
|
|
392
|
+
return;
|
|
292
393
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
.catch((error) => {
|
|
300
|
-
res.writeHead(400);
|
|
301
|
-
res.end("Failed to open program: " + error.toString());
|
|
302
|
-
});
|
|
303
|
-
} catch (error: any) {
|
|
304
|
-
res.writeHead(400);
|
|
305
|
-
res.end(error.toString());
|
|
394
|
+
program = new P();
|
|
395
|
+
} else {
|
|
396
|
+
program = deserialize(
|
|
397
|
+
fromBase64((startArguments as StartByBase64).base64),
|
|
398
|
+
Program
|
|
399
|
+
);
|
|
306
400
|
}
|
|
307
|
-
|
|
401
|
+
client
|
|
402
|
+
.open(program) // TODO all users to pass args
|
|
403
|
+
.then(async (program) => {
|
|
404
|
+
// TODO what if this is a reopen?
|
|
405
|
+
console.log(
|
|
406
|
+
"OPEN ADDRESS",
|
|
407
|
+
program.address,
|
|
408
|
+
(client as Peerbit).directory,
|
|
409
|
+
await client.services.blocks.has(program.address)
|
|
410
|
+
);
|
|
411
|
+
await properties?.session?.programs.add(
|
|
412
|
+
program.address,
|
|
413
|
+
new Uint8Array()
|
|
414
|
+
);
|
|
415
|
+
res.writeHead(200);
|
|
416
|
+
res.end(program.address.toString());
|
|
417
|
+
})
|
|
418
|
+
.catch((error) => {
|
|
419
|
+
res.writeHead(400);
|
|
420
|
+
res.end("Failed to open program: " + error.toString());
|
|
421
|
+
});
|
|
422
|
+
} catch (error: any) {
|
|
423
|
+
res.writeHead(400);
|
|
424
|
+
res.end(error.toString());
|
|
425
|
+
}
|
|
308
426
|
break;
|
|
309
427
|
|
|
310
428
|
default:
|
|
@@ -313,71 +431,101 @@ export const startServer = async (
|
|
|
313
431
|
}
|
|
314
432
|
} else if (req.url.startsWith(INSTALL_PATH)) {
|
|
315
433
|
switch (req.method) {
|
|
316
|
-
case "PUT":
|
|
317
|
-
|
|
318
|
-
const name = body;
|
|
434
|
+
case "PUT": {
|
|
435
|
+
const installArgs: InstallDependency = JSON.parse(body);
|
|
319
436
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
437
|
+
const packageName = installArgs.name; // @abc/123
|
|
438
|
+
let installName = installArgs.name; // abc123.tgz or @abc/123 (npm package name)
|
|
439
|
+
let clear: (() => void) | undefined;
|
|
440
|
+
if (installArgs.type === "tgz") {
|
|
441
|
+
const binary = fromBase64(installArgs.base64);
|
|
442
|
+
const tempFile = tmp.fileSync({
|
|
443
|
+
name:
|
|
444
|
+
base58btc.encode(Buffer.from(installName)) +
|
|
445
|
+
uuid() +
|
|
446
|
+
".tgz",
|
|
447
|
+
});
|
|
448
|
+
fs.writeFileSync(tempFile.fd, binary);
|
|
449
|
+
clear = () => tempFile.removeCallback();
|
|
450
|
+
installName = tempFile.name;
|
|
451
|
+
} else {
|
|
452
|
+
clear = undefined;
|
|
453
|
+
}
|
|
324
454
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
455
|
+
if (!installName || installName.length === 0) {
|
|
456
|
+
res.writeHead(400);
|
|
457
|
+
res.end("Invalid package: " + packageName);
|
|
458
|
+
} else {
|
|
459
|
+
try {
|
|
460
|
+
// TODO do this without sudo. i.e. for servers provide arguments so that this app folder is writeable by default by the user
|
|
461
|
+
const installDir =
|
|
462
|
+
process.env.PEERBIT_MODULES_PATH ||
|
|
463
|
+
findPeerbitProgramFolder(__dirname);
|
|
464
|
+
let permission = "";
|
|
465
|
+
if (!installDir) {
|
|
336
466
|
res.writeHead(400);
|
|
337
|
-
res.end(
|
|
338
|
-
"Failed ot install library: " +
|
|
339
|
-
name +
|
|
340
|
-
". " +
|
|
341
|
-
error.toString()
|
|
342
|
-
);
|
|
467
|
+
res.end("Missing installation directory");
|
|
343
468
|
return;
|
|
344
469
|
}
|
|
345
|
-
|
|
346
470
|
try {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
);
|
|
471
|
+
fs.accessSync(installDir, fs.constants.W_OK);
|
|
472
|
+
} catch (error) {
|
|
473
|
+
permission = "sudo";
|
|
474
|
+
}
|
|
352
475
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
476
|
+
console.log("Installing package: " + installName);
|
|
477
|
+
execSync(
|
|
478
|
+
`${permission} npm install ${installName} --prefix ${installDir} --no-save --no-package-lock`
|
|
479
|
+
); // TODO omit=dev ? but this makes breaks the tests after running once?
|
|
480
|
+
} catch (error: any) {
|
|
481
|
+
res.writeHead(400);
|
|
482
|
+
res.end(
|
|
483
|
+
"Failed to install library: " +
|
|
484
|
+
packageName +
|
|
485
|
+
". " +
|
|
486
|
+
error.toString()
|
|
487
|
+
);
|
|
488
|
+
clear?.();
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
365
491
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
492
|
+
try {
|
|
493
|
+
const programsPre = new Set(
|
|
494
|
+
getProgramFromVariants().map((x) => getSchema(x).variant)
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
await import(
|
|
498
|
+
/* webpackIgnore: true */ /* @vite-ignore */ packageName
|
|
499
|
+
);
|
|
500
|
+
await properties?.session?.imports.add(
|
|
501
|
+
packageName,
|
|
502
|
+
new Uint8Array()
|
|
503
|
+
);
|
|
504
|
+
const programsPost = getProgramFromVariants()?.map((x) =>
|
|
505
|
+
getSchema(x)
|
|
506
|
+
);
|
|
507
|
+
const newPrograms: { variant: string }[] = [];
|
|
508
|
+
for (const p of programsPost) {
|
|
509
|
+
if (!programsPre.has(p.variant)) {
|
|
510
|
+
newPrograms.push(p as { variant: string });
|
|
511
|
+
}
|
|
373
512
|
}
|
|
513
|
+
|
|
514
|
+
res.writeHead(200);
|
|
515
|
+
res.end(JSON.stringify(newPrograms.map((x) => x.variant)));
|
|
516
|
+
} catch (e: any) {
|
|
517
|
+
res.writeHead(400);
|
|
518
|
+
res.end(e.message.toString?.());
|
|
519
|
+
clear?.();
|
|
374
520
|
}
|
|
375
|
-
}
|
|
521
|
+
}
|
|
376
522
|
break;
|
|
523
|
+
}
|
|
377
524
|
|
|
378
|
-
default:
|
|
525
|
+
default: {
|
|
379
526
|
r404();
|
|
380
527
|
break;
|
|
528
|
+
}
|
|
381
529
|
}
|
|
382
530
|
} else if (req.url.startsWith(BOOTSTRAP_PATH)) {
|
|
383
531
|
switch (req.method) {
|
|
@@ -392,6 +540,48 @@ export const startServer = async (
|
|
|
392
540
|
res.end();
|
|
393
541
|
break;
|
|
394
542
|
|
|
543
|
+
default:
|
|
544
|
+
r404();
|
|
545
|
+
break;
|
|
546
|
+
}
|
|
547
|
+
} else if (req.url.startsWith(TRUST_PATH)) {
|
|
548
|
+
switch (req.method) {
|
|
549
|
+
case "PUT": {
|
|
550
|
+
trust.add(getPathValue(req, 1));
|
|
551
|
+
res.writeHead(200);
|
|
552
|
+
res.end();
|
|
553
|
+
break;
|
|
554
|
+
}
|
|
555
|
+
case "DELETE": {
|
|
556
|
+
const removed = trust.remove(getPathValue(req, 1));
|
|
557
|
+
res.writeHead(200);
|
|
558
|
+
res.end(removed);
|
|
559
|
+
break;
|
|
560
|
+
}
|
|
561
|
+
default:
|
|
562
|
+
r404();
|
|
563
|
+
break;
|
|
564
|
+
}
|
|
565
|
+
} else if (req.url.startsWith(RESTART_PATH)) {
|
|
566
|
+
switch (req.method) {
|
|
567
|
+
case "POST":
|
|
568
|
+
res.writeHead(200);
|
|
569
|
+
res.end();
|
|
570
|
+
restart();
|
|
571
|
+
break;
|
|
572
|
+
|
|
573
|
+
default:
|
|
574
|
+
r404();
|
|
575
|
+
break;
|
|
576
|
+
}
|
|
577
|
+
} else if (req.url.startsWith(TERMINATE_PATH)) {
|
|
578
|
+
switch (req.method) {
|
|
579
|
+
case "POST":
|
|
580
|
+
res.writeHead(200);
|
|
581
|
+
res.end();
|
|
582
|
+
process.exit(0);
|
|
583
|
+
break;
|
|
584
|
+
|
|
395
585
|
default:
|
|
396
586
|
r404();
|
|
397
587
|
break;
|
|
@@ -431,6 +621,7 @@ export const startServer = async (
|
|
|
431
621
|
});
|
|
432
622
|
});
|
|
433
623
|
});
|
|
624
|
+
await waitFor(() => server.listening);
|
|
434
625
|
console.log("API available at port", port);
|
|
435
626
|
return server;
|
|
436
627
|
};
|