@abtnode/core 1.8.2 → 1.8.5

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.
@@ -22,8 +22,7 @@ const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager');
22
22
  const downloadFile = require('@abtnode/util/lib/download-file');
23
23
  const Lock = require('@abtnode/util/lib/lock');
24
24
  const { getVcFromPresentation } = require('@abtnode/util/lib/vc');
25
- const handleInstanceInStore = require('@abtnode/util/lib/public-to-store');
26
- const { VC_TYPE_BLOCKLET_PURCHASE } = require('@abtnode/constant');
25
+ const { VC_TYPE_BLOCKLET_PURCHASE, WHO_CAN_ACCESS } = require('@abtnode/constant');
27
26
 
28
27
  const getBlockletEngine = require('@blocklet/meta/lib/engine');
29
28
  const {
@@ -116,6 +115,7 @@ const { getFactoryState } = require('../../util/chain');
116
115
  const runMigrationScripts = require('../migration');
117
116
  const hooks = require('../hooks');
118
117
  const { formatName } = require('../../util/get-domain-for-blocklet');
118
+ const handleInstanceInStore = require('../../util/public-to-store');
119
119
 
120
120
  const {
121
121
  isInProgress,
@@ -128,7 +128,7 @@ const {
128
128
  validateOwner,
129
129
  } = util;
130
130
 
131
- const preDownloadLock = new Lock('pre-download-lock');
131
+ const statusLock = new Lock('blocklet-status-lock');
132
132
 
133
133
  const asyncFs = fs.promises;
134
134
 
@@ -234,7 +234,9 @@ class BlockletManager extends BaseBlockletManager {
234
234
  context.headers = Object.assign(context?.headers || {}, {
235
235
  'x-server-did': info.did,
236
236
  'x-download-token': params.downloadToken,
237
+ // FIXME: 先保证兼容性,后续删除
237
238
  'x-server-publick-key': info.pk,
239
+ 'x-server-public-key': info.pk,
238
240
  'x-server-signature': sign(info.did, info.sk, {
239
241
  exp: (Date.now() + 5 * 60 * 1000) / 1000,
240
242
  }),
@@ -427,6 +429,12 @@ class BlockletManager extends BaseBlockletManager {
427
429
 
428
430
  return blocklet;
429
431
  } catch (err) {
432
+ const status = await states.blocklet.getBlockletStatus(did);
433
+ if ([BlockletStatus.stopping, BlockletStatus.stopped].includes(status)) {
434
+ logger.info('Failed to start blocklet maybe due to manually stopped');
435
+ return states.blocklet.getBlocklet(did);
436
+ }
437
+
430
438
  const error = Array.isArray(err) ? err[0] : err;
431
439
  logger.error('Failed to start blocklet', { error, did, name: blocklet.meta.name });
432
440
  const description = `Start blocklet ${blocklet.meta.name} failed with error: ${error.message}`;
@@ -677,26 +685,38 @@ class BlockletManager extends BaseBlockletManager {
677
685
  return newBlocklet;
678
686
  }
679
687
 
680
- async cancelDownload({ did }, context) {
681
- await preDownloadLock.acquire();
688
+ async cancelDownload({ did: inputDid }, context) {
682
689
  try {
683
- const blocklet = await states.blocklet.getBlocklet(did);
690
+ await statusLock.acquire();
691
+ const blocklet = await states.blocklet.getBlocklet(inputDid);
684
692
  if (!blocklet) {
685
- throw new Error('Can not cancel download for non-exist blocklet in database.', { did });
693
+ throw new Error('Can not cancel download for non-exist blocklet in database.', { inputDid });
694
+ }
695
+
696
+ const { name, did, version } = blocklet.meta;
697
+
698
+ if (![BlockletStatus.downloading, BlockletStatus.waiting].includes(blocklet.status)) {
699
+ throw new Error(`Can not cancel blocklet that status is ${fromBlockletStatus(blocklet.status)}`);
700
+ }
701
+
702
+ const job = await this.installQueue.get(did);
703
+ if (job) {
704
+ const { postAction, oldBlocklet } = job;
705
+ await this._rollback(postAction, did, oldBlocklet);
686
706
  }
687
707
 
688
708
  if (blocklet.status === BlockletStatus.downloading) {
689
709
  await this._cancelDownload(blocklet.meta, context);
690
710
  } else if (blocklet.status === BlockletStatus.waiting) {
691
711
  await this._cancelWaiting(blocklet.meta, context);
692
- } else {
693
- throw new Error(`Can not cancel blocklet that status is ${fromBlockletStatus(blocklet.status)}`);
694
712
  }
695
713
 
696
- preDownloadLock.release();
714
+ logger.info('cancel download blocklet', { did, name, version, status: fromBlockletStatus(blocklet.status) });
715
+
716
+ statusLock.release();
697
717
  return blocklet;
698
718
  } catch (error) {
699
- preDownloadLock.release();
719
+ statusLock.release();
700
720
  throw error;
701
721
  }
702
722
  }
@@ -880,6 +900,25 @@ class BlockletManager extends BaseBlockletManager {
880
900
  return newState;
881
901
  }
882
902
 
903
+ async updateWhoCanAccess({ did, whoCanAccess }) {
904
+ if (!(await this.hasBlocklet({ did }))) {
905
+ throw new Error('The blocklet does not exist');
906
+ }
907
+
908
+ if (!Object.values(WHO_CAN_ACCESS).includes(whoCanAccess)) {
909
+ logger.error(`The value of whoCanAccess is invalid: ${whoCanAccess}`);
910
+ throw new Error('the value is invalid');
911
+ }
912
+
913
+ await states.blockletExtras.setSettings(did, { whoCanAccess });
914
+
915
+ const blocklet = await this.ensureBlocklet(did);
916
+
917
+ this.emit(BlockletEvents.updated, { meta: { did: blocklet.meta.did } });
918
+
919
+ return blocklet;
920
+ }
921
+
883
922
  /**
884
923
  * upgrade blocklet from registry
885
924
  */
@@ -1495,39 +1534,45 @@ class BlockletManager extends BaseBlockletManager {
1495
1534
  const { meta } = blocklet;
1496
1535
  const { name, did, version } = meta;
1497
1536
 
1537
+ // check status
1498
1538
  try {
1499
- await preDownloadLock.acquire();
1539
+ await statusLock.acquire();
1500
1540
 
1501
1541
  const b0 = await states.blocklet.getBlocklet(did);
1502
- if (!b0 || ![BlockletStatus.waiting, BlockletStatus.downloading].includes(b0.status)) {
1542
+ if (!b0 || ![BlockletStatus.waiting].includes(b0.status)) {
1503
1543
  if (!b0) {
1504
- logger.error('blocklet does not exist before downloading', { name, did });
1544
+ throw new Error('blocklet does not exist before downloading');
1505
1545
  } else {
1506
- logger.error('blocklet status is invalid before downloading', {
1507
- name,
1508
- did,
1509
- status: fromBlockletStatus(b0.status),
1510
- });
1546
+ throw new Error(`blocklet status is invalid before downloading: ${fromBlockletStatus(b0.status)}`);
1511
1547
  }
1512
- preDownloadLock.release();
1513
- await this._rollback(postAction, did, oldBlocklet);
1514
- return;
1515
1548
  }
1549
+ statusLock.release();
1550
+ } catch (error) {
1551
+ statusLock.release();
1552
+ logger.error('Check blocklet status failed before downloading', {
1553
+ name,
1554
+ did,
1555
+ error,
1556
+ });
1557
+ await this._rollback(postAction, did, oldBlocklet);
1558
+ return;
1559
+ }
1516
1560
 
1517
- preDownloadLock.release();
1518
-
1561
+ // download bundle
1562
+ try {
1519
1563
  const { isCancelled } = await this._downloadBlocklet(blocklet, oldBlocklet, context);
1520
1564
 
1521
1565
  if (isCancelled) {
1522
- logger.info('Download was canceled manually', { name, did, version });
1523
- await this._rollback(postAction, did, oldBlocklet);
1566
+ logger.info('Download was canceled', { name, did, version });
1567
+
1568
+ if ((await states.blocklet.getBlockletStatus(did)) === BlockletStatus.downloading) {
1569
+ await this._rollback(postAction, did, oldBlocklet);
1570
+ }
1524
1571
  return;
1525
1572
  }
1526
1573
  } catch (err) {
1527
1574
  logger.error('Download blocklet tarball failed', { name, did, version });
1528
1575
 
1529
- preDownloadLock.release();
1530
-
1531
1576
  this.emit(BlockletEvents.downloadFailed, {
1532
1577
  meta: { did },
1533
1578
  error: {
@@ -1555,6 +1600,31 @@ class BlockletManager extends BaseBlockletManager {
1555
1600
  return;
1556
1601
  }
1557
1602
 
1603
+ // update status
1604
+ try {
1605
+ await statusLock.acquire();
1606
+
1607
+ if ((await states.blocklet.getBlockletStatus(did)) !== BlockletStatus.downloading) {
1608
+ throw new Error('blocklet status changed durning download');
1609
+ }
1610
+
1611
+ if (postAction === 'install') {
1612
+ const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.installing);
1613
+ this.emit(BlockletEvents.statusChange, state);
1614
+ }
1615
+
1616
+ if (['upgrade', 'downgrade'].includes(postAction)) {
1617
+ const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.upgrading);
1618
+ this.emit(BlockletEvents.statusChange, state);
1619
+ }
1620
+
1621
+ statusLock.release();
1622
+ } catch (error) {
1623
+ logger.error(error.message);
1624
+ statusLock.release();
1625
+ }
1626
+
1627
+ // install
1558
1628
  try {
1559
1629
  // install blocklet
1560
1630
  if (postAction === 'install') {
@@ -1578,9 +1648,6 @@ class BlockletManager extends BaseBlockletManager {
1578
1648
  const { did, version } = meta;
1579
1649
  logger.info('do install blocklet', { did, version });
1580
1650
 
1581
- const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.installing);
1582
- this.emit(BlockletEvents.statusChange, state);
1583
-
1584
1651
  try {
1585
1652
  const installedBlocklet = await this._installBlocklet({
1586
1653
  did,
@@ -1608,9 +1675,6 @@ class BlockletManager extends BaseBlockletManager {
1608
1675
  const { version, did } = newBlocklet.meta;
1609
1676
  logger.info(`do ${action} blocklet`, { did, version });
1610
1677
 
1611
- const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.upgrading);
1612
- this.emit(BlockletEvents.statusChange, state);
1613
-
1614
1678
  try {
1615
1679
  await this._upgradeBlocklet({
1616
1680
  newBlocklet,
@@ -1642,6 +1706,12 @@ class BlockletManager extends BaseBlockletManager {
1642
1706
  const res = await this.status(did, { forceSync: true });
1643
1707
  this.emit(BlockletEvents.started, res);
1644
1708
  } catch (error) {
1709
+ const status = await states.blocklet.getBlockletStatus(did);
1710
+ if ([BlockletStatus.stopping, BlockletStatus.stopped].includes(status)) {
1711
+ logger.info(`Check blocklet healthy failing because blocklet is ${fromBlockletStatus(status)}`);
1712
+ return;
1713
+ }
1714
+
1645
1715
  logger.error('check blocklet if started failed', { did, name, context, timeout, error });
1646
1716
 
1647
1717
  await this.deleteProcess({ did }, context);
@@ -1838,7 +1908,9 @@ class BlockletManager extends BaseBlockletManager {
1838
1908
  headers: {
1839
1909
  'x-server-did': info.did,
1840
1910
  'x-download-token': downloadToken,
1911
+ // FIXME: 先保证兼容性,后续删除
1841
1912
  'x-server-publick-key': info.pk,
1913
+ 'x-server-public-key': info.pk,
1842
1914
  'x-server-signature': sign(info.did, info.sk, {
1843
1915
  exp: (Date.now() + 5 * 60 * 1000) / 1000,
1844
1916
  }),
@@ -2406,7 +2478,9 @@ class BlockletManager extends BaseBlockletManager {
2406
2478
  headers: {
2407
2479
  'x-server-did': info.did,
2408
2480
  'x-download-token': blocklet?.tokens?.paidBlockletDownloadToken,
2481
+ // FIXME: 先保证兼容性,后续删除
2409
2482
  'x-server-publick-key': info.pk,
2483
+ 'x-server-public-key': info.pk,
2410
2484
  'x-server-signature': sign(info.did, info.sk, {
2411
2485
  exp: (Date.now() + 5 * 60 * 1000) / 1000,
2412
2486
  }),
@@ -2440,7 +2514,11 @@ class BlockletManager extends BaseBlockletManager {
2440
2514
  ticket.on('failed', async (err) => {
2441
2515
  logger.error('queue failed', { entity: 'blocklet', action, did, version, name, error: err });
2442
2516
  await this._rollback(action, did, oldBlocklet);
2443
- this.emit(`blocklet.${action}.failed`, { did, version, err });
2517
+ const eventNames = {
2518
+ upgrade: BlockletEvents.upgradeFailed,
2519
+ downgrade: BlockletEvents.downgradeFailed,
2520
+ };
2521
+ this.emit(eventNames[action], { blocklet: oldBlocklet, context });
2444
2522
  states.notification.create({
2445
2523
  title: `Blocklet ${capitalize(action)} Failed`,
2446
2524
  description: `Blocklet ${name}@${version} ${action} failed with error: ${err.message || 'queue exception'}`,
@@ -2811,7 +2889,15 @@ class BlockletManager extends BaseBlockletManager {
2811
2889
  } catch (err) {
2812
2890
  const b = await this._rollback(action, did, oldBlocklet);
2813
2891
  logger.error(`failed to ${action} blocklet`, { did, version, name, error: err });
2892
+
2814
2893
  this.emit(BlockletEvents.updated, b);
2894
+
2895
+ const eventNames = {
2896
+ upgrade: BlockletEvents.upgradeFailed,
2897
+ downgrade: BlockletEvents.downgradeFailed,
2898
+ };
2899
+ this.emit(eventNames[action], { blocklet: oldBlocklet, context });
2900
+
2815
2901
  states.notification.create({
2816
2902
  title: `Blocklet ${capitalize(action)} Failed`,
2817
2903
  description: `Blocklet ${name}@${version} ${action} failed with error: ${err.message}`,
@@ -3120,26 +3206,19 @@ class BlockletManager extends BaseBlockletManager {
3120
3206
 
3121
3207
  // eslint-disable-next-line no-unused-vars
3122
3208
  async _cancelDownload(blockletMeta, context) {
3123
- const { did, name, version } = blockletMeta;
3209
+ const { did } = blockletMeta;
3124
3210
 
3125
3211
  if (this.downloadCtrls[did]) {
3126
3212
  for (const cancelCtrl of this.downloadCtrls[did].values()) {
3127
3213
  cancelCtrl.cancel();
3128
3214
  }
3129
- logger.info('cancel download blocklet', { did, name, version });
3130
3215
  }
3131
3216
  }
3132
3217
 
3133
3218
  // eslint-disable-next-line no-unused-vars
3134
3219
  async _cancelWaiting(blockletMeta, context) {
3135
- const { did, name, version } = blockletMeta;
3136
-
3137
- const {
3138
- job: { postAction, oldBlocklet },
3139
- } = await this.installQueue.cancel(did);
3140
- await this._rollback(postAction, did, oldBlocklet);
3141
-
3142
- logger.info('cancel waiting blocklet', { did, name, version });
3220
+ const { did } = blockletMeta;
3221
+ return this.installQueue.cancel(did);
3143
3222
  }
3144
3223
 
3145
3224
  /**
package/lib/event.js CHANGED
@@ -5,7 +5,7 @@ const { wipeSensitiveData } = require('@blocklet/meta/lib/util');
5
5
  const logger = require('@abtnode/logger')('@abtnode/core:event');
6
6
  const { BLOCKLET_MODES, BlockletStatus, BlockletSource, BlockletEvents } = require('@blocklet/meta/lib/constants');
7
7
  const { EVENTS } = require('@abtnode/constant');
8
- const handleInstanceInStore = require('@abtnode/util/lib/public-to-store');
8
+ const handleInstanceInStore = require('./util/public-to-store');
9
9
 
10
10
  const eventHub =
11
11
  process.env.NODE_ENV === 'test' ? require('@arcblock/event-hub/single') : require('@arcblock/event-hub');
@@ -27,6 +27,7 @@ module.exports = ({
27
27
  domainStatus,
28
28
  teamAPI,
29
29
  certManager,
30
+ node,
30
31
  }) => {
31
32
  const notificationState = states.notification;
32
33
  const nodeState = states.node;
@@ -155,22 +156,65 @@ module.exports = ({
155
156
  };
156
157
 
157
158
  const handleBlockletEvent = async (eventName, payload) => {
159
+ const blocklet = payload.blocklet || payload;
160
+
158
161
  if ([BlockletEvents.deployed, BlockletEvents.installed].includes(eventName)) {
159
162
  await handleBlockletAdd(eventName, payload);
163
+
164
+ try {
165
+ await node.createAuditLog({
166
+ action: 'installBlocklet',
167
+ args: {
168
+ did: blocklet.meta.did,
169
+ },
170
+ context: payload.context || {},
171
+ result: blocklet,
172
+ });
173
+ } catch (error) {
174
+ logger.error('Failed to createAuditLog for installBlocklet', { error });
175
+ }
160
176
  } else if ([BlockletEvents.upgraded, BlockletEvents.downgraded].includes(eventName)) {
161
177
  await handleBlockletUpgrade(eventName, payload);
178
+
179
+ try {
180
+ await node.createAuditLog({
181
+ action: 'upgradeBlocklet',
182
+ args: {
183
+ did: blocklet.meta.did,
184
+ },
185
+ context: payload.context || {},
186
+ result: blocklet,
187
+ });
188
+ } catch (error) {
189
+ logger.error('Failed to createAuditLog for upgradeBlocklet', { error });
190
+ }
162
191
  } else if ([BlockletEvents.removed].includes(eventName)) {
163
192
  await handleBlockletRemove(eventName, payload);
164
193
  } else if ([BlockletEvents.started].includes(eventName)) {
165
194
  try {
166
- const blocklet = await blockletManager.ensureBlocklet(payload.meta.did);
167
- const { publicToStore } = blocklet.settings;
195
+ const { publicToStore } = blocklet.settings || {};
168
196
  // 如果一个 blocklet 没有设置 publicToStore,启动成功后不应给 store 发请求
169
197
  if (publicToStore) {
170
198
  await handleInstanceInStore(blocklet, { publicToStore });
171
199
  }
172
200
  } catch (error) {
173
- logger.error('BlockletEvents started failed', { error });
201
+ logger.error('handleInstanceInStore failed', { error });
202
+ }
203
+ } else if ([BlockletEvents.upgradeFailed, BlockletEvents.downgradeFailed].includes(eventName)) {
204
+ try {
205
+ await node.createAuditLog({
206
+ action: 'upgradeBlocklet',
207
+ args: {
208
+ did: blocklet.meta.did,
209
+ },
210
+ context: payload.context || {},
211
+ result: {
212
+ ...blocklet,
213
+ resultStatus: 'failed',
214
+ },
215
+ });
216
+ } catch (error) {
217
+ logger.error('Failed to createAuditLog for upgradeBlocklet failed', { error });
174
218
  }
175
219
  }
176
220
 
package/lib/index.js CHANGED
@@ -174,19 +174,6 @@ function ABTNode(options) {
174
174
  onStatesReady(createStateReadyHandler(routingSnapshot));
175
175
  const domainStatus = new DomainStatus(routerManager);
176
176
 
177
- const events = createEvents({
178
- blockletManager,
179
- blockletRegistry,
180
- ensureBlockletRouting,
181
- ensureBlockletRoutingForUpgrade,
182
- removeBlockletRouting,
183
- takeRoutingSnapshot,
184
- handleRouting,
185
- domainStatus,
186
- teamAPI,
187
- certManager,
188
- });
189
-
190
177
  const isInitialized = async () => {
191
178
  const state = await states.node.read();
192
179
  return states.node.isInitialized(state);
@@ -226,9 +213,9 @@ function ABTNode(options) {
226
213
  getLatestBlockletVersion: blockletManager.getLatestBlockletVersion.bind(blockletManager),
227
214
  getBlockletMetaFromUrl: blockletManager.getMetaFromUrl.bind(blockletManager),
228
215
  resetBlocklet: blockletManager.reset.bind(blockletManager),
229
- configPublicToStore: blockletManager.configPublicToStore.bind(blockletManager),
230
-
231
216
  deleteBlockletProcess: blockletManager.deleteProcess.bind(blockletManager),
217
+ configPublicToStore: blockletManager.configPublicToStore.bind(blockletManager),
218
+ updateWhoCanAccess: blockletManager.updateWhoCanAccess.bind(blockletManager),
232
219
 
233
220
  // For diagnose purpose
234
221
  syncBlockletStatus: blockletManager.status.bind(blockletManager),
@@ -319,7 +306,6 @@ function ABTNode(options) {
319
306
  processPassportIssuance: teamAPI.processPassportIssuance.bind(teamAPI),
320
307
  configTrustedPassports: teamAPI.configTrustedPassports.bind(teamAPI),
321
308
  configPassportIssuance: teamAPI.configPassportIssuance.bind(teamAPI),
322
- configWhoCanAccess: teamAPI.configWhoCanAccess.bind(teamAPI),
323
309
 
324
310
  // Challenge
325
311
  generateChallenge: states.challenge.generate.bind(states.challenge),
@@ -405,6 +391,20 @@ function ABTNode(options) {
405
391
  getRouterProvider,
406
392
  };
407
393
 
394
+ const events = createEvents({
395
+ blockletManager,
396
+ blockletRegistry,
397
+ ensureBlockletRouting,
398
+ ensureBlockletRoutingForUpgrade,
399
+ removeBlockletRouting,
400
+ takeRoutingSnapshot,
401
+ handleRouting,
402
+ domainStatus,
403
+ teamAPI,
404
+ certManager,
405
+ node: instance,
406
+ });
407
+
408
408
  const webhook = WebHook({ events, dataDirs, instance });
409
409
 
410
410
  const initCron = () => {
@@ -111,6 +111,9 @@ const getLogContent = async (action, args, context, result, info, node) => {
111
111
  case 'configBlocklet':
112
112
  return `updated following config for blocklet ${getBlockletInfo(result, info)}:\n${args.configs.map(x => `- ${x.key}: ${x.value}\n`)}`; // prettier-ignore
113
113
  case 'upgradeBlocklet':
114
+ if (result.resultStatus === 'failed') {
115
+ return `upgrade blocklet failed: ${getBlockletInfo(result, info)}`;
116
+ }
114
117
  return `upgraded blocklet ${getBlockletInfo(result, info)} to v${result.meta.version}`;
115
118
  case 'updateChildBlocklets':
116
119
  return `upgraded components for blocklet ${getBlockletInfo(result, info)}`;
@@ -141,7 +144,7 @@ const getLogContent = async (action, args, context, result, info, node) => {
141
144
  return `switched passport to ${args.passport.name} for ${team}`;
142
145
  case 'login':
143
146
  return `${user} logged in to ${team} with passport ${args.passport.name}`;
144
- case 'configWhoCanAccess':
147
+ case 'updateWhoCanAccess':
145
148
  return `updated access control policy to **${args.value}** for ${team} when ${args.reason}`;
146
149
  case 'configPassportIssuance':
147
150
  return `${args.enabled ? 'enabled' : 'disabled'} passport issuance for ${team}`;
@@ -214,12 +217,22 @@ const getLogContent = async (action, args, context, result, info, node) => {
214
217
  return `added extra domain **${args.domainAlias}** to ${site}`; // prettier-ignore
215
218
  case 'deleteDomainAlias':
216
219
  return `removed extra domain **${args.domainAlias}** from ${site}`; // prettier-ignore
220
+ case 'updateRoutingSite':
221
+ return `updated site from ${site}`; // prettier-ignore
222
+ case 'addRoutingRule':
223
+ return `added routing rule **${args.rule?.from?.pathPrefix}** from ${site}`; // prettier-ignore
224
+ case 'updateRoutingRule':
225
+ return `updated routing rule **${args.rule?.from?.pathPrefix}** from ${site}`; // prettier-ignore
226
+ case 'deleteRoutingRule':
227
+ return `deleted routing rule from ${site}`; // prettier-ignore
217
228
  case 'updateGateway': {
218
229
  let message = args.requestLimit.enabled ? `status: enabled, rate: ${args.requestLimit.rate}` : 'status: disabled';
219
230
  message = `update gateway. ${message}`;
220
231
 
221
232
  return message;
222
233
  }
234
+ case 'createTransferInvitation':
235
+ return `created a transfer node invitation(${result.inviteId})`;
223
236
 
224
237
  default:
225
238
  return action;
@@ -271,6 +284,7 @@ const getLogCategory = (action) => {
271
284
  case 'updatePermissionsForRole':
272
285
  case 'configTrustedPassports':
273
286
  case 'delegateTransferNFT':
287
+ case 'createTransferInvitation':
274
288
  return 'team';
275
289
 
276
290
  // accessKeys
@@ -312,16 +326,21 @@ const getScope = (args = {}) => {
312
326
  return args.teamDid;
313
327
  }
314
328
 
329
+ // this param usually means mutating an blockle application
330
+ if (args.did) {
331
+ // this param usually means mutating a nested child component
332
+ if (Array.isArray(args.did)) {
333
+ return args.did[0];
334
+ }
335
+
336
+ return args.did;
337
+ }
338
+
315
339
  // this param usually means mutating a child component
316
340
  if (args.rootDid) {
317
341
  return args.rootDid;
318
342
  }
319
343
 
320
- // this param usually means mutating a nested child component
321
- if (Array.isArray(args.did)) {
322
- return args.did[0];
323
- }
324
-
325
344
  return null;
326
345
  };
327
346
 
@@ -102,6 +102,22 @@ class BlockletState extends BaseState {
102
102
  });
103
103
  }
104
104
 
105
+ async getBlockletStatus(did) {
106
+ return new Promise((resolve, reject) => {
107
+ if (!did) {
108
+ resolve(null);
109
+ }
110
+
111
+ this.db.findOne({ $or: [{ 'meta.did': did }, { appDid: did }] }, { status: 1 }, (err, doc) => {
112
+ if (err) {
113
+ return reject(err);
114
+ }
115
+
116
+ return resolve(doc ? doc.status : null);
117
+ });
118
+ });
119
+ }
120
+
105
121
  hasBlocklet(did) {
106
122
  return new Promise((resolve, reject) => {
107
123
  if (!did) {
@@ -283,6 +283,14 @@ class NodeState extends BaseState {
283
283
  return this.updateNodeInfo({ previousMode: '', mode: info.previousMode });
284
284
  }
285
285
 
286
+ async setMode(mode) {
287
+ if (Object.values(NODE_MODES).includes(mode) === false) {
288
+ throw new Error(`Can not update server to unsupported mode: ${mode}`);
289
+ }
290
+
291
+ return this.updateNodeInfo({ previousMode: '', mode });
292
+ }
293
+
286
294
  async getEnvironments() {
287
295
  return this.read().then((info) => ({
288
296
  ABT_NODE: info.version,
@@ -312,6 +312,7 @@ const getComponentSystemEnvironments = (blocklet) => {
312
312
 
313
313
  return {
314
314
  BLOCKLET_REAL_DID: blocklet.env.id,
315
+ BLOCKLET_REAL_NAME: blocklet.env.name,
315
316
  BLOCKLET_DATA_DIR: blocklet.env.dataDir,
316
317
  BLOCKLET_LOG_DIR: blocklet.env.logsDir,
317
318
  BLOCKLET_CACHE_DIR: blocklet.env.cacheDir,
@@ -337,11 +338,21 @@ const getRuntimeEnvironments = (blocklet, nodeEnvironments, ancestors) => {
337
338
  }
338
339
  : {};
339
340
 
341
+ const root = (ancestors || [])[0] || blocklet;
342
+ const ports = {};
343
+ forEachBlockletSync(root, (x) => {
344
+ const webInterface = findWebInterface(x);
345
+ if (webInterface && x.environmentObj[webInterface.port]) {
346
+ ports[x.environmentObj.BLOCKLET_REAL_NAME] = x.environmentObj[webInterface.port];
347
+ }
348
+ });
349
+
340
350
  return {
341
351
  ...blocklet.configObj,
342
352
  ...getSharedConfigObj(blocklet, ancestors),
343
353
  ...blocklet.environmentObj,
344
354
  ...devEnvironments,
355
+ BLOCKLET_WEB_PORTS: JSON.stringify(ports),
345
356
  ...nodeEnvironments,
346
357
  ...safeNodeEnvironments,
347
358
  };
@@ -415,7 +426,7 @@ const getBlockletMetaFromUrls = async (urls) => {
415
426
  const meta = await any(urls.map(getBlockletMetaFromUrl));
416
427
  return meta;
417
428
  } catch (err) {
418
- logger.error('failed get blocklet meta', { urls });
429
+ logger.error('failed get blocklet meta', { urls, error: err });
419
430
  throw new Error('Failed get blocklet meta');
420
431
  }
421
432
  };
@@ -729,7 +740,7 @@ const parseChildrenFromMeta = async (src, context = {}) => {
729
740
  try {
730
741
  m = await getBlockletMetaFromUrls(urls);
731
742
  } catch {
732
- throw new Error('Failed get component meta');
743
+ throw new Error(`Failed get component meta: ${config.title || config.name}`);
733
744
  }
734
745
 
735
746
  validateBlockletMeta(m, { ensureDist: true });
@@ -0,0 +1,85 @@
1
+ const { sign } = require('@arcblock/jwt');
2
+ const { BlockletSource } = require('@blocklet/meta/lib/constants');
3
+ const { WHO_CAN_ACCESS } = require('@abtnode/constant');
4
+ const logger = require('@abtnode/logger')('@abtnode/util:public-to-store');
5
+
6
+ const getWallet = require('@abtnode/util/lib/get-app-wallet');
7
+ const axios = require('@abtnode/util/lib/axios');
8
+
9
+ const getAppToken = (blocklet) =>
10
+ sign(blocklet.environmentObj?.BLOCKLET_APP_ID, blocklet.environmentObj?.BLOCKLET_APP_SK);
11
+
12
+ const getAppId = (blocklet) => blocklet.environmentObj?.BLOCKLET_APP_ID;
13
+
14
+ const getAppSK = (blocklet) => blocklet.environmentObj?.BLOCKLET_APP_SK;
15
+
16
+ const getBlockletDid = (blocklet) => blocklet.meta?.bundleDid;
17
+
18
+ const getAppUrl = (blocklet) => blocklet.environmentObj?.BLOCKLET_APP_URL;
19
+
20
+ /**
21
+ * verify manages the permissions of blocklet public instance
22
+ * @param {*} blocklet
23
+ * @returns bool
24
+ */
25
+ const verifyPublicInstance = (blocklet) => {
26
+ const { whoCanAccess = WHO_CAN_ACCESS.ALL } = blocklet.settings;
27
+ return blocklet.source === BlockletSource.registry && whoCanAccess === WHO_CAN_ACCESS.ALL;
28
+ };
29
+
30
+ /**
31
+ *
32
+ * @param {*} blocklet
33
+ * @param {*} {userDid publicToStore}
34
+ * @returns bool
35
+ */
36
+ async function handleInstanceInStore(blocklet, { userDid = null, publicToStore = false } = {}) {
37
+ if (!blocklet) {
38
+ logger.error('blocklet argument is required');
39
+ throw new Error('blocklet argument is required');
40
+ }
41
+
42
+ const ownerDid = userDid || blocklet.settings?.owner?.did;
43
+ if (!ownerDid) {
44
+ return false;
45
+ }
46
+
47
+ if (!verifyPublicInstance(blocklet)) {
48
+ logger.error('no permission to set publicInstance');
49
+ throw new Error('no permission to set publicInstance');
50
+ }
51
+
52
+ const appToken = getAppToken(blocklet);
53
+ const blockletDid = getBlockletDid(blocklet);
54
+ const appId = getAppId(blocklet);
55
+ const appSK = getAppSK(blocklet);
56
+ const wallet = getWallet(appSK);
57
+ const body = {
58
+ appToken,
59
+ appId,
60
+ appPK: wallet.publicKey,
61
+ ownerDid,
62
+ appUrl: getAppUrl(blocklet),
63
+ blockletDid,
64
+ };
65
+
66
+ const api = axios.create({ baseURL: blocklet.deployedFrom, timeout: 1000 * 10 });
67
+ if (!publicToStore) {
68
+ try {
69
+ await api.delete(`/api/blocklet-instances/${appId}`, { data: body });
70
+ } catch (error) {
71
+ logger.error('failed to delete blocklet instance', { error });
72
+ throw new Error('failed to delete blocklet instance');
73
+ }
74
+ } else {
75
+ try {
76
+ await api.put(`/api/blocklet-instances/${appId}`, body);
77
+ } catch (error) {
78
+ logger.error('failed to update blocklet instance', { error });
79
+ throw new Error('failed to delete blocklet instance');
80
+ }
81
+ }
82
+ return true;
83
+ }
84
+
85
+ module.exports = handleInstanceInStore;
@@ -126,7 +126,7 @@ const updateSite = Joi.object({
126
126
  )
127
127
  .optional()
128
128
  .messages(domainMessages),
129
- });
129
+ }).unknown();
130
130
 
131
131
  const addRuleSchema = Joi.object({
132
132
  id: Joi.string().required(),
@@ -4,6 +4,7 @@ const { evaluateURLs } = require('@abtnode/util/lib/url-evaluation');
4
4
  const checkURLAccessible = require('@abtnode/util/lib/url-evaluation/check-accessible-node');
5
5
  const { EVENTS } = require('@abtnode/constant');
6
6
  const WebHookSender = require('./sender');
7
+ const WalletSender = require('./sender/wallet');
7
8
  const createQueue = require('../queue');
8
9
  const IP = require('../util/ip');
9
10
  const states = require('../states');
@@ -72,6 +73,11 @@ module.exports = ({ events, dataDirs, instance }) => {
72
73
  const baseUrls = await getBaseUrls(instance, [external, internal]);
73
74
  const senderFns = {};
74
75
 
76
+ // Always send message to wallet
77
+ webhookList.push({
78
+ type: WalletSender.type,
79
+ });
80
+
75
81
  if (webhookList.length) {
76
82
  for (let i = 0; i < webhookList.length; i++) {
77
83
  const item = webhookList[i];
@@ -79,8 +85,7 @@ module.exports = ({ events, dataDirs, instance }) => {
79
85
  const senderInstance = WebHookSender.getMessageSender(item.type);
80
86
  senderFns[item.type] = senderInstance.send.bind(senderInstance);
81
87
  }
82
- const { name } = nodeInfo;
83
- const options = { ...message, nodeInfo: `*${name}*` };
88
+ const options = { ...message, nodeInfo, node: instance };
84
89
  if (item.type === 'slack') {
85
90
  // eslint-disable-next-line
86
91
  options.urlInfo = await getSlackUrlInfo(message.action, baseUrls);
@@ -3,6 +3,7 @@ const logger = require('@abtnode/logger')('@abtnode/core:sender');
3
3
 
4
4
  const Slack = require('./slack');
5
5
  const Api = require('./api');
6
+ const Wallet = require('./wallet');
6
7
 
7
8
  const SenderMap = new Map([
8
9
  [Slack.type, Slack],
@@ -12,6 +13,10 @@ const SenderMap = new Map([
12
13
  const getSenderNames = () => [...SenderMap.keys()];
13
14
 
14
15
  const getSender = (name) => {
16
+ if (name === Wallet.type) {
17
+ return Wallet;
18
+ }
19
+
15
20
  if (!SenderMap.has(name)) {
16
21
  logger.error(`getSender:sender name [${name}] does not exist`);
17
22
  return null;
@@ -29,7 +29,7 @@ class SlackSender extends BaseSender {
29
29
  type: 'section',
30
30
  text: {
31
31
  type: 'mrkdwn',
32
- text: nodeInfo,
32
+ text: `*${nodeInfo.name}*`,
33
33
  },
34
34
  },
35
35
  {
@@ -0,0 +1,45 @@
1
+ const logger = require('@abtnode/logger')('@abtnode/core:sender:api');
2
+ const { sendToUser } = require('@blocklet/sdk/lib/util/send-notification');
3
+ const { ROLES, PASSPORT_STATUS } = require('@abtnode/constant');
4
+ const BaseSender = require('../base');
5
+
6
+ class WalletSender extends BaseSender {
7
+ async send(params, data = {}) {
8
+ const { title, description, nodeInfo, node } = data;
9
+
10
+ try {
11
+ const sender = {
12
+ appDid: nodeInfo.did,
13
+ appSk: nodeInfo.sk,
14
+ };
15
+
16
+ const message = {
17
+ title,
18
+ body: description,
19
+ };
20
+
21
+ const { users } = await node.getUsers({ teamDid: nodeInfo.did, paging: { pageSize: 100 } });
22
+ const adminUsers = users
23
+ .filter(
24
+ (x) =>
25
+ x.approved &&
26
+ (x.passports || []).some(
27
+ (y) => [ROLES.OWNER, ROLES.ADMIN].includes(y.name) && y.status === PASSPORT_STATUS.VALID
28
+ )
29
+ )
30
+ .map((x) => x.did);
31
+
32
+ if (!adminUsers.length) {
33
+ return;
34
+ }
35
+
36
+ await sendToUser(adminUsers, message, sender, process.env.ABT_NODE_SERVICE_PORT);
37
+ } catch (error) {
38
+ logger.error('failed to push notification to wallet', { error });
39
+ }
40
+ }
41
+ }
42
+
43
+ WalletSender.type = 'wallet';
44
+
45
+ module.exports = WalletSender;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.8.2",
6
+ "version": "1.8.5",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,31 +19,32 @@
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
- "@abtnode/certificate-manager": "1.8.2",
23
- "@abtnode/constant": "1.8.2",
24
- "@abtnode/cron": "1.8.2",
25
- "@abtnode/db": "1.8.2",
26
- "@abtnode/logger": "1.8.2",
27
- "@abtnode/queue": "1.8.2",
28
- "@abtnode/rbac": "1.8.2",
29
- "@abtnode/router-provider": "1.8.2",
30
- "@abtnode/static-server": "1.8.2",
31
- "@abtnode/timemachine": "1.8.2",
32
- "@abtnode/util": "1.8.2",
33
- "@arcblock/did": "1.17.2",
22
+ "@abtnode/certificate-manager": "1.8.5",
23
+ "@abtnode/constant": "1.8.5",
24
+ "@abtnode/cron": "1.8.5",
25
+ "@abtnode/db": "1.8.5",
26
+ "@abtnode/logger": "1.8.5",
27
+ "@abtnode/queue": "1.8.5",
28
+ "@abtnode/rbac": "1.8.5",
29
+ "@abtnode/router-provider": "1.8.5",
30
+ "@abtnode/static-server": "1.8.5",
31
+ "@abtnode/timemachine": "1.8.5",
32
+ "@abtnode/util": "1.8.5",
33
+ "@arcblock/did": "1.17.6",
34
34
  "@arcblock/did-motif": "^1.1.10",
35
- "@arcblock/did-util": "1.17.2",
36
- "@arcblock/event-hub": "1.17.2",
37
- "@arcblock/jwt": "^1.17.2",
35
+ "@arcblock/did-util": "1.17.6",
36
+ "@arcblock/event-hub": "1.17.6",
37
+ "@arcblock/jwt": "^1.17.6",
38
38
  "@arcblock/pm2-events": "^0.0.5",
39
- "@arcblock/vc": "1.17.2",
40
- "@blocklet/meta": "1.8.2",
39
+ "@arcblock/vc": "1.17.6",
40
+ "@blocklet/meta": "1.8.5",
41
+ "@blocklet/sdk": "1.8.5",
41
42
  "@fidm/x509": "^1.2.1",
42
- "@nedb/core": "^1.3.1",
43
- "@nedb/multi": "^1.3.1",
44
- "@ocap/mcrypto": "1.17.2",
45
- "@ocap/util": "1.17.2",
46
- "@ocap/wallet": "1.17.2",
43
+ "@nedb/core": "^1.3.2",
44
+ "@nedb/multi": "^1.3.2",
45
+ "@ocap/mcrypto": "1.17.6",
46
+ "@ocap/util": "1.17.6",
47
+ "@ocap/wallet": "1.17.6",
47
48
  "@slack/webhook": "^5.0.3",
48
49
  "axios": "^0.27.2",
49
50
  "axon": "^2.0.3",
@@ -81,5 +82,5 @@
81
82
  "express": "^4.17.1",
82
83
  "jest": "^27.4.5"
83
84
  },
84
- "gitHead": "fcbe3c97f3825c507ee16714f49bbf8f58c5b59f"
85
+ "gitHead": "58da88581cdf1dd45fd50e56db08a055be82626e"
85
86
  }