@abtnode/core 1.16.6-beta-4562aa60 → 1.16.6-beta-eaa4d39d

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/api/team.js CHANGED
@@ -110,6 +110,42 @@ class TeamAPI extends EventEmitter {
110
110
 
111
111
  // User && Invitation
112
112
 
113
+ async loginUser({ teamDid, user }) {
114
+ const state = await this.getUserState(teamDid);
115
+ const nodeInfo = await this.node.read();
116
+
117
+ if (user.role === ROLES.OWNER) {
118
+ if (teamDid !== nodeInfo.did) {
119
+ throw new Error('Cannot add user of owner role');
120
+ }
121
+
122
+ if (await state.count({ role: ROLES.OWNER })) {
123
+ throw new Error('The owner already exists');
124
+ }
125
+ }
126
+
127
+ const { _action, ...doc } = await state.login(user);
128
+ if (_action === 'update') {
129
+ logger.info('user updated successfully', { teamDid, userDid: user.did });
130
+ this.emit(EVENTS.USER_UPDATED, { teamDid, user: doc });
131
+ } else if (_action === 'add') {
132
+ if (teamDid === nodeInfo.did && nodeInfo.nodeOwner && user.did !== nodeInfo.nodeOwner.did) {
133
+ await this.notification.create({
134
+ title: 'New member join',
135
+ description: `User with Name (${user.fullName}) and DID (${user.did}) has joined this server`,
136
+ entityType: 'node',
137
+ action: '/team/members',
138
+ entityId: user.did,
139
+ severity: 'success',
140
+ });
141
+ }
142
+
143
+ logger.info('user added successfully', { teamDid, userDid: user.did, userPk: user.pk, userName: user.fullName });
144
+ this.emit(EVENTS.USER_ADDED, { teamDid, user: doc });
145
+ }
146
+ return doc;
147
+ }
148
+
113
149
  async addUser({ teamDid, user }) {
114
150
  const state = await this.getUserState(teamDid);
115
151
 
@@ -239,10 +275,10 @@ class TeamAPI extends EventEmitter {
239
275
  return res;
240
276
  }
241
277
 
242
- async getUser({ teamDid, user }) {
278
+ async getUser({ teamDid, user, options = {} }) {
243
279
  const state = await this.getUserState(teamDid);
244
280
 
245
- return state.getUser(user.did);
281
+ return state.getUser(user.did, options);
246
282
  }
247
283
 
248
284
  async getOwner({ teamDid }) {
@@ -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,8 @@ const {
99
102
  checkVersionCompatibility,
100
103
  getBlockletKnownAs,
101
104
  updateBlockletFallbackLogo,
105
+ ensureAppLogo,
106
+ getBlockletDidDomainList,
102
107
  } = require('../../util/blocklet');
103
108
  const states = require('../../states');
104
109
  const BaseBlockletManager = require('./base');
@@ -106,7 +111,6 @@ const { get: getEngine } = require('./engine');
106
111
  const blockletPm2Events = require('./pm2-events');
107
112
  const runMigrationScripts = require('../migration');
108
113
  const hooks = require('../hooks');
109
- const { getDidDomainForBlocklet } = require('../../util/get-domain-for-blocklet');
110
114
  const handleInstanceInStore = require('../../util/public-to-store');
111
115
  const { BlockletRuntimeMonitor } = require('../../monitor/blocklet-runtime-monitor');
112
116
  const getHistoryList = require('../../monitor/get-history-list');
@@ -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
@@ -1091,10 +1095,6 @@ class BlockletManager extends BaseBlockletManager {
1091
1095
  this.emit(BlockletEvents.spaceConnected, blocklet);
1092
1096
  }
1093
1097
 
1094
- if (willAppDidChange && !skipDidDocument) {
1095
- await this._updateDidDocument(blocklet);
1096
- }
1097
-
1098
1098
  // update blocklet meta
1099
1099
  if (blocklet.structVersion && !childDids.length) {
1100
1100
  const changedTitle = newConfigs.find((x) => x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_NAME)?.value;
@@ -1113,11 +1113,20 @@ class BlockletManager extends BaseBlockletManager {
1113
1113
  }
1114
1114
  }
1115
1115
 
1116
+ // chain config
1117
+ await this._ensureAppChainConfig(rootMetaDid, newConfigs);
1118
+
1116
1119
  await this._updateBlockletEnvironment(rootDid);
1117
1120
 
1118
1121
  // response
1119
1122
  const newState = await this.getBlocklet(rootDid);
1123
+
1124
+ if (willAppDidChange && !skipDidDocument) {
1125
+ await this._updateDidDocument(newState);
1126
+ }
1127
+
1120
1128
  this.emit(BlockletEvents.updated, newState);
1129
+
1121
1130
  return newState;
1122
1131
  }
1123
1132
 
@@ -1962,13 +1971,7 @@ class BlockletManager extends BaseBlockletManager {
1962
1971
  logger.info('blocklet installed', { source, did: meta.did });
1963
1972
 
1964
1973
  // 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
-
1974
+ await ensureAppLogo(blocklet, this.installDir);
1972
1975
  await updateBlockletFallbackLogo(blocklet);
1973
1976
 
1974
1977
  // Init db
@@ -2275,12 +2278,12 @@ class BlockletManager extends BaseBlockletManager {
2275
2278
  const nodeInfo = await states.node.read();
2276
2279
  const { wallet } = getBlockletInfo(blocklet, nodeInfo.sk);
2277
2280
  didDocument
2278
- .disableDNS({ wallet, didRegistryUrl: nodeInfo.didRegistry })
2281
+ .disableBlockletDNS({ appPid: blocklet.appPid, wallet, didRegistryUrl: nodeInfo.didRegistry })
2279
2282
  .then(() => {
2280
- logger.info(`disabled blocklet ${blocklet.appDid} dns`);
2283
+ logger.info(`disabled blocklet ${blocklet.appPid} dns`);
2281
2284
  })
2282
2285
  .catch((err) => {
2283
- logger.error(`disable blocklet ${blocklet.appDid} dns failed`, { error: err });
2286
+ logger.error(`disable blocklet ${blocklet.appPid} dns failed`, { error: err });
2284
2287
  });
2285
2288
  }
2286
2289
 
@@ -2346,6 +2349,9 @@ class BlockletManager extends BaseBlockletManager {
2346
2349
 
2347
2350
  // write configs to db
2348
2351
  await states.blockletExtras.setConfigs([...ancestors.map((x) => x.meta.did), b.meta.did], environments);
2352
+
2353
+ // chain config
2354
+ await this._ensureAppChainConfig(blocklet.meta.did, environments, 'name');
2349
2355
  });
2350
2356
  } else {
2351
2357
  const child = blocklet.children.find((x) => x.meta.did === childDid);
@@ -2354,6 +2360,9 @@ class BlockletManager extends BaseBlockletManager {
2354
2360
  [blocklet.meta.did, ...ancestors.map((x) => x.meta.did), b.meta.did],
2355
2361
  [...get(b.meta, 'environments', []), ...getConfigFromPreferences(child)]
2356
2362
  );
2363
+
2364
+ // chain config
2365
+ await this._ensureAppChainConfig(blocklet.meta.did, get(b.meta, 'environments', []), 'name');
2357
2366
  });
2358
2367
  }
