@abtnode/core 1.16.6-beta-8be2fe37 → 1.16.6-beta-4ea1eb90

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.
@@ -4,6 +4,7 @@ const fs = require('fs-extra');
4
4
  const path = require('path');
5
5
  const flat = require('flat');
6
6
  const get = require('lodash/get');
7
+ const omit = require('lodash/omit');
7
8
  const uniq = require('lodash/uniq');
8
9
  const merge = require('lodash/merge');
9
10
  const pick = require('lodash/pick');
@@ -61,6 +62,7 @@ const {
61
62
  BLOCKLET_META_FILE,
62
63
  BLOCKLET_CONFIGURABLE_KEY,
63
64
  RESTORE_PROGRESS_STATUS,
65
+ CHAIN_PROP_MAP_REVERSE,
64
66
  } = require('@blocklet/constant');
65
67
  const util = require('../../util');
66
68
  const {
@@ -79,6 +81,7 @@ const {
79
81
  getBlockletStatusFromProcess,
80
82
  checkBlockletProcessHealthy,
81
83
  validateBlocklet,
84
+ validateBlockletChainInfo,
82
85
  statusMap,
83
86
  pruneBlockletBundle,
84
87
  getDiskInfo,
@@ -99,6 +102,7 @@ const {
99
102
  checkVersionCompatibility,
100
103
  getBlockletKnownAs,
101
104
  updateBlockletFallbackLogo,
105
+ ensureAppLogo,
102
106
  } = require('../../util/blocklet');
103
107
  const states = require('../../states');
104
108
  const BaseBlockletManager = require('./base');
@@ -433,6 +437,7 @@ class BlockletManager extends BaseBlockletManager {
433
437
 
434
438
  // validate requirement and engine
435
439
  await validateBlocklet(blocklet);
440
+ await validateBlockletChainInfo(blocklet);
436
441
 
437
442
  if (!hasRunnableComponent(blocklet)) {
438
443
  throw new Error('No runnable component found');
@@ -613,7 +618,7 @@ class BlockletManager extends BaseBlockletManager {
613
618
  * @memberof BlockletManager
614
619
  */
615
620
  // eslint-disable-next-line no-unused-vars
616
- async restore(input, context) {
621
+ async restoreBlocklet(input, context) {
617
622
  const { from, ...param } = input;
618
623
  if (from === 'spaces') {
619
624
  return this._restoreFromSpaces(param, context);
@@ -999,7 +1004,6 @@ class BlockletManager extends BaseBlockletManager {
999
1004
  return getBackupList(this.dataDirs.data);
1000
1005
  }
1001
1006
 
1002
- // CAUTION: this method currently only support config by blocklet.meta.did
1003
1007
  // eslint-disable-next-line no-unused-vars
1004
1008
  async config({ did, configs: newConfigs, skipHook, skipDidDocument }, context) {
1005
1009
  // todo: skipDidDocument will be deleted
@@ -1113,6 +1117,9 @@ class BlockletManager extends BaseBlockletManager {
1113
1117
  }
1114
1118
  }
1115
1119
 
1120
+ // chain config
1121
+ await this._ensureAppChainConfig(rootMetaDid, newConfigs);
1122
+
1116
1123
  await this._updateBlockletEnvironment(rootDid);
1117
1124
 
1118
1125
  // response
@@ -1962,13 +1969,7 @@ class BlockletManager extends BaseBlockletManager {
1962
1969
  logger.info('blocklet installed', { source, did: meta.did });
1963
1970
 
1964
1971
  // logo
1965
- if (blocklet.source === BlockletSource.custom && blocklet.children[0]?.meta?.logo) {
1966
- const fileName = blocklet.children[0].meta.logo;
1967
- const src = path.join(getBundleDir(this.installDir, blocklet.children[0].meta), fileName);
1968
- const dist = path.join(getBundleDir(this.installDir, blocklet.meta), fileName);
1969
- await fs.copy(src, dist);
1970
- }
1971
-
1972
+ await ensureAppLogo(blocklet, this.installDir);
1972
1973
  await updateBlockletFallbackLogo(blocklet);
1973
1974
 
1974
1975
  // Init db
@@ -2346,6 +2347,9 @@ class BlockletManager extends BaseBlockletManager {
2346
2347
 
2347
2348
  // write configs to db
2348
2349
  await states.blockletExtras.setConfigs([...ancestors.map((x) => x.meta.did), b.meta.did], environments);
2350
+
2351
+ // chain config
2352
+ await this._ensureAppChainConfig(blocklet.meta.did, environments, 'name');
2349
2353
  });
2350
2354
  } else {
2351
2355
  const child = blocklet.children.find((x) => x.meta.did === childDid);
@@ -2354,6 +2358,9 @@ class BlockletManager extends BaseBlockletManager {
2354
2358
  [blocklet.meta.did, ...ancestors.map((x) => x.meta.did), b.meta.did],
2355
2359
  [...get(b.meta, 'environments', []), ...getConfigFromPreferences(child)]
2356
2360
  );
2361
+
2362
+ // chain config
2363
+ await this._ensureAppChainConfig(blocklet.meta.did, get(b.meta, 'environments', []), 'name');
2357
2364
  });
2358
2365
  }
2359
2366
  }
@@ -2603,12 +2610,21 @@ class BlockletManager extends BaseBlockletManager {
2603
2610
  async _backupToSpaces({ blocklet }, context) {
2604
2611
  const userDid = context.user.did;
2605
2612
  const { referrer } = context;
2606
- const { appDid } = blocklet;
2613
+ const {
2614
+ appDid,
2615
+ meta: { did: appPid },
2616
+ } = blocklet;
2607
2617
 
2608
- const spacesBackup = new SpacesBackup({ appDid, event: this, userDid, referrer });
2609
- this.emit(BlockletEvents.backupProgress, { appDid, message: 'Start backup...', progress: 10, completed: false });
2618
+ const spacesBackup = new SpacesBackup({ appDid, appPid, event: this, userDid, referrer });
2619
+ this.emit(BlockletEvents.backupProgress, {
2620
+ appDid,
2621
+ meta: { did: appPid },
2622
+ message: 'Start backup...',
2623
+ progress: 10,
2624
+ completed: false,
2625
+ });
2610
2626
  await spacesBackup.backup();
2611
- this.emit(BlockletEvents.backupProgress, { appDid, completed: true, progress: 100 });
2627
+ this.emit(BlockletEvents.backupProgress, { appDid, meta: { did: appPid }, completed: true, progress: 100 });
2612
2628
  }
2613
2629
 
2614
2630
  /**
@@ -2634,11 +2650,17 @@ class BlockletManager extends BaseBlockletManager {
2634
2650
  await sleep(input.delay);
2635
2651
  }
2636
2652
 
2637
- this.emit(BlockletEvents.restoreProgress, { appDid: input.appDid, status: RESTORE_PROGRESS_STATUS.start });
2653
+ const appPid = input.appDid;
2654
+
2655
+ this.emit(BlockletEvents.restoreProgress, {
2656
+ appDid: input.appDid,
2657
+ meta: { did: appPid },
2658
+ status: RESTORE_PROGRESS_STATUS.start,
2659
+ });
2638
2660
 
2639
2661
  const userDid = context.user.did;
2640
2662
 
2641
- const spacesRestore = new SpacesRestore({ ...input, event: this, userDid, referrer: context.referrer });
2663
+ const spacesRestore = new SpacesRestore({ ...input, appPid, event: this, userDid, referrer: context.referrer });
2642
2664
  const params = await spacesRestore.restore();
2643
2665
 
2644
2666
  const removeRestoreDir = () => {
@@ -2702,6 +2724,21 @@ class BlockletManager extends BaseBlockletManager {
2702
2724
  throw error;
2703
2725
  }
2704
2726
  }
2727
+
2728
+ async _ensureAppChainConfig(metaDid, configs, key = 'key') {
2729
+ const chainConfigs = configs.filter((x) => ['CHAIN_HOST', 'CHAIN_ID', 'CHAIN_TYPE'].includes(x[key]));
2730
+ if (chainConfigs.length) {
2731
+ const items = chainConfigs.map((x) => ({
2732
+ ...omit(x, ['description', 'validation']),
2733
+ [key]: CHAIN_PROP_MAP_REVERSE[x[key]],
2734
+ shared: true,
2735
+ secure: false,
2736
+ required: false,
2737
+ custom: false,
2738
+ }));
2739
+ await states.blockletExtras.setConfigs(metaDid, items);
2740
+ }
2741
+ }
2705
2742
  }
2706
2743
 
2707
2744
  module.exports = BlockletManager;
@@ -1,7 +1,7 @@
1
1
  const { BlockletStatus, BLOCKLET_MODES, fromBlockletStatus, BlockletSource } = require('@blocklet/constant');
2
2
 
3
3
  const logger = require('@abtnode/logger')('@abtnode/core:install-app-dev');
4
- const { ensureMeta, updateBlockletFallbackLogo } = require('../../../util/blocklet');
4
+ const { ensureMeta, updateBlockletFallbackLogo, ensureAppLogo } = require('../../../util/blocklet');
5
5
 
6
6
  /**
7
7
  *
@@ -79,10 +79,12 @@ const installApplicationFromDev = async ({ folder, meta, states, manager } = {})
79
79
 
80
80
  await states.blocklet.setBlockletStatus(did, BlockletStatus.installed);
81
81
 
82
- blocklet = await manager.getBlocklet(did);
83
-
82
+ // logo
83
+ await ensureAppLogo(blocklet, manager.installDir);
84
84
  await updateBlockletFallbackLogo(blocklet);
85
85
 
86
+ blocklet = await manager.getBlocklet(did);
87
+
86
88
  return blocklet;
87
89
  };
88
90
 
@@ -117,6 +117,9 @@ const installComponentFromUrl = async ({
117
117
  }
118
118
 
119
119
  await states.blockletExtras.setConfigs([blocklet.meta.did, newChild.meta.did], configs);
120
+
121
+ // chain config
122
+ await manager._ensureAppChainConfig(blocklet.meta.did, configs);
120
123
  }
121
124
  } catch (err) {
122
125
  logger.error('Add component failed', err);
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * @typedef {{
3
3
  * appDid: string
4
+ * appPid: string,
4
5
  * event: import('events').EventEmitter,
5
6
  * userDid: string,
6
7
  * referrer: string,
@@ -147,6 +148,7 @@ class SpacesBackup extends BaseBackup {
147
148
  async export() {
148
149
  this.input.event.emit(BlockletEvents.backupProgress, {
149
150
  appDid: this.input.appDid,
151
+ meta: { did: this.input.appPid },
150
152
  message: 'Preparing data for backup...',
151
153
  progress: 15,
152
154
  completed: false,
@@ -156,6 +158,7 @@ class SpacesBackup extends BaseBackup {
156
158
 
157
159
  this.input.event.emit(BlockletEvents.backupProgress, {
158
160
  appDid: this.input.appDid,
161
+ meta: { did: this.input.appPid },
159
162
  message: 'Data ready, start backup...',
160
163
  progress: 20,
161
164
  completed: false,
@@ -193,11 +196,12 @@ class SpacesBackup extends BaseBackup {
193
196
  // FIXME: @yejianchao 这里需要更完整的黑名单
194
197
  return object.name !== '.DS_Store';
195
198
  },
196
- onProgress: (data) => {
199
+ onAfterUpload: (data) => {
197
200
  logger.info('backup progress', { appDid: this.input.appDid, data });
198
201
  const percent = (data.completed * 100) / data.total;
199
202
  this.input.event.emit(BlockletEvents.backupProgress, {
200
203
  appDid: this.input.appDid,
204
+ meta: { did: this.input.appDid },
201
205
  message: `Uploading file ${basename(data.key)} (${data.completed}/${data.total})`,
202
206
  // 0.8 是因为上传文件到 spaces 占进度的 80%,+ 20 是因为需要累加之前的进度
203
207
  progress: +Math.ceil(percent * 0.8).toFixed(2) + 20,
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * @typedef {{
3
3
  * appDid: string; // --> appDid
4
+ * appPid: string;
4
5
  * endpoint: string;
5
6
  * password: Buffer; // derived from (appSk, appDid)
6
7
  * delegation: string; // from appDid --> serverDid for downloading
@@ -118,10 +119,11 @@ class SpacesRestore extends BaseRestore {
118
119
  debug: true,
119
120
  concurrency: 32,
120
121
  retryCount: 10,
121
- onProgress: (data) => {
122
+ onAfterUpload: (data) => {
122
123
  logger.info('restore progress', { appDid: this.input.appDid, data });
123
124
  this.input.event.emit(BlockletEvents.restoreProgress, {
124
125
  appDid: this.input.appDid,
126
+ meta: { did: this.input.appPid },
125
127
  status: RESTORE_PROGRESS_STATUS.downloading,
126
128
  data,
127
129
  });
@@ -140,6 +142,7 @@ class SpacesRestore extends BaseRestore {
140
142
  async import(params) {
141
143
  this.input.event.emit(BlockletEvents.restoreProgress, {
142
144
  appDid: this.input.appDid,
145
+ meta: { did: this.input.appPid },
143
146
  status: RESTORE_PROGRESS_STATUS.importData,
144
147
  });
145
148
 
@@ -150,6 +153,7 @@ class SpacesRestore extends BaseRestore {
150
153
  );
151
154
  this.input.event.emit(BlockletEvents.restoreProgress, {
152
155
  appDid: this.input.appDid,
156
+ meta: { did: this.input.appPid },
153
157
  status: RESTORE_PROGRESS_STATUS.importDataSuccess,
154
158
  });
155
159
  }
@@ -0,0 +1,68 @@
1
+ /* eslint-disable no-param-reassign */
2
+ const semver = require('semver');
3
+ const listNpmPackageVersion = require('@abtnode/util/lib/list-npm-package-version');
4
+ const logger = require('@abtnode/logger')('@abtnode/core:maintain');
5
+
6
+ const states = require('../states');
7
+
8
+ // eslint-disable-next-line no-unused-vars
9
+ const checkNewVersion = async (params, context) => {
10
+ try {
11
+ const info = await states.node.read();
12
+
13
+ if (!process.env.ABT_NODE_PACKAGE_NAME) {
14
+ logger.error('ABT_NODE_PACKAGE_NAME name was not found in environment');
15
+ return '';
16
+ }
17
+
18
+ const versions = await listNpmPackageVersion(process.env.ABT_NODE_PACKAGE_NAME, {
19
+ includePrereleases: !!info.enableBetaRelease,
20
+ });
21
+ if (Array.isArray(versions) && versions.length) {
22
+ const latestVersionStr = versions[0].version;
23
+ const latestVersion = semver.coerce(latestVersionStr);
24
+ const currentVersion = semver.coerce(info.version);
25
+ if (latestVersionStr !== info.version && semver.gte(latestVersion, currentVersion)) {
26
+ logger.info('New version found for Blocklet Server', {
27
+ latestVersion: latestVersionStr,
28
+ currentVersion: info.version,
29
+ nextVersion: info.nextVersion,
30
+ });
31
+ await states.node.updateNodeInfo({ nextVersion: latestVersionStr });
32
+ await states.notification.create({
33
+ title: 'Blocklet Server upgrade available',
34
+ description: 'A new and improved version of blocklet server is now available',
35
+ entityType: 'node',
36
+ severity: 'info',
37
+ sticky: true,
38
+ action: '/settings/about',
39
+ });
40
+
41
+ return latestVersionStr;
42
+ }
43
+ }
44
+
45
+ return '';
46
+ } catch (err) {
47
+ logger.error('Failed to check new version for Blocklet Server', { error: err });
48
+ }
49
+
50
+ return '';
51
+ };
52
+
53
+ const getCron = () => ({
54
+ name: 'check-new-version',
55
+ time: '0 0 8 * * *', // check every day
56
+ // time: '0 */5 * * * *', // check every 5 minutes
57
+ fn: async () => {
58
+ const info = await states.node.read();
59
+ if (!info.autoUpgrade) {
60
+ return;
61
+ }
62
+
63
+ checkNewVersion();
64
+ },
65
+ options: { runOnInit: false },
66
+ });
67
+
68
+ module.exports = { getCron, checkNewVersion };
@@ -1,5 +1,5 @@
1
1
  const logger = require('@abtnode/logger')('@abtnode/disk-monitor');
2
- const info = require('./sysinfo');
2
+ const info = require('../util/sysinfo');
3
3
 
4
4
  const states = require('../states');
5
5
 
@@ -29,7 +29,7 @@ const check = async (threshold) => {
29
29
  };
30
30
 
31
31
  const getCron = () => ({
32
- name: 'disk-monitor',
32
+ name: 'monitor-disk-usage',
33
33
  time: '0 5 * * * *', // check every hour
34
34
  fn: async () => {
35
35
  const nodeInfo = await states.node.read();
@@ -0,0 +1,41 @@
1
+ /* eslint-disable no-console */
2
+ const path = require('path');
3
+ const spawn = require('cross-spawn');
4
+ const { LOG_RETAIN_IN_DAYS } = require('@abtnode/constant');
5
+
6
+ const getCron = () => ({
7
+ name: 'rotate-pm2-logs',
8
+ time: '0 0 * * *', // default : every day at midnight
9
+ options: { runOnInit: false },
10
+ fn: async () => {
11
+ console.info(`Log rotator started on ${new Date().toISOString()}`);
12
+ const child = spawn('node', [path.join(__dirname, './script.js')], {
13
+ detached: true,
14
+ windowsHide: true, // required for Windows
15
+ cwd: process.cwd(),
16
+ timeout: 30 * 60 * 1000, // 30 minutes
17
+ shell: process.env.SHELL || false,
18
+ stdio: ['ignore', process.stdout, process.stderr],
19
+ env: {
20
+ PATH: process.env.PATH,
21
+ PM2_HOME: process.env.PM2_HOME,
22
+ COMPRESS: true,
23
+ RETAIN: LOG_RETAIN_IN_DAYS,
24
+ },
25
+ });
26
+
27
+ child.on('error', (err) => {
28
+ console.error('Log rotator errored', err);
29
+ });
30
+
31
+ child.on('close', (code) => {
32
+ console.info(`Log rotator exited with code ${code} on ${new Date().toISOString()}`);
33
+ });
34
+
35
+ child.unref();
36
+ },
37
+ });
38
+
39
+ module.exports = {
40
+ getCron,
41
+ };
@@ -0,0 +1,56 @@
1
+ /* eslint-disable no-console */
2
+ const pm2 = require('@abtnode/util/lib/async-pm2');
3
+
4
+ const LogRotator = require('../../util/rotator');
5
+
6
+ const RETAIN = parseInt(process.env.RETAIN || '', 10);
7
+ const COMPRESS = JSON.parse(process.env.COMPRESS || 'false');
8
+ const DATE_FORMAT = process.env.DATE_FORMAT || 'YYYY-MM-DD';
9
+ const { TZ } = process.env;
10
+
11
+ const logRotate = new LogRotator({ compress: COMPRESS, dateFormat: DATE_FORMAT, tz: TZ, retain: RETAIN });
12
+
13
+ // register the cron to force rotate file
14
+ console.info('Start rotating pm2 logs...');
15
+
16
+ // get list of process managed by pm2
17
+ pm2.connect((err) => {
18
+ if (err) {
19
+ console.error('Can not connect to pm2 daemon', err);
20
+ process.exit(1);
21
+ }
22
+
23
+ // eslint-disable-next-line no-shadow
24
+ pm2.list(async (err, apps) => {
25
+ if (err) {
26
+ console.error('Can not list apps from pm2', err);
27
+ process.exit(1);
28
+ }
29
+
30
+ const appMap = {};
31
+
32
+ // force rotate for each app
33
+ await Promise.allSettled(
34
+ apps.map((app) => {
35
+ // if apps instances are multi and one of the instances has rotated, ignore
36
+ if (app.pm2_env.instances > 1 && appMap[app.name]) {
37
+ return Promise.resolve(true);
38
+ }
39
+
40
+ appMap[app.name] = app;
41
+
42
+ return logRotate
43
+ .proceedPm2App(app)
44
+ .then(() => {
45
+ console.info(`Rotate ${app.name} log files succeed`);
46
+ })
47
+ .catch((error) => {
48
+ console.error(`Rotate ${app.name} log files failed`, { error });
49
+ });
50
+ })
51
+ );
52
+
53
+ console.info('Done rotating pm2 logs...');
54
+ process.exit(0);
55
+ });
56
+ });
package/lib/index.js CHANGED
@@ -24,7 +24,9 @@ const IP = require('./util/ip');
24
24
  const DomainStatus = require('./util/domain-status');
25
25
  const Maintain = require('./util/maintain');
26
26
  const resetNode = require('./util/reset-node');
27
- const DiskMonitor = require('./util/disk-monitor');
27
+ const DiskMonitor = require('./crons/monitor-disk-usage');
28
+ const LogRotator = require('./crons/rotate-pm2-logs');
29
+ const VersionChecker = require('./crons/check-new-version');
28
30
  const StoreUtil = require('./util/store');
29
31
  const createQueue = require('./util/queue');
30
32
  const createEvents = require('./event');
@@ -235,7 +237,7 @@ function ABTNode(options) {
235
237
  updateComponentTitle: blockletManager.updateComponentTitle.bind(blockletManager),
236
238
  updateComponentMountPoint: blockletManager.updateComponentMountPoint.bind(blockletManager),
237
239
  backupBlocklet: blockletManager.backup.bind(blockletManager),
238
- restoreBlocklet: blockletManager.restore.bind(blockletManager),
240
+ restoreBlocklet: blockletManager.restoreBlocklet.bind(blockletManager),
239
241
  migrateApplicationToStructV2: blockletManager.migrateApplicationToStructV2.bind(blockletManager),
240
242
 
241
243
  // For diagnose purpose
@@ -402,8 +404,8 @@ function ABTNode(options) {
402
404
  markMigrationExecuted: states.migration.markExecuted.bind(states.migration),
403
405
 
404
406
  // Upgrading
407
+ checkNodeVersion: VersionChecker.checkNewVersion,
405
408
  upgradeNodeVersion: () => Maintain.triggerMaintain({ action: 'upgrade', next: Maintain.resumeMaintain }),
406
- checkNodeVersion: Maintain.checkNewVersion,
407
409
  restartServer: () => Maintain.triggerMaintain({ action: 'restart', next: Maintain.resumeMaintain }),
408
410
  isBeingMaintained: Maintain.isBeingMaintained,
409
411
  resumeMaintain: Maintain.resumeMaintain,
@@ -452,10 +454,11 @@ function ABTNode(options) {
452
454
  context: { states, events, webhook },
453
455
  jobs: [
454
456
  IP.cron,
455
- Maintain.getCron(),
457
+ VersionChecker.getCron(),
456
458
  ...getRoutingCrons(),
457
459
  ...blockletManager.getCrons(),
458
460
  DiskMonitor.getCron(),
461
+ LogRotator.getCron(),
459
462
  ...getStateCrons(states),
460
463
  ...nodeAPI.getCrons(),
461
464
  ],
@@ -0,0 +1,160 @@
1
+ /* eslint-disable no-console */
2
+ const fs = require('fs-extra');
3
+ const axon = require('axon');
4
+ const path = require('path');
5
+ const shell = require('shelljs');
6
+ const semver = require('semver');
7
+
8
+ const VERBOSE = '';
9
+ // const VERBOSE = '--verbose';
10
+
11
+ const BINARY_NAME = process.env.ABT_NODE_BINARY_NAME;
12
+ const COMMAND_NAME = process.env.ABT_NODE_COMMAND_NAME;
13
+ const PACKAGE_NAME = process.env.ABT_NODE_PACKAGE_NAME;
14
+ const VALID_BINARY_NAMES = ['blocklet', 'abtnode'];
15
+
16
+ console.info(`binary name: ${BINARY_NAME}`);
17
+ console.info(`command name: ${COMMAND_NAME}`);
18
+ console.info(`package name: ${PACKAGE_NAME}`);
19
+
20
+ // async and promise style of shelljs.exec
21
+ const runAsync = (command, options) =>
22
+ new Promise((resolve) => {
23
+ console.info(`Run command: ${command}`);
24
+ shell.exec(command, { async: true, ...options }, (code, stdout, stderr) => {
25
+ resolve({ code, stdout, stderr });
26
+ });
27
+ });
28
+
29
+ const getInstaller = () => {
30
+ const { stdout: pnpmPath } = shell.which('pnpm') || {};
31
+ if (pnpmPath) {
32
+ const { stdout: expectedDir } = shell.exec('pnpm bin -g', { silent: true });
33
+ const { stdout: binPath } = shell.which(BINARY_NAME);
34
+ if (expectedDir && binPath && expectedDir.trim() === path.dirname(binPath)) {
35
+ return 'pnpm';
36
+ }
37
+ }
38
+
39
+ const { stdout: yarnPath } = shell.which('yarn') || {};
40
+ if (yarnPath) {
41
+ const { stdout: binaryDir } = shell.exec(`yarn global bin ${BINARY_NAME}`, { silent: true });
42
+ const binaryPath = path.join(binaryDir, BINARY_NAME);
43
+ if (fs.existsSync(binaryPath)) {
44
+ return 'yarn';
45
+ }
46
+ }
47
+
48
+ return 'npm';
49
+ };
50
+
51
+ const getBinaryDir = () => {
52
+ const installer = getInstaller();
53
+ if (installer === 'pnpm') {
54
+ const { stdout: binaryDir } = shell.exec('pnpm bin -g', { silent: true });
55
+ return binaryDir.trim();
56
+ }
57
+
58
+ if (installer === 'yarn') {
59
+ const { stdout: binaryDir } = shell.exec(`yarn global bin ${BINARY_NAME}`, { silent: true });
60
+ return binaryDir.trim();
61
+ }
62
+
63
+ const { stdout: binaryDir } = shell.exec(`npm bin -g ${BINARY_NAME}`, { silent: true });
64
+ return binaryDir.trim();
65
+ };
66
+
67
+ const installBlockletServer = async (version) => {
68
+ if (VALID_BINARY_NAMES.includes(BINARY_NAME) === false) {
69
+ return { code: 1, stderr: 'Abort because you are not using a standard @blocklet/cli setup' };
70
+ }
71
+
72
+ const installer = getInstaller();
73
+ const commands = {
74
+ pnpm: `pnpm add -g ${PACKAGE_NAME}@${version} ${VERBOSE}`,
75
+ npm: `npm install -g ${PACKAGE_NAME}@${version} ${VERBOSE}`,
76
+ yarn: `yarn global add ${PACKAGE_NAME}@${version} ${VERBOSE}`,
77
+ };
78
+ const command = commands[installer];
79
+ console.info('Installing blocklet server', { version, installer, command });
80
+ const result = await runAsync(command);
81
+ console.info('Installing blocklet server done', { version });
82
+ return result;
83
+ };
84
+
85
+ const verifyBlockletServer = async (version) => {
86
+ console.info('Verifying blocklet server', { version });
87
+ const result = await runAsync(`${BINARY_NAME} --version`);
88
+ console.info('Verifying blocklet server done', { version });
89
+
90
+ if (result.code === 0) {
91
+ const actual = semver.coerce(result.stdout);
92
+ if (actual && actual.version === version) {
93
+ return result;
94
+ }
95
+
96
+ return { code: 1, stderr: `Blocklet server version is not expected ${version}` };
97
+ }
98
+
99
+ return result;
100
+ };
101
+
102
+ // Restart Blocklet Server
103
+ const restartBlockletServer = async (dataDir) => {
104
+ // We put this in the event loop because it will terminate the current node process
105
+ // But we need to wait for the session state to transition from restarting to cleanup
106
+ process.nextTick(async () => {
107
+ const binaryDir = getBinaryDir();
108
+ console.info('Restarting blocklet server', { dataDir, binaryDir });
109
+
110
+ if (!binaryDir) {
111
+ console.error('Can not restart blocklet server because no binaryDir found');
112
+ return;
113
+ }
114
+
115
+ // restart the server
116
+ if (VALID_BINARY_NAMES.includes(BINARY_NAME)) {
117
+ await runAsync(`${binaryDir}/${COMMAND_NAME} start`, { cwd: path.dirname(dataDir) });
118
+ } else {
119
+ await runAsync(`${COMMAND_NAME} start`, { cwd: path.dirname(dataDir) });
120
+ }
121
+ console.info('Restarting blocklet server done');
122
+ });
123
+
124
+ return dataDir;
125
+ };
126
+
127
+ // Start the server
128
+ const sock = axon.socket('rep');
129
+ const port = Number(process.env.ABT_NODE_UPDATER_PORT);
130
+ sock.bind(port, '127.0.0.1');
131
+ sock.on('message', async (raw, reply) => {
132
+ console.info('receive', { raw });
133
+
134
+ try {
135
+ const message = JSON.parse(raw);
136
+ if (message.command === 'install') {
137
+ const result = await installBlockletServer(message.version);
138
+ reply(result);
139
+ } else if (message.command === 'verify') {
140
+ const result = await verifyBlockletServer(message.version);
141
+ reply(result);
142
+ } else if (message.command === 'restart') {
143
+ const result = await restartBlockletServer(message.dataDir);
144
+ reply(result);
145
+ } else if (message.command === 'shutdown') {
146
+ reply('Terminate myself after this message');
147
+ setTimeout(() => {
148
+ process.exit(0);
149
+ }, 500);
150
+ } else if (message.command === 'ping') {
151
+ reply('pong');
152
+ } else {
153
+ reply({ error: 'Unknown command' });
154
+ }
155
+ } catch (err) {
156
+ console.error('failed to process command', { error: err });
157
+ }
158
+ });
159
+
160
+ console.info(`Blocklet Server Updater process is ready on port ${port}`);
@@ -125,6 +125,7 @@ class NodeState extends BaseState {
125
125
  docker,
126
126
  mode,
127
127
  enableWelcomePage: mode !== NODE_MODES.SERVERLESS,
128
+ enableBetaRelease: false,
128
129
  runtimeConfig,
129
130
  ownerNft,
130
131
  diskAlertThreshold: DISK_ALERT_THRESHOLD_PERCENT,
@@ -18,6 +18,7 @@ const isUrl = require('is-url');
18
18
  const semver = require('semver');
19
19
  const axios = require('@abtnode/util/lib/axios');
20
20
  const { stableStringify } = require('@arcblock/vc');
21
+ const { chainInfo: chainInfoSchema } = require('@arcblock/did-auth/lib/schema');
21
22
 
22
23
  const { fromSecretKey } = require('@ocap/wallet');
23
24
  const { toHex, toBase58, isHex, toDid } = require('@ocap/util');
@@ -78,6 +79,7 @@ const {
78
79
  isEnvShareable,
79
80
  getBlockletAppIdList,
80
81
  isBeforeInstalled,
82
+ getChainInfo,
81
83
  } = require('@blocklet/meta/lib/util');
82
84
  const toBlockletDid = require('@blocklet/meta/lib/did');
83
85
  const { titleSchema, descriptionSchema, logoSchema } = require('@blocklet/meta/lib/schema');
@@ -884,6 +886,21 @@ const validateBlocklet = (blocklet) =>
884
886
  validateEngine(getBlockletEngineNameByPlatform(b.meta));
885
887
  });
886
888
 
889
+ const validateBlockletChainInfo = (blocklet) => {
890
+ const chainInfo = getChainInfo({
891
+ CHAIN_TYPE: blocklet.configObj[BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_CHAIN_TYPE],
892
+ CHAIN_ID: blocklet.configObj[BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_CHAIN_ID],
893
+ CHAIN_HOST: blocklet.configObj[BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_CHAIN_HOST],
894
+ });
895
+
896
+ const { error } = chainInfoSchema.validate(chainInfo);
897
+ if (error) {
898
+ throw error;
899
+ }
900
+
901
+ return chainInfo;
902
+ };
903
+
887
904
  const checkBlockletProcessHealthy = async (blocklet, { minConsecutiveTime, timeout } = {}) => {
888
905
  await forEachBlocklet(
889
906
  blocklet,
@@ -1885,6 +1902,26 @@ const updateBlockletFallbackLogo = async (blocklet) => {
1885
1902
  }
1886
1903
  };
1887
1904
 
1905
+ const ensureAppLogo = async (blocklet, blockletsDir) => {
1906
+ if (!blocklet) {
1907
+ return;
1908
+ }
1909
+
1910
+ if (
1911
+ blocklet.source === BlockletSource.custom &&
1912
+ (blocklet.children || [])[0]?.meta?.logo &&
1913
+ blocklet.children[0].env.appDir
1914
+ ) {
1915
+ const fileName = blocklet.children[0].meta.logo;
1916
+ const src = path.join(blocklet.children[0].env.appDir, fileName);
1917
+ const dist = path.join(getBundleDir(blockletsDir, blocklet.meta), fileName);
1918
+
1919
+ if (fs.existsSync(src)) {
1920
+ await fs.copy(src, dist);
1921
+ }
1922
+ }
1923
+ };
1924
+
1888
1925
  module.exports = {
1889
1926
  updateBlockletFallbackLogo,
1890
1927
  consumeServerlessNFT,
@@ -1897,6 +1934,7 @@ module.exports = {
1897
1934
  getComponentSystemEnvironments,
1898
1935
  getRuntimeEnvironments,
1899
1936
  validateBlocklet,
1937
+ validateBlockletChainInfo,
1900
1938
  fillBlockletConfigs,
1901
1939
  ensureBlockletExpanded,
1902
1940
  startBlockletProcess,
@@ -1941,4 +1979,5 @@ module.exports = {
1941
1979
  validateBlockletMeta,
1942
1980
  getBlockletKnownAs,
1943
1981
  getFixedBundleSource,
1982
+ ensureAppLogo,
1944
1983
  };
package/lib/util/ip.js CHANGED
@@ -1,9 +1,6 @@
1
1
  const getIp = require('@abtnode/util/lib/get-ip');
2
2
  const logger = require('@abtnode/logger')('@abtnode/core:ip');
3
3
 
4
- const doRpc = require('./rpc');
5
- const { memoizeAsync } = require('./index');
6
-
7
4
  let cache = null;
8
5
  const fetch = async () => {
9
6
  try {
@@ -25,9 +22,6 @@ const cron = {
25
22
  fn: fetch,
26
23
  };
27
24
 
28
- const lookup = memoizeAsync((ip) => doRpc({ command: 'lookup', ip }));
29
-
30
25
  module.exports.fetch = fetch;
31
26
  module.exports.get = get;
32
27
  module.exports.cron = cron;
33
- module.exports.lookup = lookup;
@@ -1,59 +1,15 @@
1
1
  /* eslint-disable no-param-reassign */
2
- const semver = require('semver');
3
2
  const sleep = require('@abtnode/util/lib/sleep');
4
3
  const SysInfo = require('systeminformation');
5
4
  const { NODE_MODES, NODE_MAINTAIN_PROGRESS, EVENTS } = require('@abtnode/constant');
6
- const listNpmPackageVersion = require('@abtnode/util/lib/list-npm-package-version');
7
5
  const Lock = require('@abtnode/util/lib/lock');
8
6
  const logger = require('@abtnode/logger')('@abtnode/core:maintain');
9
7
 
10
8
  const states = require('../states');
11
- const doRpc = require('./rpc');
9
+ const { doRpcCall, startRpcServer } = require('./rpc');
12
10
 
13
11
  const lock = new Lock('node-upgrade-lock');
14
12
 
15
- // eslint-disable-next-line no-unused-vars
16
- const checkNewVersion = async (params, context) => {
17
- try {
18
- const info = await states.node.read();
19
-
20
- if (!process.env.ABT_NODE_PACKAGE_NAME) {
21
- logger.error('ABT_NODE_PACKAGE_NAME name was not found in environment');
22
- return '';
23
- }
24
-
25
- const versions = await listNpmPackageVersion(process.env.ABT_NODE_PACKAGE_NAME);
26
- if (Array.isArray(versions) && versions.length) {
27
- const latestVersion = versions[0].version;
28
- if (semver.gt(latestVersion, info.version)) {
29
- // if (semver.gte(latestVersion, info.version)) {
30
- logger.info('New version found for Blocklet Server', {
31
- latestVersion,
32
- currentVersion: info.version,
33
- nextVersion: info.nextVersion,
34
- });
35
- await states.node.updateNodeInfo({ nextVersion: latestVersion });
36
- await states.notification.create({
37
- title: 'Blocklet Server upgrade available',
38
- description: 'A new and improved version of blocklet server is now available',
39
- entityType: 'node',
40
- severity: 'info',
41
- sticky: true,
42
- action: '/settings/about',
43
- });
44
-
45
- return latestVersion;
46
- }
47
- }
48
-
49
- return '';
50
- } catch (err) {
51
- logger.error('Failed to check new version for Blocklet Server', { error: err });
52
- }
53
-
54
- return '';
55
- };
56
-
57
13
  const triggerMaintain = async ({ action, next }) => {
58
14
  if (['upgrade', 'restart'].includes(action) === false) {
59
15
  throw new Error(`Unrecognized server maintain action: ${action}`);
@@ -98,6 +54,8 @@ const triggerMaintain = async ({ action, next }) => {
98
54
  throw new Error(`${action} aborted due to invalid session: ${sessionId}`);
99
55
  }
100
56
 
57
+ await startRpcServer();
58
+
101
59
  process.nextTick(async () => {
102
60
  await next(session);
103
61
  });
@@ -134,7 +92,7 @@ const resumeMaintain = async (session) => {
134
92
 
135
93
  // 2. install new version
136
94
  if (session.stage === NODE_MAINTAIN_PROGRESS.INSTALLING) {
137
- const result = await doRpc({ command: 'install', version: to });
95
+ const result = await doRpcCall({ command: 'install', version: to });
138
96
  logger.info('new version installed', { from, to, sessionId });
139
97
  if (result.code !== 0) {
140
98
  await sleep(3000);
@@ -144,7 +102,7 @@ const resumeMaintain = async (session) => {
144
102
 
145
103
  // 2. verify new version
146
104
  if (session.stage === NODE_MAINTAIN_PROGRESS.VERIFYING) {
147
- const result = await doRpc({ command: 'verify', version: to });
105
+ const result = await doRpcCall({ command: 'verify', version: to });
148
106
  await sleep(3000);
149
107
  if (result.code === 0) {
150
108
  logger.info('new version verified', { from, to, sessionId });
@@ -164,6 +122,7 @@ const resumeMaintain = async (session) => {
164
122
  await states.node.updateNodeInfo({ nextVersion: '', version: from, upgradeSessionId: '' });
165
123
  try {
166
124
  await states.node.exitMode(NODE_MODES.MAINTENANCE);
125
+ await doRpcCall({ command: 'shutdown' });
167
126
  } catch (error) {
168
127
  logger.error('Failed to rollback', { error });
169
128
  }
@@ -177,7 +136,7 @@ const resumeMaintain = async (session) => {
177
136
  await sleep(3000);
178
137
  await goNextState(NODE_MAINTAIN_PROGRESS.CLEANUP, true);
179
138
  await sleep(3000);
180
- await doRpc({ command: 'restart', dataDir: process.env.ABT_NODE_DATA_DIR });
139
+ await doRpcCall({ command: 'restart', dataDir: process.env.ABT_NODE_DATA_DIR });
181
140
  await lock.release();
182
141
  return; // we should abort here and resume after restart
183
142
  }
@@ -190,6 +149,7 @@ const resumeMaintain = async (session) => {
190
149
  await states.node.updateNodeInfo({ nextVersion: '', version: to, upgradeSessionId: '' });
191
150
  try {
192
151
  await states.node.exitMode(NODE_MODES.MAINTENANCE);
152
+ await doRpcCall({ command: 'shutdown' });
193
153
  } catch (error) {
194
154
  logger.error('Failed to exit maintenance mode', { error });
195
155
  }
@@ -215,19 +175,4 @@ const isBeingMaintained = async () => {
215
175
  return session;
216
176
  };
217
177
 
218
- const getCron = () => ({
219
- name: 'check-update',
220
- time: '0 0 8 * * *', // check every day
221
- // time: '0 */5 * * * *', // check every 5 minutes
222
- fn: async () => {
223
- const info = await states.node.read();
224
- if (!info.autoUpgrade) {
225
- return;
226
- }
227
-
228
- checkNewVersion();
229
- },
230
- options: { runOnInit: false },
231
- });
232
-
233
- module.exports = { getCron, checkNewVersion, resumeMaintain, triggerMaintain, isBeingMaintained };
178
+ module.exports = { resumeMaintain, triggerMaintain, isBeingMaintained };
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Base on: https://github.com/keymetrics/pm2-logrotate
3
+ */
4
+ const fs = require('fs-extra');
5
+ const path = require('path');
6
+ const moment = require('moment-timezone');
7
+ const zlib = require('zlib');
8
+ const log = require('@abtnode/logger');
9
+
10
+ const buildLogger =
11
+ (func) =>
12
+ (...args) => {
13
+ if (typeof args[0] === 'string') {
14
+ func(`pm2.log.rotate.${args[0]}`, ...args.slice(1));
15
+ return;
16
+ }
17
+
18
+ func('pm2.log.rotate', ...args);
19
+ };
20
+
21
+ // eslint-disable-next-line no-console
22
+ const logInfo = buildLogger(console.info);
23
+ const logError = buildLogger(console.error);
24
+
25
+ module.exports = class LogRotate {
26
+ constructor({ compress, dateFormat, tz, retain } = {}) {
27
+ this.compress = compress;
28
+ this.dateFormat = dateFormat;
29
+ this.tz = tz;
30
+ this.retain = retain;
31
+ this.compressedFileFormat = '.gz';
32
+ }
33
+
34
+ getLimitSize(maxSize) {
35
+ if (!maxSize) {
36
+ return 1024 * 1024 * 10; // 10M
37
+ }
38
+
39
+ if (typeof maxSize === 'number') {
40
+ return maxSize;
41
+ }
42
+
43
+ if (typeof maxSize !== 'string') {
44
+ throw new Error('maxSize must be a string or number');
45
+ }
46
+
47
+ // eslint-disable-next-line no-param-reassign
48
+ maxSize = maxSize.trim();
49
+
50
+ const unit = maxSize[maxSize.length - 1].toUpperCase();
51
+ if (unit === 'G') {
52
+ return parseInt(maxSize, 10) * 1024 * 1024 * 1024;
53
+ }
54
+
55
+ if (unit === 'M') {
56
+ return parseInt(maxSize, 10) * 1024 * 1024;
57
+ }
58
+
59
+ if (unit === 'K') {
60
+ return parseInt(maxSize, 10) * 1024;
61
+ }
62
+
63
+ return parseInt(maxSize, 10);
64
+ }
65
+
66
+ /**
67
+ * Apply the rotation process of the log file.
68
+ *
69
+ * @param {string} file
70
+ */
71
+ proceed(file, callback = () => {}) {
72
+ // set default final time
73
+ let finalTime = moment().subtract(1, 'day').format(this.dateFormat);
74
+ // check for a timezone
75
+ if (this.tz) {
76
+ try {
77
+ finalTime = moment().tz(this.tz).subtract(1, 'day').format(this.dateFormat);
78
+ } catch (err) {
79
+ // use default
80
+ }
81
+ }
82
+
83
+ const filename = path.basename(file, path.extname(file));
84
+ const dirname = path.dirname(file);
85
+ const extName = this.compress ? `.log${this.compressedFileFormat}` : '.log';
86
+
87
+ let finalName = `${path.join(dirname, filename)}-${finalTime}${extName}`;
88
+
89
+ // 这种情况是为了兼容,之间的日志文件名中的日志大了一天,所以升级时旧数据可能存在重复
90
+ if (fs.existsSync(finalName)) {
91
+ finalName = `${path.join(dirname, filename)}-${finalTime}.1${extName}`;
92
+ }
93
+
94
+ // if compression is enabled, add gz extention and create a gzip instance
95
+ let GZIP;
96
+ if (this.compress) {
97
+ GZIP = zlib.createGzip({
98
+ level: zlib.constants.Z_BEST_COMPRESSION,
99
+ memLevel: zlib.constants.Z_BEST_COMPRESSION,
100
+ });
101
+ }
102
+
103
+ // create our read/write streams
104
+ const readStream = fs.createReadStream(file);
105
+ const writeStream = fs.createWriteStream(finalName, { flags: 'w+' });
106
+
107
+ // pipe all stream
108
+ if (this.compress) {
109
+ readStream.pipe(GZIP).pipe(writeStream);
110
+ } else {
111
+ readStream.pipe(writeStream);
112
+ }
113
+
114
+ const onError = (err) => {
115
+ logError(err);
116
+ callback(err);
117
+ };
118
+
119
+ // listen for error
120
+ readStream.on('error', onError);
121
+ writeStream.on('error', onError);
122
+ if (this.compression) {
123
+ GZIP.on('error', onError);
124
+ }
125
+
126
+ // when the read is done, empty the file and check for retain option
127
+ writeStream.on('finish', () => {
128
+ if (GZIP) {
129
+ GZIP.close();
130
+ }
131
+ readStream.close();
132
+ writeStream.close();
133
+
134
+ fs.truncateSync(file);
135
+ logInfo('rotated file:', finalName);
136
+
137
+ callback(null, finalName);
138
+ });
139
+ }
140
+
141
+ proceedSync(file) {
142
+ return new Promise((resolve, reject) => {
143
+ this.proceed(file, (err, res) => {
144
+ if (err) {
145
+ return reject(err);
146
+ }
147
+
148
+ return resolve(res);
149
+ });
150
+ });
151
+ }
152
+
153
+ /**
154
+ * Apply the rotation process if the `file` size exceeds the `SIZE_LIMIT`.
155
+ *
156
+ * @param {string} file
157
+ */
158
+ async proceedFile(file) {
159
+ if (!fs.existsSync(file)) {
160
+ return;
161
+ }
162
+
163
+ if (fs.statSync(file).size > 0) {
164
+ await this.proceedSync(file);
165
+ }
166
+
167
+ log.deleteOldLogfiles(file, this.retain);
168
+ }
169
+
170
+ /**
171
+ * Apply the rotation process of all log files of `app` where the file size exceeds the`SIZE_LIMIT`.
172
+ *
173
+ * @param {Object} app
174
+ */
175
+ async proceedPm2App(app) {
176
+ // Check all log path
177
+ // Note: If same file is defined for multiple purposes, it will be processed once only.
178
+ if (app.pm2_env.pm_out_log_path) {
179
+ await this.proceedFile(app.pm2_env.pm_out_log_path);
180
+ }
181
+
182
+ if (app.pm2_env.pm_err_log_path && app.pm2_env.pm_err_log_path !== app.pm2_env.pm_out_log_path) {
183
+ await this.proceedFile(app.pm2_env.pm_err_log_path);
184
+ }
185
+
186
+ if (
187
+ app.pm2_env.pm_log_path &&
188
+ app.pm2_env.pm_log_path !== app.pm2_env.pm_out_log_path &&
189
+ app.pm2_env.pm_log_path !== app.pm2_env.pm_err_log_path
190
+ ) {
191
+ await this.proceedFile(app.pm2_env.pm_log_path);
192
+ }
193
+ }
194
+ };
package/lib/util/rpc.js CHANGED
@@ -1,16 +1,77 @@
1
+ /* eslint-disable no-console */
1
2
  const axon = require('axon');
3
+ const kill = require('kill-port');
4
+ const path = require('path');
5
+ const spawn = require('cross-spawn');
6
+
2
7
  const logger = require('@abtnode/logger')('@abtnode/core:rpc');
3
8
 
4
- const sock = axon.socket('req');
5
- sock.connect(Number(process.env.ABT_NODE_UPDATER_PORT || 40405), '127.0.0.1');
9
+ const host = '127.0.0.1';
10
+ const port = Number(process.env.ABT_NODE_UPDATER_PORT || 40405);
6
11
 
7
- const doRpc = async (message) =>
12
+ const doRpcCall = async (message) =>
8
13
  new Promise((resolve) => {
9
- logger.info('send rpc request', message);
10
- sock.send(JSON.stringify(message), (result) => {
11
- logger.info('receive rpc response', result);
12
- resolve(result);
14
+ logger.info('try send rpc request', message);
15
+ const sock = axon.socket('req');
16
+ sock.connect(port, host);
17
+ sock.on('connect', () => {
18
+ sock.send(JSON.stringify(message), (result) => {
19
+ logger.info('receive rpc response', result);
20
+ sock.close();
21
+ resolve(result);
22
+ });
13
23
  });
14
24
  });
15
25
 
16
- module.exports = doRpc;
26
+ const startRpcServer = async () => {
27
+ try {
28
+ await kill(port, 'tcp');
29
+ console.info('Killed existing rpc server');
30
+ } catch (err) {
31
+ console.error('Failed to kill existing rpc server', err);
32
+ }
33
+
34
+ console.info(`Rpc Server started on ${new Date().toISOString()}`);
35
+ const child = spawn('node', [path.join(__dirname, '../processes/updater.js')], {
36
+ detached: true,
37
+ windowsHide: true, // required for Windows
38
+ cwd: process.cwd(),
39
+ timeout: 60 * 60 * 1000, // 60 minutes
40
+ shell: process.env.SHELL || false,
41
+ stdio: ['ignore', process.stdout, process.stderr],
42
+ env: {
43
+ PATH: process.env.PATH,
44
+ PM2_HOME: process.env.PM2_HOME,
45
+ ABT_NODE_UPDATER_PORT: process.env.ABT_NODE_UPDATER_PORT,
46
+ ABT_NODE_PACKAGE_NAME: process.env.ABT_NODE_PACKAGE_NAME,
47
+ ABT_NODE_BINARY_NAME: process.env.ABT_NODE_BINARY_NAME,
48
+ ABT_NODE_COMMAND_NAME: process.env.ABT_NODE_COMMAND_NAME,
49
+ },
50
+ });
51
+
52
+ child.on('error', (err) => {
53
+ console.error('Rpc Server errored', err);
54
+ });
55
+
56
+ child.on('close', (code) => {
57
+ console.info(`Rpc Server exited with code ${code} on ${new Date().toISOString()}`);
58
+ });
59
+
60
+ child.unref();
61
+ };
62
+
63
+ module.exports = { doRpcCall, startRpcServer };
64
+
65
+ // Following script used to test the upgrade process
66
+ // process.env.SHELL = '/opt/homebrew/bin/zsh';
67
+ // process.env.PM2_HOME = '/Users/wangshijun/.arcblock/abtnode';
68
+ // process.env.ABT_NODE_UPDATER_PORT = '40405';
69
+ // process.env.ABT_NODE_PACKAGE_NAME = '@blocklet/cli';
70
+ // process.env.ABT_NODE_BINARY_NAME = 'blocklet';
71
+ // process.env.ABT_NODE_COMMAND_NAME = 'blocklet server';
72
+ // startRpcServer().then(async () => {
73
+ // let result = await doRpcCall({ command: 'verify', version: '1.16.5' });
74
+ // console.log(result);
75
+ // result = await doRpcCall({ command: 'shutdown', version: '1.16.5' });
76
+ // console.log(result);
77
+ // });
@@ -31,13 +31,15 @@ const getSysInfo = async () => {
31
31
  },
32
32
  mem: info.mem,
33
33
  os: info.osInfo,
34
- disks: drives.map((x) => ({
35
- device: x.fs,
36
- mountPoint: x.mount,
37
- total: x.size,
38
- used: x.used,
39
- free: x.size - x.used,
40
- })),
34
+ disks: drives
35
+ .map((x) => ({
36
+ device: x.fs,
37
+ mountPoint: x.mount,
38
+ total: x.size,
39
+ used: x.used,
40
+ free: x.size - x.used,
41
+ }))
42
+ .filter((x) => x.total > 0),
41
43
  };
42
44
  };
43
45
 
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.16.6-beta-8be2fe37",
6
+ "version": "1.16.6-beta-4ea1eb90",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,39 +19,41 @@
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
- "@abtnode/auth": "1.16.6-beta-8be2fe37",
23
- "@abtnode/certificate-manager": "1.16.6-beta-8be2fe37",
24
- "@abtnode/constant": "1.16.6-beta-8be2fe37",
25
- "@abtnode/cron": "1.16.6-beta-8be2fe37",
26
- "@abtnode/db": "1.16.6-beta-8be2fe37",
27
- "@abtnode/logger": "1.16.6-beta-8be2fe37",
28
- "@abtnode/queue": "1.16.6-beta-8be2fe37",
29
- "@abtnode/rbac": "1.16.6-beta-8be2fe37",
30
- "@abtnode/router-provider": "1.16.6-beta-8be2fe37",
31
- "@abtnode/static-server": "1.16.6-beta-8be2fe37",
32
- "@abtnode/timemachine": "1.16.6-beta-8be2fe37",
33
- "@abtnode/util": "1.16.6-beta-8be2fe37",
34
- "@arcblock/did": "1.18.71",
22
+ "@abtnode/auth": "1.16.6-beta-4ea1eb90",
23
+ "@abtnode/certificate-manager": "1.16.6-beta-4ea1eb90",
24
+ "@abtnode/constant": "1.16.6-beta-4ea1eb90",
25
+ "@abtnode/cron": "1.16.6-beta-4ea1eb90",
26
+ "@abtnode/db": "1.16.6-beta-4ea1eb90",
27
+ "@abtnode/logger": "1.16.6-beta-4ea1eb90",
28
+ "@abtnode/queue": "1.16.6-beta-4ea1eb90",
29
+ "@abtnode/rbac": "1.16.6-beta-4ea1eb90",
30
+ "@abtnode/router-provider": "1.16.6-beta-4ea1eb90",
31
+ "@abtnode/static-server": "1.16.6-beta-4ea1eb90",
32
+ "@abtnode/timemachine": "1.16.6-beta-4ea1eb90",
33
+ "@abtnode/util": "1.16.6-beta-4ea1eb90",
34
+ "@arcblock/did": "1.18.72",
35
+ "@arcblock/did-auth": "1.18.72",
35
36
  "@arcblock/did-motif": "^1.1.10",
36
- "@arcblock/did-util": "1.18.71",
37
- "@arcblock/event-hub": "1.18.71",
38
- "@arcblock/jwt": "^1.18.71",
37
+ "@arcblock/did-util": "1.18.72",
38
+ "@arcblock/event-hub": "1.18.72",
39
+ "@arcblock/jwt": "^1.18.72",
39
40
  "@arcblock/pm2-events": "^0.0.5",
40
- "@arcblock/vc": "1.18.71",
41
- "@blocklet/constant": "1.16.6-beta-8be2fe37",
42
- "@blocklet/meta": "1.16.6-beta-8be2fe37",
43
- "@blocklet/sdk": "1.16.6-beta-8be2fe37",
44
- "@did-space/client": "^0.2.74",
41
+ "@arcblock/vc": "1.18.72",
42
+ "@blocklet/constant": "1.16.6-beta-4ea1eb90",
43
+ "@blocklet/meta": "1.16.6-beta-4ea1eb90",
44
+ "@blocklet/sdk": "1.16.6-beta-4ea1eb90",
45
+ "@did-space/client": "^0.2.80",
45
46
  "@fidm/x509": "^1.2.1",
46
- "@ocap/client": "1.18.71",
47
- "@ocap/mcrypto": "1.18.71",
48
- "@ocap/util": "1.18.71",
49
- "@ocap/wallet": "1.18.71",
47
+ "@ocap/client": "1.18.72",
48
+ "@ocap/mcrypto": "1.18.72",
49
+ "@ocap/util": "1.18.72",
50
+ "@ocap/wallet": "1.18.72",
50
51
  "@slack/webhook": "^5.0.4",
51
52
  "archiver": "^5.3.1",
52
53
  "axios": "^0.27.2",
53
54
  "axon": "^2.0.3",
54
55
  "chalk": "^4.1.2",
56
+ "cross-spawn": "^7.0.3",
55
57
  "dayjs": "^1.11.7",
56
58
  "deep-diff": "^1.0.2",
57
59
  "detect-port": "^1.5.1",
@@ -67,8 +69,10 @@
67
69
  "is-url": "^1.2.4",
68
70
  "joi": "17.7.0",
69
71
  "js-yaml": "^4.1.0",
72
+ "kill-port": "^2.0.1",
70
73
  "lodash": "^4.17.21",
71
74
  "lru-cache": "^6.0.0",
75
+ "moment-timezone": "^0.5.37",
72
76
  "node-stream-zip": "^1.15.0",
73
77
  "p-limit": "^3.1.0",
74
78
  "p-retry": "4.6.1",
@@ -78,7 +82,7 @@
78
82
  "ssri": "^8.0.1",
79
83
  "stream-throttle": "^0.1.3",
80
84
  "stream-to-promise": "^3.0.0",
81
- "systeminformation": "^5.12.6",
85
+ "systeminformation": "^5.17.12",
82
86
  "tar": "^6.1.11",
83
87
  "transliteration": "^2.3.5",
84
88
  "ua-parser-js": "^1.0.2",
@@ -93,5 +97,5 @@
93
97
  "express": "^4.18.2",
94
98
  "jest": "^27.5.1"
95
99
  },
96
- "gitHead": "27229a1a62850e95907b7a53e0259a809d99377b"
100
+ "gitHead": "0f0f63b552a88343a3f8e0d434b47c3bca6a75b9"
97
101
  }