@blocklet/cli 1.16.33 → 1.16.34-beta-20241120-080738-bbbe036c
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/README.md +32 -25
- package/bin/blocklet.js +292 -1
- package/config.example.yml +33 -0
- package/lib/arcblock.js +53 -0
- package/lib/commands/blocklet/add.js +124 -0
- package/lib/commands/blocklet/assets/git-ignore +28 -0
- package/lib/commands/blocklet/assets/index.html +9 -0
- package/lib/commands/blocklet/assets/index.js +14 -0
- package/lib/commands/blocklet/assets/logo.png +0 -0
- package/lib/commands/blocklet/bundle/bundle.js +184 -0
- package/lib/commands/blocklet/bundle/bundlers/blocklet.js +138 -0
- package/lib/commands/blocklet/bundle/bundlers/changelog.js +100 -0
- package/lib/commands/blocklet/bundle/bundlers/logo.js +56 -0
- package/lib/commands/blocklet/bundle/bundlers/markdown.js +241 -0
- package/lib/commands/blocklet/bundle/bundlers/preference.js +50 -0
- package/lib/commands/blocklet/bundle/bundlers/readme.js +43 -0
- package/lib/commands/blocklet/bundle/bundlers/screenshots.js +94 -0
- package/lib/commands/blocklet/bundle/bundlers/simple.js +70 -0
- package/lib/commands/blocklet/bundle/compact/bundle-compact-file.js +48 -0
- package/lib/commands/blocklet/bundle/compact/bundle-merge-extra.js +66 -0
- package/lib/commands/blocklet/bundle/compact/default-external.js +5 -0
- package/lib/commands/blocklet/bundle/compact/index.js +88 -0
- package/lib/commands/blocklet/bundle/index.js +139 -0
- package/lib/commands/blocklet/bundle/pack.js +8 -0
- package/lib/commands/blocklet/bundle/parse-external-dependencies.js +97 -0
- package/lib/commands/blocklet/bundle/simple/index.js +62 -0
- package/lib/commands/blocklet/bundle/zip/archive.js +35 -0
- package/lib/commands/blocklet/bundle/zip/dependencies.js +333 -0
- package/lib/commands/blocklet/bundle/zip/index.js +165 -0
- package/lib/commands/blocklet/bundle/zip/main.js +124 -0
- package/lib/commands/blocklet/bundle/zip/node.js +59 -0
- package/lib/commands/blocklet/bundle/zip/resolve.js +93 -0
- package/lib/commands/blocklet/cleanup.js +52 -0
- package/lib/commands/blocklet/config.js +108 -0
- package/lib/commands/blocklet/connect.js +87 -0
- package/lib/commands/blocklet/create.js +38 -0
- package/lib/commands/blocklet/deploy.js +435 -0
- package/lib/commands/blocklet/dev.js +1000 -0
- package/lib/commands/blocklet/document.js +39 -0
- package/lib/commands/blocklet/exec.js +106 -0
- package/lib/commands/blocklet/init.js +300 -0
- package/lib/commands/blocklet/meta.js +22 -0
- package/lib/commands/blocklet/remove.js +35 -0
- package/lib/commands/blocklet/test.js +201 -0
- package/lib/commands/blocklet/upload.js +105 -0
- package/lib/commands/blocklet/version.js +81 -0
- package/lib/commands/server/cleanup.js +32 -0
- package/lib/commands/server/command.js +131 -0
- package/lib/commands/server/info.js +92 -0
- package/lib/commands/server/init.js +433 -0
- package/lib/commands/server/logs.js +99 -0
- package/lib/commands/server/rescue.js +71 -0
- package/lib/commands/server/start.js +821 -0
- package/lib/commands/server/status.js +107 -0
- package/lib/commands/server/stop.js +163 -0
- package/lib/commands/server/upgrade.js +123 -0
- package/lib/constant.js +21 -2
- package/lib/debug.js +20 -0
- package/lib/manager/config.js +122 -0
- package/lib/manager/deploy.js +75 -0
- package/lib/manager/index.js +23 -0
- package/lib/manager/process.js +47 -0
- package/lib/node.js +214 -0
- package/lib/port.js +19 -0
- package/lib/postinstall.js +3 -0
- package/lib/process/daemon.js +196 -0
- package/lib/process/service.js +86 -0
- package/lib/ui.js +137 -0
- package/lib/util/blocklet/config.js +78 -0
- package/lib/util/blocklet/env.js +172 -0
- package/lib/util/blocklet/meta.js +36 -0
- package/lib/util/blocklet/payment.js +88 -0
- package/lib/util/blocklet/sign.js +21 -0
- package/lib/util/blocklet/tar.js +119 -0
- package/lib/util/convert-to-nosources-sourcemap.js +37 -0
- package/lib/util/docker-status-log.js +17 -0
- package/lib/util/exit-when-server-stopped.js +44 -0
- package/lib/util/get-cli-binary-name.js +8 -0
- package/lib/util/get-download-bundle-step.js +36 -0
- package/lib/util/get-service-instance-number.js +12 -0
- package/lib/util/index.js +626 -0
- package/lib/util/print-error.js +11 -0
- package/lib/util/print.js +9 -0
- package/lib/util/what-uri.js +40 -0
- package/package.json +123 -27
- package/lib/run.d.ts +0 -2
- package/lib/run.js +0 -73
|
@@ -0,0 +1,626 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const toLower = require('lodash/toLower');
|
|
4
|
+
const get = require('lodash/get');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const rc = require('rc');
|
|
7
|
+
const updateNotifier = require('update-notifier');
|
|
8
|
+
const { filesize } = require('filesize');
|
|
9
|
+
const fs = require('fs-extra');
|
|
10
|
+
const git = require('git-rev-sync');
|
|
11
|
+
const uniq = require('lodash/uniq');
|
|
12
|
+
const portUsed = require('port-used');
|
|
13
|
+
const pRetry = require('p-retry');
|
|
14
|
+
const { joinURL } = require('ufo');
|
|
15
|
+
const getNodeWallet = require('@abtnode/util/lib/get-app-wallet');
|
|
16
|
+
const isDocker = require('@abtnode/util/lib/is-docker');
|
|
17
|
+
const isGitpod = require('@abtnode/util/lib/is-gitpod');
|
|
18
|
+
const ensureListening = require('@abtnode/util/lib/ensure-listening');
|
|
19
|
+
const pm2 = require('@abtnode/util/lib/async-pm2');
|
|
20
|
+
const getIP = require('@abtnode/util/lib/get-ip');
|
|
21
|
+
const sleep = require('@abtnode/util/lib/sleep');
|
|
22
|
+
const axios = require('@abtnode/util/lib/axios');
|
|
23
|
+
const cloud = require('@abtnode/util/lib/cloud');
|
|
24
|
+
const { updateServerDocument } = require('@abtnode/util/lib/did-document');
|
|
25
|
+
const { getProvider } = require('@abtnode/router-provider');
|
|
26
|
+
const { decideHttpPort, decideHttpsPort } = require('@abtnode/router-provider/lib/util');
|
|
27
|
+
const tryWithTimeout = require('@abtnode/util/lib/try-with-timeout');
|
|
28
|
+
const codespaces = require('@abtnode/util/lib/codespaces');
|
|
29
|
+
const {
|
|
30
|
+
PROCESS_NAME_EVENT_HUB,
|
|
31
|
+
PROXY_MAX_MEM_LIMIT_IN_MB,
|
|
32
|
+
DEFAULT_DESCRIPTION,
|
|
33
|
+
DEFAULT_HTTP_PORT,
|
|
34
|
+
NODE_MODES,
|
|
35
|
+
DEFAULT_HTTPS_PORT,
|
|
36
|
+
} = require('@abtnode/constant');
|
|
37
|
+
const gitUserName = require('git-user-name');
|
|
38
|
+
|
|
39
|
+
const pkg = require('../../package.json');
|
|
40
|
+
const { symbols, wrapSpinner } = require('../ui');
|
|
41
|
+
const { getInternalPort: getPort } = require('../port');
|
|
42
|
+
const debug = require('../debug')('util');
|
|
43
|
+
const getCLIBinaryName = require('./get-cli-binary-name');
|
|
44
|
+
const printError = require('./print-error');
|
|
45
|
+
const { print } = require('./print');
|
|
46
|
+
|
|
47
|
+
const { version } = require('../../package.json');
|
|
48
|
+
const { HELP_DOCS_GITHUB_CODESPACES_URL } = require('../constant');
|
|
49
|
+
|
|
50
|
+
function printInfo(...args) {
|
|
51
|
+
print.apply(null, [symbols.info, ...args]);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function printSuccess(...args) {
|
|
55
|
+
print.apply(null, [symbols.success, ...args]);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function printWarning(...args) {
|
|
59
|
+
print.apply(null, [symbols.warning, ...args]);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const isValidYamlFileName = (filePath) => ['.yml', '.yaml'].includes(toLower(path.extname(filePath)));
|
|
63
|
+
|
|
64
|
+
function getNPMConfig(key) {
|
|
65
|
+
const conf = rc('npm');
|
|
66
|
+
return get(conf, key, '');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getUserName(author) {
|
|
70
|
+
return author || gitUserName() || os.userInfo().username;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function checkUpdate() {
|
|
74
|
+
debug('check update');
|
|
75
|
+
const UPDATE_CHECK_INTERVAL = 1000 * 60 * 60 * 24; // one day
|
|
76
|
+
const notifier = updateNotifier({
|
|
77
|
+
pkg: { name: pkg.name, version: pkg.version },
|
|
78
|
+
updateCheckInterval: UPDATE_CHECK_INTERVAL,
|
|
79
|
+
});
|
|
80
|
+
if (notifier && notifier.update) {
|
|
81
|
+
notifier.notify({
|
|
82
|
+
message:
|
|
83
|
+
// eslint-disable-next-line
|
|
84
|
+
'New version available! ' +
|
|
85
|
+
chalk.dim(notifier.update.current) +
|
|
86
|
+
chalk.reset(' → ') +
|
|
87
|
+
chalk.green(notifier.update.latest) +
|
|
88
|
+
' \nRun ' +
|
|
89
|
+
chalk.cyan('blocklet server upgrade') +
|
|
90
|
+
' to get latest version',
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const cleanupProcessByName = async (name) => {
|
|
96
|
+
try {
|
|
97
|
+
const [info] = await pm2.describeAsync(name);
|
|
98
|
+
if (info) {
|
|
99
|
+
await pm2.deleteAsync(name);
|
|
100
|
+
}
|
|
101
|
+
} catch {
|
|
102
|
+
// ignore
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @param {string} info.provider routing provider name
|
|
108
|
+
* @param {number} info.httpPort
|
|
109
|
+
* @param {number} info.httpsPort
|
|
110
|
+
*/
|
|
111
|
+
const checkRoutingProvider = async (info, { configDir } = {}) => {
|
|
112
|
+
const Provider = getProvider(info.provider);
|
|
113
|
+
if (!Provider) {
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const result = await Provider.check({ configDir });
|
|
118
|
+
if (!result.available) {
|
|
119
|
+
printError(result.error);
|
|
120
|
+
printError('For more info on how to solve the problem: https://developer.blocklet.io/docs');
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ensure ports are available for the routing engine
|
|
125
|
+
if (!result.running && !result.managed) {
|
|
126
|
+
const httpPort = decideHttpPort(info.httpPort);
|
|
127
|
+
if (await portUsed.check(httpPort)) {
|
|
128
|
+
printError(
|
|
129
|
+
`Port ${chalk.cyan(httpPort)} is required by routing engine ${chalk.cyan(
|
|
130
|
+
info.provider
|
|
131
|
+
)}, but already in use, please free the port before retry.`
|
|
132
|
+
);
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const httpsPort = decideHttpsPort(info.httpsPort);
|
|
137
|
+
if (await portUsed.check(httpsPort)) {
|
|
138
|
+
printError(
|
|
139
|
+
`Port ${chalk.cyan(httpsPort)} is required by routing engine ${chalk.cyan(
|
|
140
|
+
info.provider
|
|
141
|
+
)}, but already in use, please free the port before retry.`
|
|
142
|
+
);
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return true;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const stopRouting = async ({ info, routerDir }) => {
|
|
151
|
+
const providerName = get(info, 'routing.provider', '');
|
|
152
|
+
const Provider = getProvider(providerName);
|
|
153
|
+
if (!Provider) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const httpPort = info.routing.httpPort || DEFAULT_HTTP_PORT;
|
|
158
|
+
const httpsPort = info.routing.httpsPort || DEFAULT_HTTPS_PORT;
|
|
159
|
+
|
|
160
|
+
const provider = new Provider({ configDir: path.join(routerDir, providerName), httpPort, httpsPort });
|
|
161
|
+
await provider.stop();
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const getFileSize = (file) => {
|
|
165
|
+
try {
|
|
166
|
+
const stats = fs.statSync(file);
|
|
167
|
+
return filesize(stats.size, { base: 2 });
|
|
168
|
+
} catch (err) {
|
|
169
|
+
return 'NaN';
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const getCLICommandName = () => {
|
|
174
|
+
const binaryName = getCLIBinaryName();
|
|
175
|
+
const [, , action] = process.argv;
|
|
176
|
+
|
|
177
|
+
if (action === 'server') {
|
|
178
|
+
return `${binaryName} ${action}`;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return binaryName;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const getCLIVersion = () => {
|
|
185
|
+
return version;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
function formatGQLError(error) {
|
|
189
|
+
const err = error.response ? error.response.data : error;
|
|
190
|
+
if (Array.isArray(err.errors)) {
|
|
191
|
+
return err.errors.map((x) => x.message).join(', ');
|
|
192
|
+
}
|
|
193
|
+
return err.message;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* @param {Array} url[<url>]
|
|
198
|
+
* @param {String} url[].url
|
|
199
|
+
* @param {String} url[].type
|
|
200
|
+
*/
|
|
201
|
+
const printAccessUrls = (urls, indent = 0) => {
|
|
202
|
+
const paddingLeft = Array.from(Array(indent).keys()).reduce((s) => `${s} `, '');
|
|
203
|
+
const httpUrls = urls.filter((x) => x.url.startsWith('http://'));
|
|
204
|
+
const httpsUrls = urls.filter((x) => x.url.startsWith('https://'));
|
|
205
|
+
|
|
206
|
+
if (httpUrls.length) {
|
|
207
|
+
print(`\n${paddingLeft}HTTP URLs:\n`);
|
|
208
|
+
httpUrls.forEach((x) => print(`${paddingLeft}- ${x.url}`.padStart(10)));
|
|
209
|
+
}
|
|
210
|
+
if (httpsUrls.length) {
|
|
211
|
+
print(`\n${paddingLeft}Secure URLs (Recommended):\n`);
|
|
212
|
+
httpsUrls.forEach((x) => print(`${paddingLeft}- ${x.url}`.padStart(10)));
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const getGitHash = (dir) => git.long(dir);
|
|
217
|
+
|
|
218
|
+
const getCliCwd = () => path.dirname(path.dirname(__dirname));
|
|
219
|
+
|
|
220
|
+
const startEventHub = async (logDir, maxMemoryRestart = PROXY_MAX_MEM_LIMIT_IN_MB) => {
|
|
221
|
+
const port = getPort(PROCESS_NAME_EVENT_HUB);
|
|
222
|
+
debug('start event hub', { port });
|
|
223
|
+
|
|
224
|
+
const tryStartEventHub = async () => {
|
|
225
|
+
try {
|
|
226
|
+
await wrapSpinner('Starting event hub...', async () => {
|
|
227
|
+
await pm2.startAsync({
|
|
228
|
+
namespace: 'daemon',
|
|
229
|
+
name: PROCESS_NAME_EVENT_HUB,
|
|
230
|
+
script: require.resolve('@arcblock/event-hub/lib/server-abtnode.js'),
|
|
231
|
+
max_memory_restart: `${maxMemoryRestart}M`,
|
|
232
|
+
output: path.join(logDir, 'event.output.log'),
|
|
233
|
+
error: path.join(logDir, 'event.error.log'),
|
|
234
|
+
cwd: getCliCwd(),
|
|
235
|
+
wait_ready: true,
|
|
236
|
+
max_restarts: 3,
|
|
237
|
+
listen_timeout: 3000,
|
|
238
|
+
time: true,
|
|
239
|
+
env: {
|
|
240
|
+
ABT_NODE_EVENT_PORT: port,
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
await tryWithTimeout(() => ensureListening(port), 10000);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
process.env.ABT_NODE_EVENT_PORT = port;
|
|
247
|
+
} catch (err) {
|
|
248
|
+
debug('Blocklet Event Hub start failed: ', err);
|
|
249
|
+
printError(`Blocklet Event Hub start failed: ${err.message}`);
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
await tryStartEventHub();
|
|
254
|
+
|
|
255
|
+
return port;
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const killPm2Process = async (name) => {
|
|
259
|
+
const [info] = await pm2.describeAsync(name);
|
|
260
|
+
if (!info) {
|
|
261
|
+
printWarning(`${name} is not running`);
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const proc = await pm2.deleteAsync(name);
|
|
266
|
+
return proc;
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const fixFiles = (meta, dir) => {
|
|
270
|
+
const files = meta.files || ['blocklet.md', 'screenshots'];
|
|
271
|
+
const packageFile = path.join(dir, 'package.json');
|
|
272
|
+
let packageFiles = [];
|
|
273
|
+
if (fs.existsSync(packageFile)) {
|
|
274
|
+
try {
|
|
275
|
+
const packageJson = JSON.parse(fs.readdirSync(packageFile).toString());
|
|
276
|
+
if (Array.isArray(packageJson.files)) {
|
|
277
|
+
packageFiles = packageJson.files.filter((x) => typeof x === 'string');
|
|
278
|
+
}
|
|
279
|
+
} catch (err) {
|
|
280
|
+
// Do nothing
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const uniqFiles = uniq([...files, ...packageFiles]).filter((file) => {
|
|
285
|
+
return !(!file || typeof file !== 'string');
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
meta.files = uniqFiles;
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
const getDevUrl = async ({
|
|
292
|
+
isGitpod: _isGitpod = isGitpod,
|
|
293
|
+
gitpodWorkspaceURL = process.env.GITPOD_WORKSPACE_URL,
|
|
294
|
+
abtnodeHttpPort = process.env.ABT_NODE_HTTP_PORT,
|
|
295
|
+
isDocker: _isDocker = isDocker,
|
|
296
|
+
getUrl = () => '',
|
|
297
|
+
}) => {
|
|
298
|
+
// Gitpod
|
|
299
|
+
if (_isGitpod()) {
|
|
300
|
+
const gitpodURL = gitpodWorkspaceURL;
|
|
301
|
+
return gitpodURL.replace(/^https?:\/\//g, (p) => `${p}${abtnodeHttpPort}-`);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Docker
|
|
305
|
+
if (_isDocker() && !process.env.ABT_NODE_HOST) {
|
|
306
|
+
return 'http://127.0.0.1';
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Local
|
|
310
|
+
const url = await getUrl();
|
|
311
|
+
return url || '';
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
const getDataDir = () => path.join(os.homedir(), '.arcblock');
|
|
315
|
+
|
|
316
|
+
const readInitInfoFromEc2 = () => {
|
|
317
|
+
try {
|
|
318
|
+
const info = fs.readFileSync(path.join(os.homedir(), '.arcblock/abtnode/user-data')).toString().trim();
|
|
319
|
+
return JSON.parse(info);
|
|
320
|
+
} catch {
|
|
321
|
+
return {
|
|
322
|
+
ownerDid: null,
|
|
323
|
+
issuerDid: null,
|
|
324
|
+
initialBlocklets: [],
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
const printVersionTip = () => {
|
|
330
|
+
const versionTip = `${getCLIBinaryName()} ${process.argv[2]} v${version}`;
|
|
331
|
+
print(chalk.bold(versionTip));
|
|
332
|
+
debug(`Nodejs: ${process.version}`);
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
const checkTerminalProxy = (name) => {
|
|
336
|
+
const httpsProxy = process.env.HTTPS_PROXY || process.env.https_proxy || '';
|
|
337
|
+
const httpProxy = process.env.HTTP_PROXY || process.env.http_proxy || '';
|
|
338
|
+
if (httpsProxy) {
|
|
339
|
+
printWarning(
|
|
340
|
+
chalk.black(chalk.bgYellow(`HTTPS_PROXY detected: ${httpsProxy}, which may cause ${name} not working properly.`))
|
|
341
|
+
);
|
|
342
|
+
printWarning(
|
|
343
|
+
chalk.black(chalk.bgYellow(`If that happens, please clear the environment variable and restart ${name}.`))
|
|
344
|
+
);
|
|
345
|
+
} else if (httpProxy) {
|
|
346
|
+
printWarning(
|
|
347
|
+
chalk.black(chalk.bgYellow(`HTTP_PROXY detected: ${httpProxy}, which may cause ${name} not working properly.`))
|
|
348
|
+
);
|
|
349
|
+
printWarning(
|
|
350
|
+
chalk.black(chalk.bgYellow(`If that happens, please clear the environment variable and restart ${name}.`))
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
const getIpDescription = async () => {
|
|
356
|
+
let defaultIP = '';
|
|
357
|
+
let nodeDescription = '';
|
|
358
|
+
const ips = await getIP({ timeout: 1000 });
|
|
359
|
+
|
|
360
|
+
if (isDocker()) {
|
|
361
|
+
nodeDescription = 'Blocklet Server on docker';
|
|
362
|
+
} else if (await cloud.isInCloud()) {
|
|
363
|
+
defaultIP = ips.external || ips.internal; // use external ip by default on AWS EC2
|
|
364
|
+
nodeDescription = `Blocklet Server on cloud [${defaultIP}]`;
|
|
365
|
+
} else {
|
|
366
|
+
defaultIP = ips.internal || ips.external; // use internal ip by default on local
|
|
367
|
+
nodeDescription = `Blocklet Server on [${defaultIP}]`;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return { ip: defaultIP, description: nodeDescription };
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
const getDefaultName = () => `Blocklet Server [${os.userInfo().username}]`;
|
|
374
|
+
|
|
375
|
+
const getDefaultDescription = async () => {
|
|
376
|
+
try {
|
|
377
|
+
// eslint-disable-next-line no-use-before-define
|
|
378
|
+
const { description } = await lib.getIpDescription(); // module.exports.getIpDescription is for unit test
|
|
379
|
+
return description;
|
|
380
|
+
} catch {
|
|
381
|
+
// do nothing
|
|
382
|
+
return DEFAULT_DESCRIPTION;
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
const isDaemonIpAccessible = async ({ ip, port, adminPath }) => {
|
|
387
|
+
try {
|
|
388
|
+
const urlObj = new URL(`http://${ip}`);
|
|
389
|
+
urlObj.port = port;
|
|
390
|
+
urlObj.pathname = joinURL(adminPath, '/api/gql');
|
|
391
|
+
|
|
392
|
+
const url = urlObj.href;
|
|
393
|
+
|
|
394
|
+
const ping = async () => {
|
|
395
|
+
debug(`ping daemon(${ip}:${port}), url: ${url}`);
|
|
396
|
+
const { data } = await axios.post(
|
|
397
|
+
url,
|
|
398
|
+
JSON.stringify({
|
|
399
|
+
query: `{
|
|
400
|
+
getNodeInfo {
|
|
401
|
+
code
|
|
402
|
+
info {
|
|
403
|
+
did
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}`,
|
|
407
|
+
}),
|
|
408
|
+
{
|
|
409
|
+
headers: {
|
|
410
|
+
'Content-Type': 'application/json',
|
|
411
|
+
Accept: 'application/json',
|
|
412
|
+
},
|
|
413
|
+
timeout: 3 * 1000,
|
|
414
|
+
}
|
|
415
|
+
);
|
|
416
|
+
|
|
417
|
+
const ok = !!get(data, 'data.getNodeInfo.info.did');
|
|
418
|
+
if (!ok) {
|
|
419
|
+
await sleep(1000);
|
|
420
|
+
throw new Error('Daemon is unavailable');
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
// 重试 2 次,总共 ping 3 次
|
|
425
|
+
await pRetry(ping, { retries: 2 });
|
|
426
|
+
return true;
|
|
427
|
+
} catch (error) {
|
|
428
|
+
debug(`check daemon ip(${ip}:${port}) is accessible failed`, error.message);
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
const getAccessibleIps = async (info, forceIntranet = false) => {
|
|
434
|
+
const nodeHttpPort = info.routing.httpPort;
|
|
435
|
+
|
|
436
|
+
const ips = await getIP({ includeExternal: !forceIntranet });
|
|
437
|
+
const getDaemonPort = (port, defaultPort) => (port && port !== defaultPort ? `${port}` : '');
|
|
438
|
+
const port = getDaemonPort(nodeHttpPort, DEFAULT_HTTP_PORT);
|
|
439
|
+
const result = { internal: ips.internal };
|
|
440
|
+
|
|
441
|
+
// 云环境下下完全相信获取到的 IP 信息
|
|
442
|
+
if ((await cloud.isInCloud()) === true) {
|
|
443
|
+
result.external = ips.external;
|
|
444
|
+
return result;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// eslint-disable-next-line no-use-before-define
|
|
448
|
+
if (await lib.isDaemonIpAccessible({ ip: ips.external, port, adminPath: info.routing.adminPath })) {
|
|
449
|
+
result.external = ips.external;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
return result;
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
const prettyAvailableModes = () =>
|
|
456
|
+
Object.values(NODE_MODES)
|
|
457
|
+
.map((d) => `"${d}"`)
|
|
458
|
+
.join(', ');
|
|
459
|
+
|
|
460
|
+
const printInvalidModeInfo = () => {
|
|
461
|
+
printError('Invalid mode');
|
|
462
|
+
printInfo(`Valid mode should be ${prettyAvailableModes()}`);
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
const wrapDefaultStoreUrl =
|
|
466
|
+
(url) =>
|
|
467
|
+
({ source: { name } }) => {
|
|
468
|
+
printInfo(`Using store ${chalk.cyan(url)} to download component ${chalk.cyan(name)}`);
|
|
469
|
+
return url;
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
const isValidMode = (mode) => Object.values(NODE_MODES).includes(mode);
|
|
473
|
+
|
|
474
|
+
const ensurePermission = (dataDir) => {
|
|
475
|
+
try {
|
|
476
|
+
// eslint-disable-next-line no-bitwise
|
|
477
|
+
fs.accessSync(dataDir, fs.constants.R_OK | fs.constants.W_OK);
|
|
478
|
+
} catch (err) {
|
|
479
|
+
printError(`Can not access directory ${chalk.cyan(dataDir)}, please check the permission`);
|
|
480
|
+
process.exit(1);
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* if index.html or index.htm in not in folder meta.main (cwd: dir), throw error
|
|
486
|
+
*/
|
|
487
|
+
const checkEntryFileForStaticBlocklet = (meta, dir) => {
|
|
488
|
+
const main = meta.main || '.';
|
|
489
|
+
const mainDir = path.join(dir, main);
|
|
490
|
+
const hasEntryFile =
|
|
491
|
+
fs.existsSync(path.join(mainDir, 'index.html')) || fs.existsSync(path.join(mainDir, 'index.htm'));
|
|
492
|
+
|
|
493
|
+
if (!hasEntryFile) {
|
|
494
|
+
throw new Error(`Can not find index.html or index.htm in ${mainDir}`);
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
const updateDidDocument = async (...args) => {
|
|
499
|
+
try {
|
|
500
|
+
await updateServerDocument(...args);
|
|
501
|
+
return { status: 'success' };
|
|
502
|
+
} catch (err) {
|
|
503
|
+
return { status: 'error', message: get(err, 'response.data.error') || err.message };
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
const getDaemonAccessUrls = async ({ info, wallet, getBaseUrls, forceIntranet }) => {
|
|
508
|
+
let accessUrls = [];
|
|
509
|
+
|
|
510
|
+
if (codespaces.isCodespaces()) {
|
|
511
|
+
const url = codespaces.getAccessUrl({ port: info.routing.httpsPort, pathName: info.routing.adminPath });
|
|
512
|
+
accessUrls.push({ url });
|
|
513
|
+
} else {
|
|
514
|
+
const accessibleIps = [];
|
|
515
|
+
if (process.env.ABT_NODE_HOST) {
|
|
516
|
+
accessibleIps.push(process.env.ABT_NODE_HOST);
|
|
517
|
+
} else {
|
|
518
|
+
const ips = await wrapSpinner(
|
|
519
|
+
`Fetching accessible IPs (forceIntranet: ${JSON.stringify(forceIntranet)})...`,
|
|
520
|
+
() => getAccessibleIps(info, forceIntranet),
|
|
521
|
+
{
|
|
522
|
+
throwOnError: false,
|
|
523
|
+
printErrorFn: printError,
|
|
524
|
+
}
|
|
525
|
+
);
|
|
526
|
+
|
|
527
|
+
if (ips.external) {
|
|
528
|
+
accessibleIps.push(ips.external); // 如果公网 IP 可访问,那么它是 accessibleIps 的第一个元素
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if (ips.internal) {
|
|
532
|
+
accessibleIps.push(ips.internal);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
debug('accessible ips', accessibleIps);
|
|
537
|
+
|
|
538
|
+
if (accessibleIps.length > 0) {
|
|
539
|
+
if (wallet) {
|
|
540
|
+
await wrapSpinner(
|
|
541
|
+
'Updating DID Domain...',
|
|
542
|
+
async () => {
|
|
543
|
+
const result = await updateDidDocument({
|
|
544
|
+
ips: [accessibleIps[0]],
|
|
545
|
+
didRegistryUrl: info.didRegistry,
|
|
546
|
+
wallet,
|
|
547
|
+
domain: info.didDomain,
|
|
548
|
+
blockletServerVersion: version,
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
if (result.status === 'success') {
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
throw new Error(result.message);
|
|
556
|
+
},
|
|
557
|
+
{ throwOnError: false, printErrorFn: printError }
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
printSuccess('You can access your Blocklet Server with either of the following URLs');
|
|
562
|
+
accessUrls = await getBaseUrls(accessibleIps);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
return accessUrls;
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
const printCodespacesDevelopmentGuide = () => {
|
|
570
|
+
printInfo(
|
|
571
|
+
`For detailed guidance on developing blocklets in GitHub Codespaces, please visit: ${chalk.cyan(HELP_DOCS_GITHUB_CODESPACES_URL)}`
|
|
572
|
+
);
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
const printBlockletDevelopmentGuide = () => {
|
|
576
|
+
if (codespaces.isCodespaces()) {
|
|
577
|
+
printCodespacesDevelopmentGuide();
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
const lib = {
|
|
582
|
+
checkRoutingProvider,
|
|
583
|
+
checkTerminalProxy,
|
|
584
|
+
print,
|
|
585
|
+
printError,
|
|
586
|
+
printInfo,
|
|
587
|
+
printSuccess,
|
|
588
|
+
printWarning,
|
|
589
|
+
printBlockletDevelopmentGuide,
|
|
590
|
+
isValidYamlFileName,
|
|
591
|
+
getNPMConfig,
|
|
592
|
+
checkUpdate,
|
|
593
|
+
stopRouting,
|
|
594
|
+
getFileSize,
|
|
595
|
+
getCLIBinaryName,
|
|
596
|
+
getCLICommandName,
|
|
597
|
+
getCLIVersion,
|
|
598
|
+
getAccessibleIps,
|
|
599
|
+
formatGQLError,
|
|
600
|
+
printAccessUrls,
|
|
601
|
+
getGitHash,
|
|
602
|
+
getCliCwd,
|
|
603
|
+
startEventHub,
|
|
604
|
+
killPm2Process,
|
|
605
|
+
getUserName,
|
|
606
|
+
fixFiles,
|
|
607
|
+
getDevUrl,
|
|
608
|
+
getDataDir,
|
|
609
|
+
getWallet: getNodeWallet,
|
|
610
|
+
readInitInfoFromEc2,
|
|
611
|
+
printVersionTip,
|
|
612
|
+
printInvalidModeInfo,
|
|
613
|
+
getIpDescription,
|
|
614
|
+
getDefaultName,
|
|
615
|
+
getDefaultDescription,
|
|
616
|
+
wrapDefaultStoreUrl,
|
|
617
|
+
isValidMode,
|
|
618
|
+
prettyAvailableModes,
|
|
619
|
+
isDaemonIpAccessible,
|
|
620
|
+
ensurePermission,
|
|
621
|
+
checkEntryFileForStaticBlocklet,
|
|
622
|
+
cleanupProcessByName,
|
|
623
|
+
getDaemonAccessUrls,
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
module.exports = lib;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const { symbols } = require('../ui');
|
|
2
|
+
const debug = require('../debug')('util');
|
|
3
|
+
|
|
4
|
+
module.exports = function printError(...args) {
|
|
5
|
+
debug(...args);
|
|
6
|
+
if (args.length && args[0] instanceof Error) {
|
|
7
|
+
args[0] = args[0].message;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
console.error.apply(null, [symbols.error, ...args]);
|
|
11
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const isUrl = require('is-url');
|
|
2
|
+
const isURI = require('validate.io-uri');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
*
|
|
7
|
+
* @param {string} uri
|
|
8
|
+
* @return {boolean}
|
|
9
|
+
*/
|
|
10
|
+
function isAnchor(uri) {
|
|
11
|
+
return uri && uri.startsWith('#');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
*
|
|
16
|
+
*
|
|
17
|
+
* @param {string} uri
|
|
18
|
+
* @return {boolean}
|
|
19
|
+
*/
|
|
20
|
+
function isNoHeaderUri(uri) {
|
|
21
|
+
return uri && (uri.startsWith('://') || uri.startsWith('//'));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
*
|
|
26
|
+
*
|
|
27
|
+
* @param {string} uri
|
|
28
|
+
* @return {boolean}
|
|
29
|
+
*/
|
|
30
|
+
function isLocalUri(uri) {
|
|
31
|
+
if (!uri) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return !(isURI(uri) || isAnchor(uri) || isNoHeaderUri(uri) || isUrl(uri));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = {
|
|
39
|
+
isLocalUri,
|
|
40
|
+
};
|