@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.
Files changed (87) hide show
  1. package/README.md +32 -25
  2. package/bin/blocklet.js +292 -1
  3. package/config.example.yml +33 -0
  4. package/lib/arcblock.js +53 -0
  5. package/lib/commands/blocklet/add.js +124 -0
  6. package/lib/commands/blocklet/assets/git-ignore +28 -0
  7. package/lib/commands/blocklet/assets/index.html +9 -0
  8. package/lib/commands/blocklet/assets/index.js +14 -0
  9. package/lib/commands/blocklet/assets/logo.png +0 -0
  10. package/lib/commands/blocklet/bundle/bundle.js +184 -0
  11. package/lib/commands/blocklet/bundle/bundlers/blocklet.js +138 -0
  12. package/lib/commands/blocklet/bundle/bundlers/changelog.js +100 -0
  13. package/lib/commands/blocklet/bundle/bundlers/logo.js +56 -0
  14. package/lib/commands/blocklet/bundle/bundlers/markdown.js +241 -0
  15. package/lib/commands/blocklet/bundle/bundlers/preference.js +50 -0
  16. package/lib/commands/blocklet/bundle/bundlers/readme.js +43 -0
  17. package/lib/commands/blocklet/bundle/bundlers/screenshots.js +94 -0
  18. package/lib/commands/blocklet/bundle/bundlers/simple.js +70 -0
  19. package/lib/commands/blocklet/bundle/compact/bundle-compact-file.js +48 -0
  20. package/lib/commands/blocklet/bundle/compact/bundle-merge-extra.js +66 -0
  21. package/lib/commands/blocklet/bundle/compact/default-external.js +5 -0
  22. package/lib/commands/blocklet/bundle/compact/index.js +88 -0
  23. package/lib/commands/blocklet/bundle/index.js +139 -0
  24. package/lib/commands/blocklet/bundle/pack.js +8 -0
  25. package/lib/commands/blocklet/bundle/parse-external-dependencies.js +97 -0
  26. package/lib/commands/blocklet/bundle/simple/index.js +62 -0
  27. package/lib/commands/blocklet/bundle/zip/archive.js +35 -0
  28. package/lib/commands/blocklet/bundle/zip/dependencies.js +333 -0
  29. package/lib/commands/blocklet/bundle/zip/index.js +165 -0
  30. package/lib/commands/blocklet/bundle/zip/main.js +124 -0
  31. package/lib/commands/blocklet/bundle/zip/node.js +59 -0
  32. package/lib/commands/blocklet/bundle/zip/resolve.js +93 -0
  33. package/lib/commands/blocklet/cleanup.js +52 -0
  34. package/lib/commands/blocklet/config.js +108 -0
  35. package/lib/commands/blocklet/connect.js +87 -0
  36. package/lib/commands/blocklet/create.js +38 -0
  37. package/lib/commands/blocklet/deploy.js +435 -0
  38. package/lib/commands/blocklet/dev.js +1000 -0
  39. package/lib/commands/blocklet/document.js +39 -0
  40. package/lib/commands/blocklet/exec.js +106 -0
  41. package/lib/commands/blocklet/init.js +300 -0
  42. package/lib/commands/blocklet/meta.js +22 -0
  43. package/lib/commands/blocklet/remove.js +35 -0
  44. package/lib/commands/blocklet/test.js +201 -0
  45. package/lib/commands/blocklet/upload.js +105 -0
  46. package/lib/commands/blocklet/version.js +81 -0
  47. package/lib/commands/server/cleanup.js +32 -0
  48. package/lib/commands/server/command.js +131 -0
  49. package/lib/commands/server/info.js +92 -0
  50. package/lib/commands/server/init.js +433 -0
  51. package/lib/commands/server/logs.js +99 -0
  52. package/lib/commands/server/rescue.js +71 -0
  53. package/lib/commands/server/start.js +821 -0
  54. package/lib/commands/server/status.js +107 -0
  55. package/lib/commands/server/stop.js +163 -0
  56. package/lib/commands/server/upgrade.js +123 -0
  57. package/lib/constant.js +21 -2
  58. package/lib/debug.js +20 -0
  59. package/lib/manager/config.js +122 -0
  60. package/lib/manager/deploy.js +75 -0
  61. package/lib/manager/index.js +23 -0
  62. package/lib/manager/process.js +47 -0
  63. package/lib/node.js +214 -0
  64. package/lib/port.js +19 -0
  65. package/lib/postinstall.js +3 -0
  66. package/lib/process/daemon.js +196 -0
  67. package/lib/process/service.js +86 -0
  68. package/lib/ui.js +137 -0
  69. package/lib/util/blocklet/config.js +78 -0
  70. package/lib/util/blocklet/env.js +172 -0
  71. package/lib/util/blocklet/meta.js +36 -0
  72. package/lib/util/blocklet/payment.js +88 -0
  73. package/lib/util/blocklet/sign.js +21 -0
  74. package/lib/util/blocklet/tar.js +119 -0
  75. package/lib/util/convert-to-nosources-sourcemap.js +37 -0
  76. package/lib/util/docker-status-log.js +17 -0
  77. package/lib/util/exit-when-server-stopped.js +44 -0
  78. package/lib/util/get-cli-binary-name.js +8 -0
  79. package/lib/util/get-download-bundle-step.js +36 -0
  80. package/lib/util/get-service-instance-number.js +12 -0
  81. package/lib/util/index.js +626 -0
  82. package/lib/util/print-error.js +11 -0
  83. package/lib/util/print.js +9 -0
  84. package/lib/util/what-uri.js +40 -0
  85. package/package.json +123 -27
  86. package/lib/run.d.ts +0 -2
  87. package/lib/run.js +0 -73