2359
2368
  }
@@ -2540,32 +2549,24 @@ class BlockletManager extends BaseBlockletManager {
2540
2549
  async _updateDidDocument(blocklet) {
2541
2550
  const nodeInfo = await states.node.read();
2542
2551
 
2543
- const { wallet } = getBlockletInfo(
2544
- {
2545
- meta: blocklet.meta,
2546
- environments: [BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK, BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_WALLET_TYPE]
2547
- .map((key) => ({ key, value: blocklet.configObj[key] }))
2548
- .filter((x) => x.value),
2549
- },
2550
- nodeInfo.sk
2551
- );
2552
- const didDomain = getDidDomainForBlocklet({ appPid: blocklet.appPid, didDomain: nodeInfo.didDomain });
2553
-
2554
2552
  const domainAliases = (get(blocklet, 'site.domainAliases') || []).filter(
2555
2553
  (item) => !item.value.endsWith(nodeInfo.didDomain) && !item.value.endsWith('did.staging.arcblock.io') // did.staging.arcblock.io 是旧 did domain, 但主要存在于比较旧的节点中, 需要做兼容
2556
2554
  );
2557
2555
 
2558
- domainAliases.push({ value: didDomain, isProtected: true });
2556
+ const didDomainList = getBlockletDidDomainList(blocklet, nodeInfo);
2557
+ domainAliases.push(...didDomainList);
2559
2558
 
2560
2559
  // 先更新 routing rule db 中的 domain aliases, 这一步的目的是为了后面用
2561
2560
  await states.site.updateDomainAliasList(blocklet.site.id, domainAliases);
2562
2561
 
2563
2562
  this.emit(BlockletEvents.appDidChanged, blocklet);
2564
2563
 
2564
+ const { wallet } = getBlockletInfo(blocklet, nodeInfo.sk);
2565
+ const alsoKnownAs = getBlockletKnownAs(blocklet);
2565
2566
  await didDocument.updateBlockletDocument({
2566
2567
  wallet,
2567
2568
  appPid: blocklet.appPid,
2568
- alsoKnownAs: getBlockletKnownAs(blocklet),
2569
+ alsoKnownAs,
2569
2570
  daemonDidDomain: util.getServerDidDomain(nodeInfo),
2570
2571
  didRegistryUrl: nodeInfo.didRegistry,
2571
2572
  domain: nodeInfo.didDomain,
@@ -2603,12 +2604,21 @@ class BlockletManager extends BaseBlockletManager {
2603
2604
  async _backupToSpaces({ blocklet }, context) {
2604
2605
  const userDid = context.user.did;
2605
2606
  const { referrer } = context;
2606
- const { appDid } = blocklet;
2607
+ const {
2608
+ appDid,
2609
+ meta: { did: appPid },
2610
+ } = blocklet;
2607
2611
 
2608
- const spacesBackup = new SpacesBackup({ appDid, event: this, userDid, referrer });
2609
- this.emit(BlockletEvents.backupProgress, { appDid, message: 'Start backup...', progress: 10, completed: false });
2612
+ const spacesBackup = new SpacesBackup({ appDid, appPid, event: this, userDid, referrer });
2613
+ this.emit(BlockletEvents.backupProgress, {
2614
+ appDid,
2615
+ meta: { did: appPid },
2616
+ message: 'Start backup...',
2617
+ progress: 10,
2618
+ completed: false,
2619
+ });
2610
2620
  await spacesBackup.backup();
2611
- this.emit(BlockletEvents.backupProgress, { appDid, completed: true, progress: 100 });
2621
+ this.emit(BlockletEvents.backupProgress, { appDid, meta: { did: appPid }, completed: true, progress: 100 });
2612
2622
  }
2613
2623
 
2614
2624
  /**
@@ -2634,11 +2644,17 @@ class BlockletManager extends BaseBlockletManager {
2634
2644
  await sleep(input.delay);
2635
2645
  }
2636
2646
 
2637
- this.emit(BlockletEvents.restoreProgress, { appDid: input.appDid, status: RESTORE_PROGRESS_STATUS.start });
2647
+ const appPid = input.appDid;
2648
+
2649
+ this.emit(BlockletEvents.restoreProgress, {
2650
+ appDid: input.appDid,
2651
+ meta: { did: appPid },
2652
+ status: RESTORE_PROGRESS_STATUS.start,
2653
+ });
2638
2654
 
2639
2655
  const userDid = context.user.did;
2640
2656
 
2641
- const spacesRestore = new SpacesRestore({ ...input, event: this, userDid, referrer: context.referrer });
2657
+ const spacesRestore = new SpacesRestore({ ...input, appPid, event: this, userDid, referrer: context.referrer });
2642
2658
  const params = await spacesRestore.restore();
2643
2659
 
2644
2660
  const removeRestoreDir = () => {
@@ -2702,6 +2718,21 @@ class BlockletManager extends BaseBlockletManager {
2702
2718
  throw error;
2703
2719
  }
2704
2720
  }
2721
+
2722
+ async _ensureAppChainConfig(metaDid, configs, key = 'key') {
2723
+ const chainConfigs = configs.filter((x) => ['CHAIN_HOST', 'CHAIN_ID', 'CHAIN_TYPE'].includes(x[key]));
2724
+ if (chainConfigs.length) {
2725
+ const items = chainConfigs.map((x) => ({
2726
+ ...omit(x, ['description', 'validation']),
2727
+ [key]: CHAIN_PROP_MAP_REVERSE[x[key]],
2728
+ shared: true,
2729
+ secure: false,
2730
+ required: false,
2731
+ custom: false,
2732
+ }));
2733
+ await states.blockletExtras.setConfigs(metaDid, items);
2734
+ }
2735
+ }
2705
2736
  }
2706
2737
 
2707
2738
  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
@@ -301,6 +303,7 @@ function ABTNode(options) {
301
303
  getNodeUsers: () => teamAPI.getUsers({ teamDid: options.nodeDid }),
302
304
  getNodeUser: (user) => teamAPI.getUser({ teamDid: options.nodeDid, user }),
303
305
  addUser: teamAPI.addUser.bind(teamAPI),
306
+ loginUser: teamAPI.loginUser.bind(teamAPI),
304
307
  removeUser: teamAPI.removeUser.bind(teamAPI),
305
308
  updateUser: teamAPI.updateUser.bind(teamAPI),
306
309
  updateUserApproval: teamAPI.updateUserApproval.bind(teamAPI),
@@ -402,8 +405,8 @@ function ABTNode(options) {
402
405
  markMigrationExecuted: states.migration.markExecuted.bind(states.migration),
403
406
 
404
407
  // Upgrading
408
+ checkNodeVersion: VersionChecker.checkNewVersion,
405
409
  upgradeNodeVersion: () => Maintain.triggerMaintain({ action: 'upgrade', next: Maintain.resumeMaintain }),
406
- checkNodeVersion: Maintain.checkNewVersion,
407
410
  restartServer: () => Maintain.triggerMaintain({ action: 'restart', next: Maintain.resumeMaintain }),
408
411
  isBeingMaintained: Maintain.isBeingMaintained,
409
412
  resumeMaintain: Maintain.resumeMaintain,
@@ -452,10 +455,11 @@ function ABTNode(options) {
452
455
  context: { states, events, webhook },
453
456
  jobs: [
454
457
  IP.cron,
455
- Maintain.getCron(),
458
+ VersionChecker.getCron(),
456
459
  ...getRoutingCrons(),
457
460
  ...blockletManager.getCrons(),
458
461
  DiskMonitor.getCron(),
462
+ LogRotator.getCron(),
459
463
  ...getStateCrons(states),
460
464
  ...nodeAPI.getCrons(),
461
465
  ],