@abtnode/core 1.16.13-beta-55b3e93d → 1.16.13-beta-423a40b1

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
@@ -340,6 +340,7 @@ class TeamAPI extends EventEmitter {
340
340
  logger.info('user approval updated successfully', { teamDid, userDid: user.did, approved: user.approved });
341
341
 
342
342
  this.emit(EVENTS.USER_UPDATED, { teamDid, user: doc2 });
343
+ this.emit(EVENTS.USER_PERMISSION_UPDATED, { teamDid, user: doc2 });
343
344
 
344
345
  return doc2;
345
346
  }
@@ -442,6 +443,7 @@ class TeamAPI extends EventEmitter {
442
443
  logger.info('user passport revoked successfully', { teamDid, userDid, passportId });
443
444
 
444
445
  this.emit(EVENTS.USER_UPDATED, { teamDid, user: doc });
446
+ this.emit(EVENTS.USER_PERMISSION_UPDATED, { teamDid, user: doc });
445
447
 
446
448
  return doc;
447
449
  }
@@ -458,6 +460,7 @@ class TeamAPI extends EventEmitter {
458
460
  logger.info('user passport enabled successfully', { teamDid, userDid, passportId });
459
461
 
460
462
  this.emit(EVENTS.USER_UPDATED, { teamDid, user: doc });
463
+ this.emit(EVENTS.USER_PERMISSION_UPDATED, { teamDid, user: doc });
461
464
 
462
465
  return doc;
463
466
  }