@@ -0,0 +1,47 @@
1
+ const path = require('path');
2
+ const pm2 = require('@abtnode/util/lib/async-pm2');
3
+
4
+ const { PROCESS_NAME_DAEMON } = require('@abtnode/constant');
5
+
6
+ const getPm2ProcessInfo = (name) =>
7
+ new Promise((resolve, reject) => {
8
+ pm2.describe(name, (err, [info]) => {
9
+ if (err) {
10
+ return reject(err);
11
+ }
12
+
13
+ return resolve(info);
14
+ });
15
+ });
16
+
17
+ const isRunning = async (name) => {
18
+ const info = await getPm2ProcessInfo(name);
19
+ return !!info;
20
+ };
21
+
22
+ const checkRunning = async () => {
23
+ const isDaemonRunning = await isRunning(PROCESS_NAME_DAEMON);
24
+ return isDaemonRunning === true;
25
+ };
26
+
27
+ const getRunningConfigFile = async () => {
28
+ if (!(await isRunning(PROCESS_NAME_DAEMON))) {
29
+ return '';
30
+ }
31
+
32
+ const instanceInfo = await getPm2ProcessInfo(PROCESS_NAME_DAEMON);
33
+ const { ABT_NODE_CONFIG_FILE: runningInstanceConfigFile = '' } = instanceInfo.pm2_env.env;
34
+
35
+ return runningInstanceConfigFile;
36
+ };
37
+
38
+ const getRunningConfigDataDir = async () => {
39
+ const configFile = await getRunningConfigFile();
40
+ if (configFile) {
41
+ return path.dirname(configFile);
42
+ }
43
+
44
+ return '';
45
+ };
46
+
47
+ module.exports = { checkRunning, getPm2ProcessInfo, getRunningConfigFile, getRunningConfigDataDir };
package/lib/node.js ADDED
@@ -0,0 +1,214 @@
1
+ const fs = require('fs');
2
+ const chalk = require('chalk');
3
+ const yaml = require('js-yaml');
4
+ const ABTNode = require('@abtnode/core');
5
+ const security = require('@abtnode/util/lib/security');
6
+ const { WsClient } = require('@arcblock/ws');
7
+ const { isFromPublicKey } = require('@arcblock/did');
8
+ const { getBaseUrls } = require('@abtnode/core/lib/util');
9
+
10
+ const debug = require('./debug')('node');
11
+ const { version } = require('../package.json');
12
+
13
+ const util = require('./util');
14
+ const {
15
+ getDataDirectoryByConfigFile,
16
+ getConfigFile,
17
+ joinKFile,
18
+ getConfigDir,
19
+ verifyDataDirectory,
20
+ } = require('./manager');
21
+
22
+ const { print, printError, printInfo } = util;
23
+
24
+ const isHex = (str) => /^0x[0-9a-f]+$/i.test(str);
25
+
26
+ const getOwner = (info) => {
27
+ if (info && info.pk && info.did && isFromPublicKey(info.did, info.pk)) {
28
+ return info;
29
+ }
30
+
31
+ return undefined;
32
+ };
33
+
34
+ const printConfigError = () => {
35
+ printError('Cannot find config file for Blocklet Server');
36
+ printInfo(`Please initialize a configuration directory first: run ${chalk.cyan('blocklet server init')}`);
37
+ print();
38
+ };
39
+
40
+ let instance = null;
41
+ let client = null;
42
+
43
+ const readNodeConfig = async (dir) => {
44
+ const configDir = await getConfigDir(dir);
45
+ if (fs.existsSync(configDir)) {
46
+ try {
47
+ verifyDataDirectory(configDir);
48
+ } catch (error) {
49
+ debug('validate data directory failed:', error);
50
+ printError('Validate data directory failed:', error.message);
51
+ process.exit(1);
52
+ }
53
+ } else {
54
+ printConfigError();
55
+ process.exit(1); // TODO: 太讨厌这种提前 exit 的函数了,非常不好调试, polunzh
56
+ }
57
+
58
+ const configFile = await getConfigFile(dir);
59
+
60
+ let config = {};
61
+ try {
62
+ config = yaml.load(fs.readFileSync(configFile).toString(), { json: true });
63
+ const ukFile = joinKFile(configDir);
64
+ if (fs.existsSync(ukFile) && isHex(config.node.sk) === false) {
65
+ config.node.sk = security.decrypt(config.node.sk, config.node.did, fs.readFileSync(ukFile));
66
+ }
67
+ } catch (err) {
68
+ debug(err);
69
+ printError('Blocklet Server config parse failed', err.message);
70
+ process.exit(1);
71
+ }
72
+
73
+ return { config, configFile, dataDir: getDataDirectoryByConfigFile(configFile) };
74
+ };
75
+
76
+ const readNodeConfigWithValidate = async (dir) => {
77
+ const { config, configFile, dataDir } = await readNodeConfig(dir);
78
+
79
+ // validate node config
80
+ const wallet = util.getWallet(config.node.sk);
81
+ if (wallet.address !== config.node.did) {
82
+ printError('config.node.sk and config.node.did does not match');
83
+ process.exit(1);
84
+ }
85
+
86
+ if (!config.node.port) {
87
+ printError('config.node.port must be specified');
88
+ process.exit(1);
89
+ }
90
+
91
+ printInfo('Server DID', chalk.cyan(config.node.did));
92
+
93
+ return { config, configFile, dataDir };
94
+ };
95
+
96
+ async function getNode({ dir = process.cwd() } = {}) {
97
+ const { config, configFile, dataDir } = await readNodeConfigWithValidate(dir);
98
+
99
+ util.ensurePermission(dataDir);
100
+
101
+ printInfo('Server config from', chalk.cyan(configFile));
102
+
103
+ const wallet = util.getWallet(config.node.sk);
104
+ const node = ABTNode({
105
+ version,
106
+ nodeSk: wallet.secretKey,
107
+ nodePk: wallet.publicKey,
108
+ nodeDid: wallet.address,
109
+ port: config.node.port,
110
+ dataDir,
111
+ nodeOwner: getOwner(config.node.owner),
112
+ name: config.node.name,
113
+ description: config.node.description,
114
+ blockletPort: config.blocklet.port,
115
+ routing: config.node.routing,
116
+ docker: config.node.docker,
117
+ mode: config.node.mode,
118
+ runtimeConfig: config.node.runtimeConfig,
119
+ ownerNft: config.node.ownerNft || { holder: '', issuer: '' },
120
+ launcher: config.node.launcher,
121
+ didRegistry: config.node.didRegistry,
122
+ didDomain: config.node.didDomain,
123
+ slpDomain: config.node.slpDomain,
124
+ enablePassportIssuance: config.node.enablePassportIssuance,
125
+ trustedPassports: config.node.trustedPassports,
126
+ webWalletUrl: config.node.webWalletUrl,
127
+ });
128
+
129
+ return {
130
+ node,
131
+ config,
132
+ dataDir,
133
+ configFile,
134
+ wallet,
135
+ getBaseUrls: (ips) => getBaseUrls(node, ips),
136
+ publishEvent: (event, data) => publishEvent(node, event, data),
137
+ getWsClient: () => getWsClient(node),
138
+ };
139
+ }
140
+
141
+ async function getWsClient(node) {
142
+ if (!client) {
143
+ const info = await node.getNodeInfo();
144
+ const endpoint = `ws://127.0.0.1:${info.port}`;
145
+ const wallet = util.getWallet(info.sk);
146
+ const timestamp = Date.now().toString();
147
+
148
+ client = new WsClient(endpoint, {
149
+ silent: true,
150
+ params: () => ({
151
+ token: wallet.sign(timestamp),
152
+ timestamp,
153
+ }),
154
+ });
155
+
156
+ process.on('exit', () => {
157
+ client.disconnect();
158
+ });
159
+ }
160
+
161
+ if (client.isConnected()) {
162
+ return client;
163
+ }
164
+
165
+ return new Promise((resolve, reject) => {
166
+ client.onOpen(() => {
167
+ resolve(client);
168
+ });
169
+
170
+ client.onError((err) => {
171
+ reject(new Error(`Failed to connect to daemon socket server: ${err.message}`));
172
+ });
173
+
174
+ client.connect();
175
+ });
176
+ }
177
+
178
+ function publishEvent(node, event, data) {
179
+ return new Promise((resolve, reject) => {
180
+ getWsClient(node)
181
+ .then((c) => {
182
+ const channel = c.channel('cli');
183
+ channel
184
+ .join()
185
+ .receive('ok', () => {
186
+ channel.push(event, data);
187
+ channel.leave();
188
+ resolve(true);
189
+ })
190
+ .receive('error', (err) => {
191
+ reject(new Error(`join channel error: ${err.message}`));
192
+ })
193
+ .receive('timeout', () => {
194
+ reject(new Error('join channel timeout: cli'));
195
+ });
196
+ })
197
+ .catch((err) => reject(err));
198
+ });
199
+ }
200
+
201
+ module.exports = {
202
+ getNode: async (...args) => {
203
+ if (instance) {
204
+ return instance;
205
+ }
206
+
207
+ instance = await getNode(...args);
208
+ return instance;
209
+ },
210
+ readNodeConfig,
211
+ readNodeConfigWithValidate,
212
+ publishEvent,
213
+ getWsClient,
214
+ };
package/lib/port.js ADDED
@@ -0,0 +1,19 @@
1
+ const {
2
+ PROCESS_NAME_PROXY,
3
+ PROCESS_NAME_UPDATER,
4
+ PROCESS_NAME_SERVICE,
5
+ PROCESS_NAME_EVENT_HUB,
6
+ } = require('@abtnode/constant');
7
+
8
+ const ports = {
9
+ [PROCESS_NAME_PROXY]: 40404,
10
+ [PROCESS_NAME_UPDATER]: 40405,
11
+ [PROCESS_NAME_SERVICE]: 40406,
12
+ [PROCESS_NAME_EVENT_HUB]: 40407,
13
+ };
14
+
15
+ const getInternalPort = (processName) => {
16
+ return ports[processName];
17
+ };
18
+
19
+ module.exports = { getInternalPort };
@@ -0,0 +1,3 @@
1
+ const { ensureNewInstalled } = require('./arcblock');
2
+
3
+ ensureNewInstalled();
@@ -0,0 +1,196 @@
1
+ /* eslint-disable no-console */
2
+ /* eslint-disable no-await-in-loop */
3
+ if (!process.env.NODE_ENV) {
4
+ process.env.NODE_ENV = 'production';
5
+ }
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+
10
+ process.env.ABT_NODE_LOG_NAME = 'daemon';
11
+ process.env.ABT_NODE_LOG_DIR = path.join(process.env.ABT_NODE_DATA_DIR, 'logs', '_abtnode'); // enable log
12
+
13
+ const runningBefore = !!process.env.BLOCKLET_SERVER_RUNNING_BEFORE;
14
+
15
+ const logger = require('@abtnode/logger')('daemon');
16
+ const ABTNode = require('@abtnode/core');
17
+ const AsyncQueue = require('async/queue');
18
+ const formatContext = require('@abtnode/util/lib/format-context');
19
+ const getNodeWallet = require('@abtnode/util/lib/get-app-wallet');
20
+ const { BlockletStatus } = require('@blocklet/constant');
21
+ const { getBlockletEngine } = require('@blocklet/meta/lib/engine');
22
+ const { findComponentV2, forEachComponentV2, hasStartEngine, getDisplayName } = require('@blocklet/meta/lib/util');
23
+ const getComponentProcessId = require('@blocklet/meta/lib/get-component-process-id');
24
+ const getPm2ProcessInfo = require('@abtnode/util/lib/get-pm2-process-info');
25
+ const { SERVER_STATUS } = require('@abtnode/constant');
26
+
27
+ const createServer = require('@abtnode/webapp/blocklet'); // eslint-disable-line
28
+
29
+ const wallet = getNodeWallet(process.env.ABT_NODE_SK);
30
+ const dataDir = process.env.ABT_NODE_DATA_DIR;
31
+
32
+ const node = ABTNode({
33
+ nodeSk: wallet.secretKey,
34
+ nodePk: wallet.publicKey,
35
+ nodeDid: wallet.address,
36
+ port: process.env.ABT_NODE_PORT,
37
+ dataDir,
38
+ blockletPort: process.env.ABT_NODE_BLOCKLET_PORT,
39
+ daemon: true,
40
+ });
41
+
42
+ node.collectorPath = path.dirname(require.resolve('@blocklet/form-collector'));
43
+
44
+ const createAuditLog = (action, args, result) => {
45
+ return node
46
+ .createAuditLog(
47
+ {
48
+ action,
49
+ args,
50
+ context: formatContext({
51
+ user: { fullName: 'CLI', role: 'admin', did: wallet.address },
52
+ headers: { 'user-agent': 'CLI' },
53
+ }),
54
+ result,
55
+ },
56
+ node
57
+ )
58
+ .catch(console.error);
59
+ };
60
+
61
+ const getComponentDids = async ({ app }) => {
62
+ const processNotRunnings = [];
63
+ await forEachComponentV2(app, async (component) => {
64
+ if (component.status !== BlockletStatus.running) {
65
+ return;
66
+ }
67
+
68
+ if (!hasStartEngine(component.meta)) {
69
+ return;
70
+ }
71
+
72
+ if (getBlockletEngine(component.meta)?.interpreter === 'blocklet') {
73
+ return;
74
+ }
75
+
76
+ const processId = getComponentProcessId(component, [app]);
77
+ try {
78
+ const info = await getPm2ProcessInfo(processId);
79
+ if (info.pm2_env.status !== 'online') {
80
+ // process not running
81
+ processNotRunnings.push(component.meta.did);
82
+ }
83
+ } catch {
84
+ // probably process not exists
85
+ processNotRunnings.push(component.meta.did);
86
+ }
87
+ });
88
+
89
+ return processNotRunnings;
90
+ };
91
+
92
+ const restartRunningComponent = async () => {
93
+ const queue = AsyncQueue(async (app, next) => {
94
+ logger.info(`Restarting app ${getDisplayName(app)}...`);
95
+ const componentDids = await getComponentDids({ app });
96
+
97
+ if (!componentDids.length) {
98
+ logger.info(`> No component need to restart for app ${getDisplayName(app)}`);
99
+ next();
100
+ return;
101
+ }
102
+
103
+ try {
104
+ const result = await node.startBlocklet({ did: app.meta.did, checkHealthImmediately: true, componentDids }, {});
105
+ logger.info(`> Restarted app ${getDisplayName(app)} / [${componentDids.join(',')}]`);
106
+ await createAuditLog('startBlocklet', { did: app.meta.did, reason: 'on server restart' }, result);
107
+ } catch (err) {
108
+ logger.error(`> Restarted app ${getDisplayName(app)} / [${componentDids.join(',')}] failed`, err);
109
+ }
110
+
111
+ next();
112
+ }, 1);
113
+
114
+ const apps = await node.getBlocklets({ includeRuntimeInfo: false });
115
+ const running = apps.filter((app) =>
116
+ findComponentV2(app, (component) => component.status === BlockletStatus.running)
117
+ );
118
+ if (running.length === 0) {
119
+ return;
120
+ }
121
+
122
+ running.forEach((app) => queue.push(app));
123
+ await queue.drain();
124
+ };
125
+
126
+ const syncComponentStatus = async () => {
127
+ const apps = await node.getBlocklets({ includeRuntimeInfo: false });
128
+ for (let i = 0; i < apps.length; i++) {
129
+ const app = apps[i];
130
+ try {
131
+ await node.syncBlockletStatus(app.meta.did, { forceSync: true });
132
+ } catch (err) {
133
+ logger.error(`Blocklet ${getDisplayName(app)} status sync failed: ${err.message}`);
134
+ }
135
+ }
136
+ };
137
+
138
+ logger.info('Waiting for Blocklet Server Daemon instance created');
139
+ node.onReady(async () => {
140
+ logger.info('Blocklet Server Daemon instance was created');
141
+ const info = await node.getNodeInfo();
142
+
143
+ await node.handleRouting(info);
144
+ logger.info('Service gateway was restarted');
145
+
146
+ const server = createServer(node);
147
+ server.listen(process.env.ABT_NODE_PORT, async (err) => {
148
+ if (err) throw err;
149
+ console.info(`> Blocklet Server Daemon ready on ${process.env.ABT_NODE_PORT}`);
150
+
151
+ // Should resume upgrading if we are in process
152
+ const session = await node.isBeingMaintained();
153
+ if (session) {
154
+ console.info('> Resume maintain', session);
155
+ await node.resumeMaintain(session);
156
+ }
157
+
158
+ try {
159
+ await node.cleanupDirtyMaintainState();
160
+ } catch (e) {
161
+ console.error('Blocklet Server failed to cleanup dirty maintain state', e);
162
+ }
163
+
164
+ try {
165
+ const initialized = await node.isInitialized();
166
+ if (initialized) {
167
+ // 无论之前是否 crash, 当启动 server (非热重启) 时,永远重启之前运行中的组件
168
+ await node.updateNodeStatus(SERVER_STATUS.RUNNING);
169
+ if (!runningBefore) {
170
+ await restartRunningComponent();
171
+ } else {
172
+ logger.info('> Blocklet Server is restarted from running before');
173
+ }
174
+
175
+ await syncComponentStatus();
176
+ }
177
+ } catch (e) {
178
+ console.error('> Blocklet Server failed to restart running blocklets', e);
179
+ }
180
+
181
+ try {
182
+ if (dataDir && fs.existsSync(path.join(dataDir, 'start.lock')) === false) {
183
+ await node.createNotification({
184
+ title: 'Blocklet Server Crashed',
185
+ description: `Seems like blocklet server (did: ${info.did}, pid: ${process.pid}) just recovered from a crash, please check logs for more detail.`,
186
+ entityType: 'node',
187
+ severity: 'error',
188
+ sticky: true,
189
+ action: '/logs/abtnode',
190
+ });
191
+ }
192
+ } catch {
193
+ // Do nothing
194
+ }
195
+ });
196
+ });
@@ -0,0 +1,86 @@
1
+ if (!process.env.NODE_ENV) {
2
+ process.env.NODE_ENV = 'production';
3
+ }
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+
8
+ const { BlockletStatus, BlockletInternalEvents } = require('@blocklet/constant');
9
+ const { filterComponentsV2 } = require('@blocklet/meta/lib/util');
10
+
11
+ process.env.ABT_NODE_LOG_NAME = 'service';
12
+ process.env.ABT_NODE_LOG_DIR = path.join(process.env.ABT_NODE_DATA_DIR, 'logs', '_abtnode'); // enable log
13
+
14
+ const ABTNode = require('@abtnode/core');
15
+ const getNodeWallet = require('@abtnode/util/lib/get-app-wallet');
16
+
17
+ const createServer = require('@abtnode/blocklet-services');
18
+
19
+ const { version } = require('../../package.json');
20
+
21
+ const wallet = getNodeWallet(process.env.ABT_NODE_SK);
22
+ const port = Number(process.env.ABT_NODE_SERVICE_PORT);
23
+ const dataDir = process.env.ABT_NODE_DATA_DIR;
24
+
25
+ const node = ABTNode({
26
+ nodeSk: wallet.secretKey,
27
+ nodePk: wallet.publicKey,
28
+ nodeDid: wallet.address,
29
+ port: process.env.ABT_NODE_PORT,
30
+ dataDir,
31
+ blockletPort: process.env.ABT_NODE_BLOCKLET_PORT,
32
+ service: true,
33
+ });
34
+
35
+ const notifyServerVersionToApps = async (server) => {
36
+ try {
37
+ const apps = await node.getBlocklets({ includeRuntimeInfo: false });
38
+ const runningApps = apps.filter(
39
+ (app) => filterComponentsV2(app, (x) => x.status === BlockletStatus.running).length
40
+ );
41
+ await Promise.all(
42
+ runningApps.map(async (app) => {
43
+ await server.sendToAppComponents({
44
+ appDid: app.appDid,
45
+ event: BlockletInternalEvents.appConfigChanged,
46
+ data: { configs: [{ key: 'ABT_NODE_VERSION', value: version }] },
47
+ });
48
+ await node.syncAppConfig(app, { serverVersion: version });
49
+ })
50
+ );
51
+ } catch (error) {
52
+ console.error(error);
53
+ }
54
+ };
55
+
56
+ node.onReady(() => {
57
+ const server = createServer(node);
58
+
59
+ server.listen(port, '127.0.0.1', async (err) => {
60
+ if (err) throw err;
61
+ // eslint-disable-next-line
62
+ console.log(`> Blocklet Service ready on ${port}`);
63
+
64
+ try {
65
+ if (dataDir && fs.existsSync(path.join(dataDir, 'start.lock')) === false) {
66
+ await node.createNotification({
67
+ title: 'Blocklet Service Crashed',
68
+ description: `Seems like blocklet service (pid: ${process.pid}) just recovered from a crash, please check logs for more detail.`,
69
+ entityType: 'node',
70
+ severity: 'error',
71
+ sticky: true,
72
+ action: '/logs/abtnode',
73
+ });
74
+ }
75
+ } catch {
76
+ // Do nothing
77
+ }
78
+
79
+ setTimeout(() => {
80
+ // Let first worker process do something as master
81
+ if (process.env.NODE_APP_INSTANCE === '0') {
82
+ notifyServerVersionToApps(server);
83
+ }
84
+ }, 1000 * 30);
85
+ });
86
+ });
package/lib/ui.js ADDED
@@ -0,0 +1,137 @@
1
+ const ora = require('ora');
2
+ const util = require('util');
3
+ const chalk = require('chalk');
4
+ const progress = require('cli-progress');
5
+ const symbols = require('log-symbols');
6
+
7
+ // https://github.com/sindresorhus/cli-spinners/blob/master/spinners.json
8
+ const spinners = [
9
+ 'dots',
10
+ 'dots2',
11
+ 'dots3',
12
+ 'dots4',
13
+ 'dots5',
14
+ 'dots6',
15
+ 'dots7',
16
+ 'dots8',
17
+ 'dots9',
18
+ 'dots10',
19
+ 'dots11',
20
+ 'dots12',
21
+ 'line',
22
+ 'line2',
23
+ 'pipe',
24
+ 'simpleDots',
25
+ 'simpleDotsScrolling',
26
+ 'star',
27
+ 'star2',
28
+ 'flip',
29
+ 'hamburger',
30
+ 'growVertical',
31
+ 'growHorizontal',
32
+ 'balloon',
33
+ 'balloon2',
34
+ 'noise',
35
+ 'bounce',
36
+ 'boxBounce',
37
+ 'boxBounce2',
38
+ 'triangle',
39
+ 'arc',
40
+ 'circle',
41
+ 'squareCorners',
42
+ 'circleQuarters',
43
+ 'circleHalves',
44
+ 'squish',
45
+ 'toggle',
46
+ 'toggle2',
47
+ 'toggle3',
48
+ 'toggle4',
49
+ 'toggle5',
50
+ 'toggle6',
51
+ 'toggle7',
52
+ 'toggle8',
53
+ 'toggle9',
54
+ 'toggle10',
55
+ 'toggle11',
56
+ 'toggle12',
57
+ 'toggle13',
58
+ 'arrow',
59
+ 'arrow2',
60
+ 'arrow3',
61
+ 'bouncingBar',
62
+ 'bouncingBall',
63
+ 'smiley',
64
+ 'monkey',
65
+ 'hearts',
66
+ 'clock',
67
+ 'earth',
68
+ 'moon',
69
+ 'runner',
70
+ 'pong',
71
+ 'shark',
72
+ 'dqpb',
73
+ 'weather',
74
+ 'christmas',
75
+ 'grenade',
76
+ 'point',
77
+ 'layer',
78
+ ];
79
+
80
+ const getSpinner = (opts) => {
81
+ const random = Math.floor(Math.random() * spinners.length);
82
+ const spinner = ora(Object.assign({ spinner: spinners[random] }, opts || {}));
83
+ if (typeof opts === 'string') {
84
+ spinner.text = opts;
85
+ }
86
+
87
+ return spinner;
88
+ };
89
+
90
+ const wrapSpinner = async (
91
+ // eslint-disable-next-line default-param-last
92
+ message = '',
93
+ func,
94
+ { throwOnError = true, printErrorFn = console.error, ref = {} } = {}
95
+ ) => {
96
+ const spinner = getSpinner(message);
97
+ ref.spinner = spinner;
98
+ const startTime = Date.now();
99
+ spinner.start();
100
+ try {
101
+ const result = await func();
102
+ spinner.succeed(`${message} Done in ${chalk.cyan((Date.now() - startTime) / 1000)}s`);
103
+ return result;
104
+ } catch (err) {
105
+ spinner.fail(`${message} Fail after ${chalk.cyan((Date.now() - startTime) / 1000)}s`);
106
+
107
+ if (typeof printErrorFn === 'function') {
108
+ printErrorFn(err.message);
109
+ }
110
+
111
+ if (throwOnError) {
112
+ throw err;
113
+ }
114
+
115
+ return err;
116
+ }
117
+ };
118
+
119
+ module.exports = {
120
+ symbols,
121
+ pretty: (data, options) => {
122
+ if (data && typeof data === 'object') {
123
+ return `\n${JSON.stringify(data, null, 2)}\n`;
124
+ }
125
+
126
+ return util.inspect(data, Object.assign({ depth: 8, colors: true, compact: false }, options));
127
+ },
128
+ getProgress: ({ title, unit = 'MB' }) =>
129
+ new progress.Bar({
130
+ format: `${title} |${chalk.cyan('{bar}')} {percentage}% || {value}/{total} ${unit}`,
131
+ barCompleteChar: '\u2588',
132
+ barIncompleteChar: '\u2591',
133
+ hideCursor: true,
134
+ }),
135
+ getSpinner,
136
+ wrapSpinner,
137
+ };