@@ -30,7 +30,7 @@ const runUserHook = async (label, hookName, args) => {
30
30
  // FIXME @linchen timeout 应该动态设置或不设置
31
31
  await runScript(hook, [label, hookName].join(':'), {
32
32
  cwd: appDir,
33
- env: getSafeEnv(env),
33
+ env: { ...getSafeEnv(env), BLOCKLET_HOOK_NAME: hookName },
34
34
  silent,
35
35
  output: outputFile,
36
36
  error: errorFile,
@@ -9,6 +9,7 @@ const omit = require('lodash/omit');
9
9
  const merge = require('lodash/merge');
10
10
  const pick = require('lodash/pick');
11
11
  const isEmpty = require('lodash/isEmpty');
12
+ const cloneDeep = require('lodash/cloneDeep');
12
13
  const { isNFTExpired, getNftExpirationDate } = require('@abtnode/util/lib/nft');
13
14
  const didDocument = require('@abtnode/util/lib/did-document');
14
15
  const { sign } = require('@arcblock/jwt');
@@ -142,6 +143,7 @@ const RollbackCache = require('./helper/rollback-cache');
142
143
  const { migrateApplicationToStructV2 } = require('./helper/migrate-application-to-struct-v2');
143
144
  const { getBackupFilesUrlFromEndpoint, getBackupEndpoint, getSpaceNameByEndpoint } = require('../../util/spaces');
144
145
  const { validateAddSpaceGateway, validateUpdateSpaceGateway } = require('../../validators/space-gateway');
146
+ const { sessionConfigSchema } = require('../../validators/util');
145
147
 
146
148
  const { formatEnvironments, getBlockletMeta, validateOwner } = util;
147
149
 
@@ -539,7 +541,7 @@ class BlockletManager extends BaseBlockletManager {
539
541
  entity: 'blocklet',
540
542
  action: 'check_if_started',
541
543
  ...params,
542
- id: did,
544
+ id: `${did}/${(componentDids || []).join(',')}`,
543
545
  });
544
546
  }
545
547
 
@@ -701,7 +703,7 @@ class BlockletManager extends BaseBlockletManager {
701
703
  const ticket = this.startQueue.push({
702
704
  entity: 'blocklet',
703
705
  action: 'restart',
704
- id: did,
706
+ id: `${did}/${(componentDids || []).join(',')}`,
705
707
  did,
706
708
  componentDids,
707
709
  context,
@@ -1385,6 +1387,27 @@ class BlockletManager extends BaseBlockletManager {
1385
1387
  return this.getBlocklet(rootDid);
1386
1388
  }
1387
1389
 
1390
+ async updateAppSessionConfig({ did, config }) {
1391
+ const validateConfig = await sessionConfigSchema.validateAsync(config);
1392
+
1393
+ const blocklet = await this.getBlocklet(did);
1394
+ if (!blocklet) {
1395
+ throw new Error('blocklet does not exist');
1396
+ }
1397
+
1398
+ const sessionConfig = cloneDeep(blocklet.settings.session || {});
1399
+
1400
+ sessionConfig.cacheTtl = validateConfig.cacheTtl;
1401
+ sessionConfig.ttl = validateConfig.ttl;
1402
+
1403
+ await states.blockletExtras.setSettings(blocklet.meta.did, { session: sessionConfig });
1404
+
1405
+ const newState = await this.getBlocklet(did);
1406
+ this.emit(BlockletEvents.updated, newState);
1407
+
1408
+ return newState;
1409
+ }
1410
+
1388
1411
  // eslint-disable-next-line no-unused-vars
1389
1412
  async getRuntimeHistory({ did, hours }, context) {
1390
1413
  const metaDid = await states.blocklet.getBlockletMetaDid(did);
@@ -1944,7 +1967,8 @@ class BlockletManager extends BaseBlockletManager {
1944
1967
  }
1945
1968
 
1946
1969
  async _onCheckIfStarted(jobInfo, { throwOnError } = {}) {
1947
- const { did, context, minConsecutiveTime = 5000, timeout, componentDids } = jobInfo;
1970
+ const startedAt = Date.now();
1971
+ const { did, context, minConsecutiveTime = 2000, timeout, componentDids } = jobInfo;
1948
1972
  const blocklet = await this.getBlocklet(did);
1949
1973
 
1950
1974
  const { meta } = blocklet;
@@ -1965,6 +1989,7 @@ class BlockletManager extends BaseBlockletManager {
1965
1989
  });
1966
1990
 
1967
1991
  this.emit(BlockletEvents.started, res);
1992
+ logger.info('blocklet healthy', { did, name, time: Date.now() - startedAt });
1968
1993
  } catch (error) {
1969
1994
  const status = await states.blocklet.getBlockletStatus(did);
1970
1995
  if ([BlockletStatus.stopping, BlockletStatus.stopped].includes(status)) {
@@ -1974,7 +1999,7 @@ class BlockletManager extends BaseBlockletManager {
1974
1999
 
1975
2000
  logger.error('check blocklet if started failed', { did, name, context, timeout, error });
1976
2001
 
1977
- await this.deleteProcess({ did }, context);
2002
+ await this.deleteProcess({ did, componentDids }, context);
1978
2003
  await states.blocklet.setBlockletStatus(did, BlockletStatus.error, { componentDids });
1979
2004
 
1980
2005
  this._createNotification(did, {
@@ -145,6 +145,9 @@ const installApplicationFromBackup = async ({
145
145
  extra.controller = controller;
146
146
  }
147
147
 
148
+ // 在旧版本中安装的 Blocklet 不存在 extra.meta 字段,这里补充一下
149
+ extra.meta = extra.meta || { did, name: appName };
150
+
148
151
  await states.blockletExtras.insert(extra);
149
152
  logger.info('blocklet extra is copied successfully');
150
153
 
package/lib/event.js CHANGED
@@ -105,6 +105,7 @@ module.exports = ({
105
105
 
106
106
  // Emit events to node listener
107
107
  // Call eventHandler
108
+ // NOT emit events to event hub
108
109
  const onInternalEvent = (name, data) => {
109
110
  events.emit(name, data);
110
111
  if (typeof eventHandler === 'function') {
@@ -421,6 +422,7 @@ module.exports = ({
421
422
  listen(teamAPI, EVENTS.USER_ADDED, onEvent);
422
423
  listen(teamAPI, EVENTS.USER_REMOVED, onEvent);
423
424
  listen(teamAPI, EVENTS.USER_UPDATED, onEvent);
425
+ listen(teamAPI, EVENTS.USER_PERMISSION_UPDATED, onEvent);
424
426
  listen(teamAPI, BlockletEvents.updated, onEvent);
425
427
  listen(teamManager, BlockletEvents.storeChange, onEvent);
426
428
 
package/lib/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
+ const uniq = require('lodash/uniq');
3
4
  const md5 = require('@abtnode/util/lib/md5');
4
5
  const formatContext = require('@abtnode/util/lib/format-context');
5
6
  const Cron = require('@abtnode/cron');
@@ -259,6 +260,7 @@ function ABTNode(options) {
259
260
  configOAuth: blockletManager.configOAuth.bind(blockletManager),
260
261
  configNotification: blockletManager.configNotification.bind(blockletManager),
261
262
  updateWhoCanAccess: blockletManager.updateWhoCanAccess.bind(blockletManager),
263
+ updateAppSessionConfig: blockletManager.updateAppSessionConfig.bind(blockletManager),
262
264
  updateComponentTitle: blockletManager.updateComponentTitle.bind(blockletManager),
263
265
  updateComponentMountPoint: blockletManager.updateComponentMountPoint.bind(blockletManager),
264
266
  backupBlocklet: blockletManager.backup.bind(blockletManager),
@@ -390,7 +392,24 @@ function ABTNode(options) {
390
392
 
391
393
  // AuditLog
392
394
  createAuditLog: (params) => states.auditLog.create(params, instance),
393
- getAuditLogs: states.auditLog.findPaginated.bind(states.auditLog),
395
+ getAuditLogs: async (params) => {
396
+ if (params.scope) {
397
+ const blocklet = await states.blocklet.getBlocklet(params.scope);
398
+ if (blocklet) {
399
+ params.scope = uniq(
400
+ [
401
+ params.scope,
402
+ blocklet.appDid,
403
+ blocklet.appPid,
404
+ blocklet.structV1Did,
405
+ ...blocklet.migratedFrom.map((x) => x.appDid),
406
+ ].filter(Boolean)
407
+ );
408
+ }
409
+ }
410
+
411
+ return states.auditLog.findPaginated.call(states.auditLog, params);
412
+ },
394
413
 
395
414
  // Routing
396
415
  routerManager,
@@ -3,13 +3,14 @@
3
3
  const fs = require('fs-extra');
4
4
  const path = require('path');
5
5
  const tar = require('tar');
6
+ const UUID = require('uuid');
6
7
  const isUrl = require('is-url');
7
8
  const get = require('lodash/get');
8
9
  const cloneDeep = require('lodash/cloneDeep');
9
10
  const groupBy = require('lodash/groupBy');
10
11
  const isEqual = require('lodash/isEqual');
11
12
  const joinUrl = require('url-join');
12
- const { replaceSlotToIp, findComponentById, findWebInterface } = require('@blocklet/meta/lib/util');
13
+ const { replaceSlotToIp, findComponentById, findWebInterface, getComponentId } = require('@blocklet/meta/lib/util');
13
14
  const { getProvider } = require('@abtnode/router-provider');
14
15
  const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
15
16
  const getTmpDir = require('@abtnode/util/lib/get-tmp-directory');
@@ -488,6 +489,60 @@ const ensureBlockletWellknownRules = (sites, blocklets) => {
488
489
  .filter(Boolean);
489
490
  };
490
491
 
492
+ // Expand component rules to blocklet rules
493
+ const isComponentRule = (x) => x.to.type === ROUTING_RULE_TYPES.COMPONENT;
494
+ const expandComponentRules = (sites = [], blocklets) => {
495
+ return sites
496
+ .map((site) => {
497
+ if (!site.domain.endsWith(BLOCKLET_SITE_GROUP_SUFFIX)) {
498
+ return site;
499
+ }
500
+
501
+ if (site.componentExpanded) {
502
+ return site;
503
+ }
504
+
505
+ const blocklet = blocklets.find((x) => x.meta.did === site.blockletDid);
506
+ const components = blocklet.children.filter((x) => x.mountPoint !== '/' && x.mode === BLOCKLET_MODES.PRODUCTION);
507
+ const expandedRules = [];
508
+
509
+ site.rules.filter(isComponentRule).forEach((baseRule) => {
510
+ components.forEach((x) => {
511
+ const newRule = {
512
+ id: UUID.v4(),
513
+ groupId: baseRule.groupId,
514
+ from: {
515
+ groupPathPrefix: '/',
516
+ },
517
+ to: {
518
+ type: ROUTING_RULE_TYPES.BLOCKLET,
519
+ componentId: getComponentId(x, [blocklet]),
520
+ interfaceName: BLOCKLET_INTERFACE_PUBLIC,
521
+ port: findInterfacePortByName(x, BLOCKLET_INTERFACE_PUBLIC),
522
+ did: blocklet.meta.did,
523
+ target: '/',
524
+ },
525
+ };
526
+
527
+ if (x.meta.did === baseRule.to.componentId) {
528
+ newRule.from.pathPrefix = baseRule.from.pathPrefix;
529
+ newRule.to.pageGroup = baseRule.to.pageGroup;
530
+ } else {
531
+ newRule.from.pathPrefix = joinUrl(baseRule.from.pathPrefix, x.mountPoint);
532
+ }
533
+
534
+ expandedRules.push(newRule);
535
+ });
536
+ });
537
+
538
+ site.rules = site.rules.filter((x) => !isComponentRule(x)).concat(expandedRules);
539
+ site.componentExpanded = true;
540
+
541
+ return site;
542
+ })
543
+ .filter(Boolean);
544
+ };
545
+
491
546
  const ensureBlockletCache = (sites = [], blocklets) => {
492
547
  return sites
493
548
  .map((site) => {
@@ -545,12 +600,13 @@ const ensureLatestInfo = async (sites = [], { withDefaultCors = true } = {}) =>
545
600
  let result = await ensureLatestNodeInfo(sites, { withDefaultCors });
546
601
  result = await ensureBlockletDid(result);
547
602
  result = await filterSitesForRemovedBlocklets(sites, blocklets);
548
- result = await ensureBlockletWellknownRules(result, blocklets);
549
603
  result = await ensureBlockletCache(result, blocklets);
550
604
  result = await ensureWellknownRule(result);
551
605
  result = await ensureCorsForWebWallet(result);
552
606
  result = await ensureCorsForDidSpace(result, blocklets);
553
607
  result = await ensureLatestInterfaceInfo(result);
608
+ result = await ensureBlockletWellknownRules(result, blocklets);
609
+ result = await expandComponentRules(result, blocklets);
554
610
 
555
611
  return result;
556
612
  };
@@ -1457,3 +1513,4 @@ module.exports.ensureLatestInterfaceInfo = ensureLatestInterfaceInfo;
1457
1513
  module.exports.ensureLatestInfo = ensureLatestInfo;
1458
1514
  module.exports.ensureWellknownRule = ensureWellknownRule;
1459
1515
  module.exports.ensureBlockletWellknownRules = ensureBlockletWellknownRules;
1516
+ module.exports.expandComponentRules = expandComponentRules;
@@ -223,6 +223,7 @@ Router.formatSites = (sites = []) => {
223
223
  did: rule.to.did,
224
224
  componentId: rule.to.componentId,
225
225
  cacheGroup: site.mode === BLOCKLET_MODES.PRODUCTION ? 'blockletJs' : '',
226
+ pageGroup: rule.to.pageGroup,
226
227
  },
227
228
  });
228
229
  site.rules.push({
@@ -238,6 +239,7 @@ Router.formatSites = (sites = []) => {
238
239
  did: rule.to.did,
239
240
  componentId: rule.to.componentId,
240
241
  cacheGroup: site.mode === BLOCKLET_MODES.PRODUCTION ? 'blockletJs' : '',
242
+ pageGroup: rule.to.pageGroup,
241
243
  },
242
244
  });
243
245
  });
@@ -271,6 +273,7 @@ Router.formatSites = (sites = []) => {
271
273
  type: ROUTING_RULE_TYPES.DAEMON,
272
274
  port: daemonRule.to.port,
273
275
  cacheGroup: site.mode === BLOCKLET_MODES.PRODUCTION ? 'blockletJs' : '',
276
+ pageGroup: rule.to.pageGroup,
274
277
  did: rule.to.did,
275
278
  },
276
279
  });
@@ -287,6 +290,7 @@ Router.formatSites = (sites = []) => {
287
290
  type: ROUTING_RULE_TYPES.DAEMON,
288
291
  target: BLOCKLET_PROXY_PATH_PREFIX,
289
292
  cacheGroup: !isServiceFeDevelopment && site.mode === BLOCKLET_MODES.PRODUCTION ? 'blockletProxy' : '',
293
+ pageGroup: rule.to.pageGroup,
290
294
  did: rule.to.did,
291
295
  },
292
296
  });
@@ -598,30 +598,30 @@ class RouterManager extends EventEmitter {
598
598
 
599
599
  /**
600
600
  * get all rules to be add or update to site from root rule
601
- * @param {*} rootRule
601
+ * @param {*} rawRule
602
602
  */
603
- async getRulesForMutation(rootRule) {
604
- if (rootRule.to.type !== ROUTING_RULE_TYPES.BLOCKLET) {
605
- return [rootRule];
603
+ async getRulesForMutation(rawRule) {
604
+ if (rawRule.to.type !== ROUTING_RULE_TYPES.BLOCKLET) {
605
+ return [rawRule];
606
606
  }
607
607
 
608
608
  const rules = [];
609
609
 
610
610
  // get child rules
611
- const blocklet = await states.blocklet.getBlocklet(rootRule.to.did);
611
+ const blocklet = await states.blocklet.getBlocklet(rawRule.to.did);
612
612
 
613
613
  // blocklet may be mounted in relative prefix (for old usage), so blockletPrefix may not be '/'
614
- // blocklet prefix is the origin pathPrefix in rootRule
615
- const blockletPrefix = normalizePathPrefix(rootRule.from.pathPrefix);
614
+ // blocklet prefix is the origin pathPrefix in rawRule
615
+ const blockletPrefix = normalizePathPrefix(rawRule.from.pathPrefix);
616
616
 
617
617
  // root component's mountPoint may not be '/'
618
618
  const rootComponentPrefix = joinUrl(blockletPrefix, blocklet.mountPoint || '/');
619
- rootRule.from.pathPrefix = normalizePathPrefix(rootComponentPrefix);
619
+ rawRule.from.pathPrefix = normalizePathPrefix(rootComponentPrefix);
620
620
 
621
621
  const isOccupiable = blocklet.meta.group === BlockletGroup.gateway;
622
622
 
623
623
  if (!isOccupiable) {
624
- rules.push(rootRule);
624
+ rules.push(rawRule);
625
625
  }
626
626
 
627
627
  forEachChildSync(blocklet, (component, { id, ancestors }) => {
@@ -650,7 +650,7 @@ class RouterManager extends EventEmitter {
650
650
  mountPoint
651
651
  );
652
652
 
653
- const occupied = normalizePathPrefix(pathPrefix) === normalizePathPrefix(rootRule.from.pathPrefix);
653
+ const occupied = normalizePathPrefix(pathPrefix) === normalizePathPrefix(rawRule.from.pathPrefix);
654
654
 
655
655
  if (occupied && !isOccupiable) {
656
656
  return;
@@ -658,8 +658,8 @@ class RouterManager extends EventEmitter {
658
658
 
659
659
  // if is root path, child rule become root rule
660
660
  const childRule = {
661
- id: occupied ? rootRule.id : uuid.v4(),
662
- groupId: rootRule.id,
661
+ id: occupied ? rawRule.id : uuid.v4(),
662
+ groupId: rawRule.id,
663
663
  from: {
664
664
  pathPrefix: normalizePathPrefix(pathPrefix),
665
665
  groupPathPrefix: blockletPrefix,
@@ -667,11 +667,11 @@ class RouterManager extends EventEmitter {
667
667
  to: {
668
668
  type: ROUTING_RULE_TYPES.BLOCKLET,
669
669
  port: findInterfacePortByName(component, childWebInterface.name),
670
- did: rootRule.to.did, // root component did
671
- interfaceName: rootRule.to.interfaceName, // root component interface
670
+ did: rawRule.to.did, // root component did
671
+ interfaceName: rawRule.to.interfaceName, // root component interface
672
672
  componentId: id,
673
673
  },
674
- isProtected: occupied ? rootRule.isProtected : true,
674
+ isProtected: occupied ? rawRule.isProtected : true,
675
675
  };
676
676
 
677
677
  rules.push(childRule);
@@ -167,6 +167,8 @@ const getLogContent = async (action, args, context, result, info, node) => {
167
167
  return `removed blocklet store ${args.url}`;
168
168
  case 'selectBlockletStore':
169
169
  return `selected blocklet store ${args.url}`;
170
+ case 'updateAppSessionConfig':
171
+ return `updated session config: ${JSON.stringify(args.config)}`;
170
172
 
171
173
  // teams: members/passports
172
174
  case 'addUser':
@@ -153,6 +153,7 @@ class BlockletState extends BaseState {
153
153
  this.defaultPort = config.blockletPort || 5555;
154
154
  // @didMap: { [did: string]: metaDid: string }
155
155
  this.didMap = new Map();
156
+ this.statusLocks = new Map();
156
157
  }
157
158
 
158
159
  async getBlocklet(did, { decryptSk = true } = {}) {
@@ -207,6 +208,8 @@ class BlockletState extends BaseState {
207
208
 
208
209
  this.didMap.delete(doc.meta?.did);
209
210
  this.didMap.delete(doc.appDid);
211
+ this.statusLocks.delete(doc.meta?.did);
212
+ this.statusLocks.delete(doc.appDid);
210
213
 
211
214
  this.emit('remove', doc);
212
215
  return formatBlocklet(doc, 'onRead', this.config.dek);
@@ -505,39 +508,49 @@ class BlockletState extends BaseState {
505
508
  throw new Error('Unsupported blocklet status');
506
509
  }
507
510
 
508
- const doc = await this.getBlocklet(did);
509
-
510
- if (doc.meta?.group === BlockletGroup.gateway && !doc.children?.length) {
511
- return this.updateBlocklet(did, { status });
512
- }
511
+ const statusLock = this._getStatusLock(did);
513
512
 
514
- // for backward compatibility
515
- if (!doc.structVersion && !doc.children?.length) {
516
- return this.updateBlocklet(did, { status });
517
- }
513
+ try {
514
+ await statusLock.acquire();
515
+ const doc = await this.getBlocklet(did);
518
516
 
519
- // update children status
520
- forEachComponentV2Sync(doc, (component) => {
521
- if (component.meta.group === BlockletGroup.gateway) {
522
- return;
517
+ if (doc.meta?.group === BlockletGroup.gateway && !doc.children?.length) {
518
+ return this.updateBlocklet(did, { status });
523
519
  }
524
520
 
525
- if (shouldSkipComponent(component.meta.did, componentDids)) {
526
- return;
521
+ // for backward compatibility
522
+ if (!doc.structVersion && !doc.children?.length) {
523
+ return this.updateBlocklet(did, { status });
527
524
  }
528
525
 
529
- component.status = status;
530
- if (status === BlockletStatus.running) {
531
- component.startedAt = new Date();
532
- component.stoppedAt = null;
533
- }
534
- if (status === BlockletStatus.stopped) {
535
- component.startedAt = null;
536
- component.stoppedAt = new Date();
537
- }
538
- });
526
+ // update children status
527
+ forEachComponentV2Sync(doc, (component) => {
528
+ if (component.meta.group === BlockletGroup.gateway) {
529
+ return;
530
+ }
539
531
 
540
- return this.updateBlocklet(did, pick(doc, ['status', 'children']));
532
+ if (shouldSkipComponent(component.meta.did, componentDids)) {
533
+ return;
534
+ }
535
+
536
+ component.status = status;
537
+ if (status === BlockletStatus.running) {
538
+ component.startedAt = new Date();
539
+ component.stoppedAt = null;
540
+ }
541
+ if (status === BlockletStatus.stopped) {
542
+ component.startedAt = null;
543
+ component.stoppedAt = new Date();
544
+ }
545
+ });
546
+
547
+ const res = await this.updateBlocklet(did, pick(doc, ['status', 'children']));
548
+ statusLock.release();
549
+ return res;
550
+ } catch (error) {
551
+ statusLock.release();
552
+ throw error;
553
+ }
541
554
  }
542
555
 
543
556
  setInstalledAt(did) {
@@ -641,6 +654,14 @@ class BlockletState extends BaseState {
641
654
  updateStructV1Did(did, v1Did) {
642
655
  return this.updateBlocklet(did, { structV1Did: v1Did });
643
656
  }
657
+
658
+ _getStatusLock(did) {
659
+ if (!this.statusLocks.has(did)) {
660
+ this.statusLocks[did] = new Lock();
661
+ }
662
+
663
+ return this.statusLocks[did];
664
+ }
644
665
  }
645
666
 
646
667
  BlockletState.BlockletStatus = BlockletStatus;
@@ -41,6 +41,13 @@ const {
41
41
  APP_STRUCT_VERSION,
42
42
  EXPIRED_BLOCKLET_DATA_RETENTION_DAYS,
43
43
  } = require('@abtnode/constant');
44
+ const {
45
+ parseComponents,
46
+ ensureMeta,
47
+ filterDuplicateComponents,
48
+ validateBlockletMeta,
49
+ getComponentConfig,
50
+ } = require('@blocklet/resolver');
44
51
  const formatBackSlash = require('@abtnode/util/lib/format-back-slash');
45
52
  const { toSvg: createDidLogo } =
46
53
  process.env.NODE_ENV !== 'test' ? require('@arcblock/did-motif') : require('@arcblock/did-motif/dist/did-motif.cjs');
@@ -67,7 +74,6 @@ const validateBlockletEntry = require('@blocklet/meta/lib/entry');
67
74
  const getBlockletEngine = require('@blocklet/meta/lib/engine');
68
75
  const getBlockletInfo = require('@blocklet/meta/lib/info');
69
76
  const getBlockletWallet = require('@blocklet/meta/lib/wallet');
70
- const { validateMeta, fixAndValidateService } = require('@blocklet/meta/lib/validate');
71
77
  const {
72
78
  forEachBlocklet,
73
79
  getDisplayName,
@@ -75,7 +81,6 @@ const {
75
81
  forEachBlockletSync,
76
82
  forEachChildSync,
77
83
  forEachComponentV2Sync,
78
- isComponentBlocklet,
79
84
  getSharedConfigObj,
80
85
  getComponentName,
81
86
  isEnvShareable,
@@ -85,13 +90,8 @@ const {
85
90
  isInProgress,
86
91
  isRunning,
87
92
  } = require('@blocklet/meta/lib/util');
88
- const toBlockletDid = require('@blocklet/meta/lib/did');
89
93
  const { titleSchema, descriptionSchema, logoSchema } = require('@blocklet/meta/lib/schema');
90
- const {
91
- getSourceUrlsFromConfig,
92
- getBlockletMetaFromUrls,
93
- getBlockletMetaFromUrl,
94
- } = require('@blocklet/meta/lib/util-meta');
94
+ const { getBlockletMetaFromUrl } = require('@blocklet/meta/lib/util-meta');
95
95
  const getComponentProcessId = require('@blocklet/meta/lib/get-component-process-id');
96
96
 
97
97
  const { validate: validateEngine, get: getEngine } = require('../blocklet/manager/engine');
@@ -103,12 +103,6 @@ const externalIpUtil = require('./get-accessible-external-node-ip');
103
103
  const { getIpDnsDomainForBlocklet } = require('./get-domain-for-blocklet');
104
104
  const { replaceDomainSlot } = require('./index');
105
105
 
106
- const getComponentConfig = (meta) => {
107
- const components = meta.components || meta.children || [];
108
- const staticComponents = (meta.staticComponents || []).map((x) => ({ ...x, static: true }));
109
- return [...components, ...staticComponents];
110
- };
111
-
112
106
  /**
113
107
  * get blocklet engine info, default is node
114
108
  * @param {object} blockletMeta blocklet meta
@@ -469,11 +463,11 @@ const isUsefulError = (err) =>
469
463
  !/^Process \d+ not found$/.test(err.message);
470
464
 
471
465
  const getHealthyCheckTimeout = (blocklet, { checkHealthImmediately } = {}) => {
472
- let minConsecutiveTime = 5000;
466
+ let minConsecutiveTime = 2000;
473
467
  if (process.env.NODE_ENV === 'test' && process.env.ABT_NODE_TEST_MIN_CONSECUTIVE_TIME !== undefined) {
474
468
  minConsecutiveTime = process.env.ABT_NODE_TEST_MIN_CONSECUTIVE_TIME;
475
469
  } else if (checkHealthImmediately) {
476
- minConsecutiveTime = 3000;
470
+ minConsecutiveTime = 1000;
477
471
  }
478
472
 
479
473
  if (process.env.BLOCKLET_START_TIMEOUT) {
@@ -540,6 +534,7 @@ const startBlockletProcess = async (
540
534
 
541
535
  // get env
542
536
  const env = getRuntimeEnvironments(b, nodeEnvironments, ancestors);
537
+ const startedAt = Date.now();
543
538
 
544
539
  // run hook
545
540
  await preStart(b, { env });
@@ -564,7 +559,7 @@ const startBlockletProcess = async (
564
559
  output: path.join(logsDir, 'output.log'),
565
560
  error: path.join(logsDir, 'error.log'),
566
561
  wait_ready: process.env.NODE_ENV !== 'test',
567
- listen_timeout: 5000,
562
+ listen_timeout: 3000,
568
563
  max_memory_restart: `${maxMemoryRestart}M`,
569
564
  max_restarts: b.mode === BLOCKLET_MODES.DEVELOPMENT ? 0 : 3,
570
565
  env: {
@@ -618,9 +613,9 @@ const startBlockletProcess = async (
618
613
 
619
614
  const status = await getProcessState(processId);
620
615
  if (status === BlockletStatus.error) {
621
- throw new Error(`${processId} is not running within 5 seconds`);
616
+ throw new Error(`${processId} is not running within 3 seconds`);
622
617
  }
623
- logger.info('blocklet started', { processId, status });
618
+ logger.info('blocklet started', { processId, status, time: Date.now() - startedAt });
624
619
 
625
620
  // run hook
626
621
  postStart(b, { env }).catch((err) => {
@@ -736,128 +731,6 @@ const reloadProcess = (processId) =>
736
731
  });
737
732
  });
738
733
 
739
- /**
740
- * this function has side effect to component (will set component.children: Array<staticComponent> )
741
- * this function has side effect to dynamicComponents (will push dynamicComponent in dynamicComponents)
742
- *
743
- * @param {Component} component
744
- * @param {{
745
- * ancestors: Array<{meta}>
746
- * dynamicComponents: Array<{Component}>
747
- * }} context
748
- * @returns {{
749
- * dynamicComponents: Array<Component>,
750
- * staticComponents: Array<Component>,
751
- * }}
752
- */
753
- const parseComponents = async (component, context = {}) => {
754
- const { ancestors = [], dynamicComponents = [] } = context;
755
- if (ancestors.length > 40) {
756
- throw new Error('The depth of component should not exceed 40');
757
- }
758
-
759
- const configs = getComponentConfig(component.meta) || [];
760
-
761
- if (!configs || !configs.length) {
762
- return {
763
- staticComponents: [],
764
- dynamicComponents,
765
- };
766
- }
767
-
768
- const staticComponents = [];
769
-
770
- // FIXME @linchen 改成并行获取
771
- for (const config of configs) {
772
- const isStatic = !!config.static;
773
-
774
- if (isStatic) {
775
- if (!config.name) {
776
- throw new Error(`Name does not found in child ${config.name}`);
777
- }
778
-
779
- if (!config.mountPoint) {
780
- throw new Error(`MountPoint does not found in child ${config.name}`);
781
- }
782
- }
783
-
784
- const urls = getSourceUrlsFromConfig(config);
785
-
786
- let rawMeta;
787
- try {
788
- rawMeta = await getBlockletMetaFromUrls(urls, { logger });
789
- } catch (error) {
790
- throw new Error(`Failed get component meta. Component: ${urls.join(', ')}, reason: ${error.message}`);
791
- }
792
-
793
- if (rawMeta.group === BlockletGroup.gateway) {
794
- throw new Error(`Cannot add gateway component ${rawMeta.title || rawMeta.did}`);
795
- }
796
-
797
- validateBlockletMeta(rawMeta, { ensureDist: true });
798
-
799
- if (!isComponentBlocklet(rawMeta)) {
800
- throw new Error(
801
- `The blocklet cannot be a component: ${rawMeta.title}. The reason may be that the developer set capabilities.component to false in blocklet.yml`
802
- );
803
- }
804
-
805
- const webInterface = findWebInterface(rawMeta);
806
- if (!webInterface) {
807
- throw new Error(`Web interface does not found in component ${rawMeta.title || rawMeta.name}`);
808
- }
809
-
810
- const meta = ensureMeta(rawMeta, { name: isStatic ? config.name : null });
811
-
812
- // check circular dependencies
813
- if (ancestors.map((x) => x.meta?.bundleDid).indexOf(meta.bundleDid) > -1) {
814
- throw new Error('Blocklet components have circular dependencies');
815
- }
816
-
817
- if (config.title) {
818
- meta.title = config.title;
819
- meta.title = await titleSchema.validateAsync(config.title);
820
- }
821
-
822
- if (config.description) {
823
- meta.description = await descriptionSchema.validateAsync(config.description);
824
- }
825
-
826
- const mountPoint = isStatic ? config.mountPoint : config.mountPoint || `/${meta.did}`;
827
-
828
- const child = {
829
- mountPoint,
830
- meta,
831
- bundleSource: config.source,
832
- dependencies: [],
833
- };
834
-
835
- if (isStatic) {
836
- staticComponents.push(child);
837
- } else {
838
- dynamicComponents.push(child);
839
- component.dependencies = component.dependencies || [];
840
- component.dependencies.push({
841
- did: child.meta.bundleDid,
842
- required: !!config.required,
843
- version: config.source.version || 'latest',
844
- });
845
- }
846
-
847
- await parseComponents(child, {
848
- ancestors: [...ancestors, { meta }],
849
- dynamicComponents,
850
- });
851
- }
852
-
853
- component.children = staticComponents;
854
-
855
- return {
856
- staticComponents,
857
- dynamicComponents,
858
- };
859
- };
860
-
861
734
  const validateBlocklet = (blocklet) =>
862
735
  forEachBlocklet(blocklet, (b) => {
863
736
  isRequirementsSatisfied(b.meta.requirements);
@@ -894,7 +767,9 @@ const checkBlockletProcessHealthy = async (blocklet, { minConsecutiveTime, timeo
894
767
 
895
768
  const logToTerminal = [blocklet.mode, b.mode].includes(BLOCKLET_MODES.DEVELOPMENT);
896
769
 
770
+ const startedAt = Date.now();
897
771
  await _checkProcessHealthy(b, { minConsecutiveTime, timeout, logToTerminal });
772
+ logger.info('component healthy', { did: b.meta.did, time: Date.now() - startedAt });
898
773
  },
899
774
  { parallel: true }
900
775
  );
@@ -923,10 +798,9 @@ const _checkProcessHealthy = async (blocklet, { minConsecutiveTime, timeout, log
923
798
  }
924
799
  };
925
800
 
926
- await sleep(400);
927
801
  let status = await getStatus();
928
- for (let i = 0; i < 10 && status !== 'online'; i++) {
929
- const t = process.env.NODE_ENV !== 'test' ? 1000 : 30;
802
+ for (let i = 0; i < 20 && status !== 'online'; i++) {
803
+ const t = process.env.NODE_ENV !== 'test' ? 500 : 30;
930
804
  await sleep(t);
931
805
  status = await getStatus();
932
806
  }
@@ -945,11 +819,7 @@ const _checkProcessHealthy = async (blocklet, { minConsecutiveTime, timeout, log
945
819
  );
946
820
  }
947
821
  try {
948
- await ensureEndpointHealthy({
949
- port,
950
- minConsecutiveTime,
951
- timeout,
952
- });
822
+ await ensureEndpointHealthy({ port, minConsecutiveTime, timeout });
953
823
  } catch (error) {
954
824
  logger.error('ensure endpoint healthy failed', { port, minConsecutiveTime, timeout });
955
825
  throw error;
@@ -1327,45 +1197,6 @@ const needBlockletDownload = (blocklet, oldBlocklet) => {
1327
1197
  return get(oldBlocklet, 'meta.dist.integrity') !== get(blocklet, 'meta.dist.integrity');
1328
1198
  };
1329
1199
 
1330
- const isDidMatchName = (did, name) => {
1331
- if (isValidDid(did) && name === did) {
1332
- return true;
1333
- }
1334
-
1335
- return toBlockletDid(name) === did;
1336
- };
1337
-
1338
- /**
1339
- * set bundleDid and bundleMeta in meta
1340
- * update name and did to server index
1341
- * in app structure 2.0, application's bundleDid, bundleName, meta, did will be same
1342
- */
1343
- const ensureMeta = (meta, { name, did } = {}) => {
1344
- if (name && did && !isDidMatchName(did, name)) {
1345
- throw new Error(`name does not match with did: ${name}, ${did}`);
1346
- }
1347
-
1348
- const newMeta = {
1349
- ...meta,
1350
- };
1351
-
1352
- if (!newMeta.did || !newMeta.name || !isDidMatchName(newMeta.did, newMeta.name)) {
1353
- throw new Error(`name does not match with did in meta: ${newMeta.name}, ${newMeta.did}`);
1354
- }
1355
-
1356
- if (!meta.bundleDid) {
1357
- newMeta.bundleDid = meta.did;
1358
- newMeta.bundleName = meta.name;
1359
- }
1360
-
1361
- if (name) {
1362
- newMeta.name = name;
1363
- newMeta.did = did || toBlockletDid(name);
1364
- }
1365
-
1366
- return newMeta;
1367
- };
1368
-
1369
1200
  const getBlocklet = async ({
1370
1201
  did,
1371
1202
  dataDirs,
@@ -1784,36 +1615,6 @@ const checkVersionCompatibility = (components) => {
1784
1615
  }
1785
1616
  };
1786
1617
 
1787
- const filterDuplicateComponents = (components = [], currents = []) => {
1788
- const arr = [];
1789
-
1790
- components.forEach((component) => {
1791
- if (currents.some((x) => x.meta.did === component.meta.did)) {
1792
- return;
1793
- }
1794
-
1795
- const index = arr.findIndex((x) => x.meta.did === component.meta.did);
1796
- if (index > -1) {
1797
- const exist = arr[index];
1798
-
1799
- // 选择最小版本:
1800
- // 如果 com1 和 com2 都依赖某个 component, com1 声明依赖版本 1.0.0, 实际获取版本 1.0.0; com2 声明依赖版本 latest(不限), 实际获取版本 2.0.0 -> 则应该取 1.0.0
1801
- if (semver.lt(component.meta.version, exist.meta.version)) {
1802
- arr.splice(index, 1, component);
1803
- }
1804
- } else {
1805
- arr.push(component);
1806
- }
1807
- });
1808
-
1809
- return arr;
1810
- };
1811
-
1812
- const validateBlockletMeta = (meta, opts = {}) => {
1813
- fixAndValidateService(meta);
1814
- return validateMeta(meta, { ensureName: true, skipValidateDidName: true, schemaOptions: { ...opts } });
1815
- };
1816
-
1817
1618
  const getBlockletKnownAs = (blocklet) => {
1818
1619
  const alsoKnownAs = [blocklet.appDid];
1819
1620
  if (Array.isArray(blocklet.migratedFrom)) {
@@ -181,7 +181,7 @@ const setupAppOwner = async (node, sessionId) => {
181
181
  passport,
182
182
  role,
183
183
  secret: appSecret,
184
- expiresIn: '7d',
184
+ expiresIn: '1d',
185
185
  });
186
186
 
187
187
  await node.setBlockletOwner({ did: appDid, owner: { did: ownerDid, pk: ownerPk } });
@@ -17,19 +17,21 @@ const domainMessages = {
17
17
  },
18
18
  };
19
19
 
20
+ const getPrefixSchema = (field) =>
21
+ Joi.string()
22
+ .trim()
23
+ .max(150)
24
+ .messages({
25
+ zh: { 'string.empty': `${field} 不能为空`, 'string.max': `${field} 的最大长度是 150` },
26
+ en: { 'string.empty': `${field} cannot be empty`, 'string.max': `The maximum length of ${field} is 150` },
27
+ })
28
+ .custom((value) => urlPathFriendly(value));
29
+
20
30
  const ruleSchema = {
21
31
  isProtected: Joi.boolean(),
22
32
 
23
33
  from: Joi.object({
24
- pathPrefix: Joi.string()
25
- .required()
26
- .trim()
27
- .max(150)
28
- .messages({
29
- zh: { 'string.empty': 'URL 前缀不能为空', 'string.max': 'URL 前缀的最大长度是 150' },
30
- en: { 'string.empty': 'URL prefix cannot be empty', 'string.max': 'The maximum length of URL prefix is 150' },
31
- })
32
- .custom((value) => urlPathFriendly(value)),
34
+ pathPrefix: getPrefixSchema('from.pathPrefix').required(),
33
35
  groupPathPrefix: Joi.string().trim().min(1).max(150), // path prefix of interface of root blocklet
34
36
  header: Joi.any(), // TODO: header does not take effect
35
37
  }),
@@ -44,12 +46,24 @@ const ruleSchema = {
44
46
  ROUTING_RULE_TYPES.REDIRECT,
45
47
  ROUTING_RULE_TYPES.GENERAL_PROXY,
46
48
  ROUTING_RULE_TYPES.DIRECT_RESPONSE,
49
+ ROUTING_RULE_TYPES.GENERAL_REWRITE,
50
+ ROUTING_RULE_TYPES.COMPONENT,
47
51
  ROUTING_RULE_TYPES.NONE
48
52
  )
49
53
  .required(),
50
- did: Joi.string().label('did').when('type', { is: ROUTING_RULE_TYPES.BLOCKLET, then: Joi.required() }), // root blocklet did
54
+ did: Joi.string()
55
+ .label('did')
56
+ .when('type', {
57
+ is: Joi.string().valid(ROUTING_RULE_TYPES.BLOCKLET, ROUTING_RULE_TYPES.COMPONENT),
58
+ then: Joi.required(),
59
+ }), // root blocklet did
51
60
  port: Joi.number().label('port').port().when('type', { is: ROUTING_RULE_TYPES.BLOCKLET, then: Joi.required() }),
52
- url: Joi.string().label('url').when('type', { is: ROUTING_RULE_TYPES.REDIRECT, then: Joi.required() }),
61
+ url: Joi.string()
62
+ .label('url')
63
+ .when('type', {
64
+ is: Joi.string().valid(ROUTING_RULE_TYPES.REDIRECT, ROUTING_RULE_TYPES.GENERAL_REWRITE),
65
+ then: Joi.required(),
66
+ }),
53
67
  redirectCode: Joi.alternatives()
54
68
  .try(301, 302, 307, 308)
55
69
  .label('redirect code')
@@ -70,7 +84,8 @@ const ruleSchema = {
70
84
  .valid(...Object.keys(ROUTER_CACHE_GROUPS))
71
85
  .allow('')
72
86
  .default(''),
73
- targetPrefix: Joi.string().trim().max(150), // path prefix of interface of target blocklet
87
+ targetPrefix: getPrefixSchema('to.targetPrefix'), // path prefix of interface of target blocklet
88
+ pageGroup: Joi.string().allow('').default(''),
74
89
  },
75
90
 
76
91
  // List of services that manipulate the request before the upstream blocklet
@@ -22,8 +22,20 @@ const blockletController = Joi.object({
22
22
 
23
23
  const createValidator = (schema) => (entity) => schema.validateAsync(entity);
24
24
 
25
+ const sessionConfigSchema = Joi.object({
26
+ cacheTtl: Joi.number()
27
+ .min(5 * 60) // 5min
28
+ .max(86400) // 1d
29
+ .default(10 * 60), // 10min
30
+ ttl: Joi.number()
31
+ .min(86400) // 1d
32
+ .max(86400 * 30) // 30d
33
+ .default(86400 * 7), // 7d
34
+ });
35
+
25
36
  module.exports = {
26
37
  createValidator,
27
38
  getMultipleLangParams,
28
39
  blockletController,
40
+ sessionConfigSchema,
29
41
  };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.16.13-beta-55b3e93d",
6
+ "version": "1.16.13-beta-423a40b1",
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": "Apache-2.0",
21
21
  "dependencies": {
22
- "@abtnode/auth": "1.16.13-beta-55b3e93d",
23
- "@abtnode/certificate-manager": "1.16.13-beta-55b3e93d",
24
- "@abtnode/constant": "1.16.13-beta-55b3e93d",
25
- "@abtnode/cron": "1.16.13-beta-55b3e93d",
26
- "@abtnode/logger": "1.16.13-beta-55b3e93d",
27
- "@abtnode/models": "1.16.13-beta-55b3e93d",
28
- "@abtnode/queue": "1.16.13-beta-55b3e93d",
29
- "@abtnode/rbac": "1.16.13-beta-55b3e93d",
30
- "@abtnode/router-provider": "1.16.13-beta-55b3e93d",
31
- "@abtnode/static-server": "1.16.13-beta-55b3e93d",
32
- "@abtnode/timemachine": "1.16.13-beta-55b3e93d",
33
- "@abtnode/util": "1.16.13-beta-55b3e93d",
22
+ "@abtnode/auth": "1.16.13-beta-423a40b1",
23
+ "@abtnode/certificate-manager": "1.16.13-beta-423a40b1",
24
+ "@abtnode/constant": "1.16.13-beta-423a40b1",
25
+ "@abtnode/cron": "1.16.13-beta-423a40b1",
26
+ "@abtnode/logger": "1.16.13-beta-423a40b1",
27
+ "@abtnode/models": "1.16.13-beta-423a40b1",
28
+ "@abtnode/queue": "1.16.13-beta-423a40b1",
29
+ "@abtnode/rbac": "1.16.13-beta-423a40b1",
30
+ "@abtnode/router-provider": "1.16.13-beta-423a40b1",
31
+ "@abtnode/static-server": "1.16.13-beta-423a40b1",
32
+ "@abtnode/timemachine": "1.16.13-beta-423a40b1",
33
+ "@abtnode/util": "1.16.13-beta-423a40b1",
34
34
  "@arcblock/did": "1.18.84",
35
35
  "@arcblock/did-auth": "1.18.84",
36
36
  "@arcblock/did-ext": "^1.18.84",
37
- "@arcblock/did-motif": "^1.1.10",
37
+ "@arcblock/did-motif": "^1.1.11",
38
38
  "@arcblock/did-util": "1.18.84",
39
39
  "@arcblock/event-hub": "1.18.84",
40
40
  "@arcblock/jwt": "^1.18.84",
41
41
  "@arcblock/pm2-events": "^0.0.5",
42
42
  "@arcblock/validator": "^1.18.84",
43
43
  "@arcblock/vc": "1.18.84",
44
- "@blocklet/constant": "1.16.13-beta-55b3e93d",
45
- "@blocklet/meta": "1.16.13-beta-55b3e93d",
46
- "@blocklet/sdk": "1.16.13-beta-55b3e93d",
44
+ "@blocklet/constant": "1.16.13-beta-423a40b1",
45
+ "@blocklet/meta": "1.16.13-beta-423a40b1",
46
+ "@blocklet/resolver": "1.16.13-beta-423a40b1",
47
+ "@blocklet/sdk": "1.16.13-beta-423a40b1",
47
48
  "@did-space/client": "^0.2.117",
48
49
  "@fidm/x509": "^1.2.1",
49
50
  "@ocap/mcrypto": "1.18.84",
@@ -96,5 +97,5 @@
96
97
  "express": "^4.18.2",
97
98
  "jest": "^27.5.1"
98
99
  },
99
- "gitHead": "1c734de99ff70fad99e9cc3fb55834e0bb6f5bfd"
100
+ "gitHead": "1aee04f45042bd4784ca72f9f8b93918980be4d4"
100
101
  }