@abtnode/core 1.7.7 → 1.7.10

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
@@ -104,6 +104,19 @@ class TeamAPI extends EventEmitter {
104
104
  return state.getUser(user.did);
105
105
  }
106
106
 
107
+ async getOwner({ teamDid }) {
108
+ const owner = await this.teamManager.getOwner(teamDid);
109
+
110
+ if (!owner) {
111
+ return null;
112
+ }
113
+
114
+ const state = await this.getUserState(teamDid);
115
+
116
+ const full = await state.getUser(owner.did);
117
+ return full || owner;
118
+ }
119
+
107
120
  async updateUser({ teamDid, user }) {
108
121
  const state = await this.getUserState(teamDid);
109
122
 
@@ -211,7 +224,7 @@ class TeamAPI extends EventEmitter {
211
224
 
212
225
  // Invite member
213
226
 
214
- async createInvitation({ teamDid, role, remark, interfaceName }, context) {
227
+ async createInvitation({ teamDid, role, remark }, context) {
215
228
  await this.teamManager.checkEnablePassportIssuance(teamDid);
216
229
 
217
230
  if (!role) {
@@ -242,7 +255,6 @@ class TeamAPI extends EventEmitter {
242
255
  expireDate,
243
256
  inviter: user,
244
257
  teamDid,
245
- interfaceName,
246
258
  });
247
259
 
248
260
  logger.info('Create invite member', { role, user, inviteId });
@@ -254,7 +266,6 @@ class TeamAPI extends EventEmitter {
254
266
  expireDate: new Date(expireDate).toString(),
255
267
  inviter: user,
256
268
  teamDid,
257
- interfaceName,
258
269
  };
259
270
  }
260
271
 
@@ -271,7 +282,6 @@ class TeamAPI extends EventEmitter {
271
282
  expireDate: new Date(d.expireDate).toString(),
272
283
  inviter: d.inviter,
273
284
  teamDid: d.teamDid,
274
- interfaceName: d.interfaceName,
275
285
  }));
276
286
  }
277
287
 
@@ -104,6 +104,7 @@ const blockletPm2Events = require('./pm2-events');
104
104
  const { getFactoryState } = require('../../util/chain');
105
105
  const runMigrationScripts = require('../migration');
106
106
  const hooks = require('../hooks');
107
+ const { formatName } = require('../../util/get-domain-for-blocklet');
107
108
 
108
109
  const {
109
110
  isInProgress,
@@ -113,6 +114,7 @@ const {
113
114
  shouldUpdateBlockletStatus,
114
115
  getBlockletMeta,
115
116
  validateBlockletMeta,
117
+ validateOwner,
116
118
  } = util;
117
119
 
118
120
  const preDownloadLock = new Lock('pre-download-lock');
@@ -255,7 +257,7 @@ class BlockletManager extends BaseBlockletManager {
255
257
  }
256
258
 
257
259
  const blocklet = await states.blocklet.getBlocklet(meta.did);
258
- const isRunning = blocklet?.status === BlockletStatus.running;
260
+ const isRunning = blocklet ? blocklet.status === BlockletStatus.running : false;
259
261
 
260
262
  return { meta, isFree, isInstalled: !!blocklet, isRunning };
261
263
  }
@@ -279,9 +281,9 @@ class BlockletManager extends BaseBlockletManager {
279
281
  return this._installFromStore({ did, registry }, { ...context, blockletPurchaseVerified: true });
280
282
  }
281
283
 
282
- async start({ did, checkHealthImmediately = false, throwOnError }, context) {
284
+ async start({ did, throwOnError, checkHealthImmediately = false, e2eMode = false }, context) {
283
285
  logger.info('start blocklet', { did });
284
- const blocklet = await this.ensureBlocklet(did);
286
+ const blocklet = await this.ensureBlocklet(did, e2eMode);
285
287
 
286
288
  try {
287
289
  if (!hasRunnableComponent(blocklet)) {
@@ -319,6 +321,7 @@ class BlockletManager extends BaseBlockletManager {
319
321
  }),
320
322
  nodeEnvironments,
321
323
  nodeInfo: await states.node.read(),
324
+ e2eMode,
322
325
  });
323
326
 
324
327
  // check blocklet healthy
@@ -481,6 +484,40 @@ class BlockletManager extends BaseBlockletManager {
481
484
  }
482
485
  }
483
486
 
487
+ async reset({ did }, context = {}) {
488
+ logger.info('reset blocklet', { did });
489
+
490
+ const blocklet = await this.ensureBlocklet(did);
491
+
492
+ if (isInProgress(blocklet.status)) {
493
+ throw new Error('Cannot reset when blocklet is in progress');
494
+ }
495
+
496
+ try {
497
+ await this.deleteProcess({ did }, context);
498
+ } catch {
499
+ // do nothing
500
+ }
501
+
502
+ // Cleanup disk storage
503
+ const { name } = blocklet.meta;
504
+ const dataDir = path.join(this.dataDirs.data, name);
505
+ const logsDir = path.join(this.dataDirs.logs, name);
506
+ const cacheDir = path.join(this.dataDirs.cache, name);
507
+ fs.removeSync(cacheDir);
508
+ fs.removeSync(dataDir);
509
+ fs.removeSync(logsDir);
510
+
511
+ // Reset config in db
512
+ await this._delExtras(did);
513
+ await this._setConfigs(did);
514
+ await this.updateBlockletEnvironment(did);
515
+ await this.resetSiteByDid(did, context);
516
+
517
+ logger.info('blocklet reset', { did });
518
+ return blocklet;
519
+ }
520
+
484
521
  async deleteComponent({ did, rootDid }, context) {
485
522
  logger.info('delete blocklet component', { did, rootDid });
486
523
 
@@ -590,7 +627,7 @@ class BlockletManager extends BaseBlockletManager {
590
627
  }
591
628
 
592
629
  // eslint-disable-next-line no-unused-vars
593
- async config({ did, childDid, configs: newConfigs }, context) {
630
+ async config({ did, childDid, configs: newConfigs, skipHook }, context) {
594
631
  logger.info('config blocklet', { did });
595
632
  if (!Array.isArray(newConfigs)) {
596
633
  throw new Error('configs list is not an array');
@@ -657,15 +694,17 @@ class BlockletManager extends BaseBlockletManager {
657
694
  blocklet.configObj[x.key] = x.value;
658
695
  }
659
696
 
660
- // FIXME: we should also call preConfig for child blocklets
661
- await hooks.preConfig(blocklet.env.appId, {
662
- appDir: blocklet.env.appDir,
663
- hooks: Object.assign(blocklet.meta.hooks || {}, blocklet.meta.scripts || {}),
664
- exitOnError: true,
665
- env: { ...getRuntimeEnvironments(blocklet, nodeEnvironments), ...blocklet.configObj },
666
- did,
667
- context,
668
- });
697
+ if (!skipHook) {
698
+ // FIXME: we should also call preConfig for child blocklets
699
+ await hooks.preConfig(blocklet.env.appId, {
700
+ appDir: blocklet.env.appDir,
701
+ hooks: Object.assign(blocklet.meta.hooks || {}, blocklet.meta.scripts || {}),
702
+ exitOnError: true,
703
+ env: { ...getRuntimeEnvironments(blocklet, nodeEnvironments), ...blocklet.configObj },
704
+ did,
705
+ context,
706
+ });
707
+ }
669
708
 
670
709
  // update db
671
710
  const configs = childDid
@@ -928,14 +967,22 @@ class BlockletManager extends BaseBlockletManager {
928
967
  * After the dev function finished, the caller should send a BlockletEvents.deployed event to the daemon
929
968
  * @returns {Object} blocklet
930
969
  */
931
- async dev(folder) {
932
- logger.info('dev blocklet', { folder });
970
+ async dev(folder, { rootDid, mountPoint } = {}) {
971
+ logger.info('dev component', { folder, rootDid, mountPoint });
933
972
 
934
973
  const meta = getBlockletMeta(folder);
935
974
  if (meta.group !== 'static' && (!meta.scripts || !meta.scripts.dev)) {
936
975
  throw new Error('Incorrect blocklet manifest: missing `scripts.dev` field');
937
976
  }
938
977
 
978
+ if (rootDid) {
979
+ return this._devComponent({ folder, meta, rootDid, mountPoint });
980
+ }
981
+
982
+ return this._devBlocklet({ folder, meta });
983
+ }
984
+
985
+ async _devBlocklet({ folder, meta }) {
939
986
  const { did, version } = meta;
940
987
 
941
988
  const exist = await states.blocklet.getBlocklet(did);
@@ -990,7 +1037,58 @@ class BlockletManager extends BaseBlockletManager {
990
1037
  return blocklet;
991
1038
  }
992
1039
 
993
- async ensureBlocklet(did) {
1040
+ async _devComponent({ folder, meta, rootDid, mountPoint }) {
1041
+ const { did, version } = meta;
1042
+
1043
+ const existRoot = await states.blocklet.getBlocklet(rootDid);
1044
+ if (!existRoot) {
1045
+ throw new Error('Root blocklet does not exist');
1046
+ }
1047
+
1048
+ const exist = existRoot.children.find((x) => x.meta.did === meta.did);
1049
+ if (exist) {
1050
+ if (exist.mode === BLOCKLET_MODES.PRODUCTION) {
1051
+ throw new Error('The blocklet component of production mode already exists, please remove it before developing');
1052
+ }
1053
+
1054
+ const status = fromBlockletStatus(exist.status);
1055
+ if (['starting', 'running'].includes(status)) {
1056
+ throw new Error(`The blocklet component is already on ${status}, please stop it before developing`);
1057
+ }
1058
+
1059
+ logger.info('remove blocklet component for dev', { did, version });
1060
+
1061
+ await this.deleteComponent({ did, rootDid });
1062
+ }
1063
+
1064
+ const defaultPath = formatName(meta.name);
1065
+ const children = await parseChildren(existRoot.meta, {
1066
+ children: [
1067
+ {
1068
+ meta,
1069
+ mountPoint: mountPoint || `/${defaultPath}`,
1070
+ source: BlockletSource.local,
1071
+ deployedFrom: folder,
1072
+ status: BlockletStatus.installed,
1073
+ mode: BLOCKLET_MODES.DEVELOPMENT,
1074
+ },
1075
+ ],
1076
+ dynamic: true,
1077
+ });
1078
+ await states.blocklet.addChildren(rootDid, children);
1079
+
1080
+ logger.info('add blocklet component for dev', { did, version, meta });
1081
+
1082
+ // Add environments
1083
+ await this._setConfigs(rootDid);
1084
+ await this.updateBlockletEnvironment(rootDid);
1085
+
1086
+ const rootBlocklet = await this.ensureBlocklet(rootDid);
1087
+
1088
+ return rootBlocklet;
1089
+ }
1090
+
1091
+ async ensureBlocklet(did, e2eMode = false) {
994
1092
  if (!isValidDid(did)) {
995
1093
  throw new Error(`Blocklet did is invalid: ${did}`);
996
1094
  }
@@ -1008,6 +1106,7 @@ class BlockletManager extends BaseBlockletManager {
1008
1106
  ...getBlockletDirs(blocklet, {
1009
1107
  dataDirs: this.dataDirs,
1010
1108
  ensure: true,
1109
+ e2eMode,
1011
1110
  }),
1012
1111
  };
1013
1112
 
@@ -1051,9 +1150,14 @@ class BlockletManager extends BaseBlockletManager {
1051
1150
  return blocklet;
1052
1151
  }
1053
1152
 
1054
- async setInitialized({ did }) {
1153
+ async setInitialized({ did, owner }) {
1154
+ if (!validateOwner(owner)) {
1155
+ throw new Error('Blocklet owner is invalid');
1156
+ }
1157
+
1055
1158
  const blocklet = await states.blocklet.getBlocklet(did);
1056
- await states.blockletExtras.setSettings(blocklet.meta.did, { initialized: true });
1159
+ await states.blockletExtras.setSettings(blocklet.meta.did, { initialized: true, owner });
1160
+ logger.info('Blocklet initialized', { did, owner });
1057
1161
 
1058
1162
  this.emit(BlockletEvents.updated, { meta: { did: blocklet.meta.did } });
1059
1163
 
@@ -1736,7 +1840,7 @@ class BlockletManager extends BaseBlockletManager {
1736
1840
  meta,
1737
1841
  mountPoint,
1738
1842
  source: BlockletSource.upload,
1739
- deployedFrom: `Upload by ${context.user.did}`,
1843
+ deployedFrom: `Upload by ${context.user.fullName}`,
1740
1844
  sourceUrl: '',
1741
1845
  dynamic: true,
1742
1846
  };
@@ -21,6 +21,7 @@ async function runScripts({
21
21
  printSuccess,
22
22
  printError,
23
23
  }) {
24
+ // 获取所有待执行的脚本
24
25
  let scripts = [];
25
26
  try {
26
27
  scripts = await getScripts(scriptsDir);
@@ -37,13 +38,17 @@ async function runScripts({
37
38
  }
38
39
 
39
40
  printInfo('pending scripts', pendingScripts);
41
+ // 执行脚本前需要对数据库文件做一次备份
40
42
  try {
41
43
  await doBackup({ dbDir, backupDir, printInfo, printSuccess, printError });
42
44
  } catch (err) {
43
45
  printError(`Failed to backup state db due to ${err.message}, abort!`);
46
+ // 备份失败时,需要移除备份目录
47
+ fs.removeSync(backupDir);
44
48
  throw err;
45
49
  }
46
50
 
51
+ // 按照顺序依次执行 migration 脚本
47
52
  for (let i = 0; i < pendingScripts.length; i++) {
48
53
  const { script: scriptPath } = pendingScripts[i];
49
54
  try {
@@ -59,7 +64,10 @@ async function runScripts({
59
64
 
60
65
  try {
61
66
  await doRestore({ dbDir, backupDir, printInfo, printSuccess, printError });
67
+ // 恢复备份成功时,需要移除备份目录
68
+ fs.removeSync(backupDir);
62
69
  } catch (restoreErr) {
70
+ // 如果恢复备份失败,则保留备份目录,恢复数据
63
71
  printError(`Failed to restore state db due to: ${restoreErr.message}`);
64
72
  }
65
73
 
package/lib/cert.js CHANGED
@@ -37,6 +37,16 @@ const onCertIssued = (cert) => {
37
37
  });
38
38
  };
39
39
 
40
+ const onCertIssueFailed = (cert) => {
41
+ states.notification.create({
42
+ title: 'Certificate Issue Failed',
43
+ description: `Failed to issue certificate for ${cert.domain}`,
44
+ severity: 'error',
45
+ entityType: 'certificate',
46
+ entityId: cert.id,
47
+ });
48
+ };
49
+
40
50
  const getDomainFromInput = (input) => {
41
51
  if (Object.prototype.toString.call(input) === '[object Object]') {
42
52
  return input.domain;
@@ -58,6 +68,10 @@ class Cert extends EventEmitter {
58
68
 
59
69
  this.manager.on('cert.expired', onCertExpired);
60
70
  this.manager.on('cert.about_to_expire', onCertAboutExpire);
71
+ this.manager.on('cert.error', (cert) => {
72
+ this.emit('cert.error', cert);
73
+ onCertIssueFailed(cert);
74
+ });
61
75
  }
62
76
 
63
77
  start() {
package/lib/event.js CHANGED
@@ -25,11 +25,14 @@ module.exports = ({
25
25
  handleRouting,
26
26
  domainStatus,
27
27
  teamAPI,
28
+ certManager,
28
29
  }) => {
29
30
  const notificationState = states.notification;
30
31
  const nodeState = states.node;
31
32
 
32
33
  const events = new EventEmitter();
34
+ events.setMaxListeners(0);
35
+
33
36
  // HACK: do not emit any events from CLI
34
37
  if (isCLI() && process.env.NODE_ENV !== 'test') {
35
38
  events.emit = (name) => logger.debug('stopped core state event in CLI', name);
@@ -123,6 +126,14 @@ module.exports = ({
123
126
  await teamAPI.refreshBlockletInterfacePermissions(blocklet.meta);
124
127
  } catch (error) {
125
128
  logger.error('upgrade blocklet routing rules error', { event: name, error });
129
+ notificationState.create({
130
+ title: 'Blocklet URL Mapping Error',
131
+ // eslint-disable-next-line max-len
132
+ description: `Failed to upgrade URL mapping for blocklet ${blocklet.meta.name}@${blocklet.meta.version}, due to: ${error.message}`,
133
+ entityType: 'blocklet',
134
+ entityId: blocklet.meta.did,
135
+ severity: 'error',
136
+ });
126
137
  }
127
138
 
128
139
  try {
@@ -220,5 +231,8 @@ module.exports = ({
220
231
  events.handleServerEvent = handleServerEvent;
221
232
  events.handleBlockletEvent = handleBlockletEvent;
222
233
 
234
+ certManager.on('cert.issued', (data) => onEvent(EVENTS.CERT_ISSUED, data));
235
+ certManager.on('cert.error', (data) => onEvent(EVENTS.CERT_ERROR, data));
236
+
223
237
  return events;
224
238
  };
package/lib/index.js CHANGED
@@ -10,6 +10,7 @@ const {
10
10
  fromBlockletSource,
11
11
  toBlockletSource,
12
12
  } = require('@blocklet/meta/lib/constants');
13
+ const { getServiceMetas } = require('@blocklet/meta/lib/service');
13
14
  const { listProviders } = require('@abtnode/router-provider');
14
15
  const { DEFAULT_CERTIFICATE_EMAIL, EVENTS } = require('@abtnode/constant');
15
16
 
@@ -29,7 +30,6 @@ const IP = require('./util/ip');
29
30
  const DomainStatus = require('./util/domain-status');
30
31
  const Upgrade = require('./util/upgrade');
31
32
  const resetNode = require('./util/reset-node');
32
- const { getServices } = require('./util/service');
33
33
  const DiskMonitor = require('./util/disk-monitor');
34
34
  const createQueue = require('./queue');
35
35
  const createEvents = require('./event');
@@ -133,11 +133,13 @@ function ABTNode(options) {
133
133
  registry: blockletRegistry,
134
134
  daemon: options.daemon,
135
135
  });
136
+ blockletManager.setMaxListeners(0);
136
137
 
137
138
  const {
138
139
  handleRouting,
139
140
  getRoutingRulesByDid,
140
141
  getSiteByDid,
142
+ resetSiteByDid,
141
143
  updateNodeRouting,
142
144
  takeRoutingSnapshot,
143
145
  getRoutingSites,
@@ -159,6 +161,7 @@ function ABTNode(options) {
159
161
 
160
162
  blockletManager.getRoutingRulesByDid = getRoutingRulesByDid;
161
163
  blockletManager.getSiteByDid = getSiteByDid;
164
+ blockletManager.resetSiteByDid = resetSiteByDid;
162
165
 
163
166
  // Generate an on node ready callback
164
167
  const onStatesReady = createStateReadyQueue({ states, options, dataDirs });
@@ -216,6 +219,7 @@ function ABTNode(options) {
216
219
  updateChildBlocklets: blockletManager.updateChildren.bind(blockletManager),
217
220
  getLatestBlockletVersion: blockletManager.getLatestBlockletVersion.bind(blockletManager),
218
221
  getBlockletMetaFromUrl: blockletManager.getMetaFromUrl.bind(blockletManager),
222
+ resetBlocklet: blockletManager.reset.bind(blockletManager),
219
223
 
220
224
  deleteBlockletProcess: blockletManager.deleteProcess.bind(blockletManager),
221
225
 
@@ -261,6 +265,7 @@ function ABTNode(options) {
261
265
  getUsers: teamAPI.getUsers.bind(teamAPI),
262
266
  getUsersCount: teamAPI.getUsersCount.bind(teamAPI),
263
267
  getUser: teamAPI.getUser.bind(teamAPI),
268
+ getOwner: teamAPI.getOwner.bind(teamAPI),
264
269
  getNodeUsers: () => teamAPI.getUsers({ teamDid: options.nodeDid }),
265
270
  getNodeUser: (user) => teamAPI.getUser({ teamDid: options.nodeDid, user }),
266
271
  addUser: teamAPI.addUser.bind(teamAPI),
@@ -372,7 +377,7 @@ function ABTNode(options) {
372
377
  endSession: (params, context) => states.session.end(params.id, context),
373
378
 
374
379
  // Services
375
- getServices: (params, context) => getServices({ stringifySchema: true }, context),
380
+ getServices: (params, context) => getServiceMetas({ stringifySchema: true }, context),
376
381
 
377
382
  // Utilities: moved here because some deps require native build
378
383
  getSysInfo,
@@ -7,7 +7,7 @@ module.exports = async ({ states, config, configFile, printInfo }) => {
7
7
  printInfo('Try to update node config to 1.0.21...');
8
8
  let changed = false;
9
9
 
10
- const rawConfig = yaml.safeLoad(fs.readFileSync(configFile).toString());
10
+ const rawConfig = yaml.load(fs.readFileSync(configFile).toString());
11
11
  const info = await states.node.read();
12
12
 
13
13
  const updates = [
@@ -13,7 +13,7 @@ module.exports = async ({ states, config, configFile, printInfo }) => {
13
13
  printInfo('Try to update node config to 1.0.22...');
14
14
  let changed = false;
15
15
 
16
- const rawConfig = yaml.safeLoad(fs.readFileSync(configFile).toString());
16
+ const rawConfig = yaml.load(fs.readFileSync(configFile).toString());
17
17
  const info = await states.node.read();
18
18
 
19
19
  const updates = [
@@ -10,7 +10,7 @@ module.exports = async ({ states, config, configFile, printInfo }) => {
10
10
  printInfo('Try to update node config to 1.0.25...');
11
11
  let changed = false;
12
12
 
13
- const rawConfig = yaml.safeLoad(fs.readFileSync(configFile).toString());
13
+ const rawConfig = yaml.load(fs.readFileSync(configFile).toString());
14
14
  const info = await states.node.read();
15
15
 
16
16
  const updates = [
@@ -7,7 +7,7 @@ module.exports = async ({ states, config, configFile, printInfo }) => {
7
7
  printInfo('Try to update node config to 1.0.32...');
8
8
  let changed = false;
9
9
 
10
- const rawConfig = yaml.safeLoad(fs.readFileSync(configFile).toString());
10
+ const rawConfig = yaml.load(fs.readFileSync(configFile).toString());
11
11
  const info = await states.node.read();
12
12
 
13
13
  const updates = [{ key: 'routing.https', value: true }];
@@ -22,7 +22,7 @@ module.exports = async ({ states, printInfo, configFile }) => {
22
22
  await states.node.updateNodeInfo(info);
23
23
 
24
24
  if (process.env.NODE_ENV !== 'development') {
25
- let rawConfig = yaml.safeLoad(fs.readFileSync(configFile).toString());
25
+ let rawConfig = yaml.load(fs.readFileSync(configFile).toString());
26
26
  set(rawConfig, 'node.routing.ipWildcardDomain', info.routing.ipWildcardDomain);
27
27
  set(rawConfig, 'node.routing.wildcardCertHost', DEFAULT_WILDCARD_CERT_HOST);
28
28
  set(rawConfig, 'node.didDomain', DEFAULT_DID_DOMAIN);
@@ -19,7 +19,7 @@ module.exports = async ({ states, configFile, dataDir }) => {
19
19
  try {
20
20
  fs.writeFileSync(file, crypto.randomBytes(32), { encoding: 'binary', mode: '0600' });
21
21
 
22
- const config = yaml.safeLoad(fs.readFileSync(configFile).toString(), { json: true });
22
+ const config = yaml.load(fs.readFileSync(configFile).toString(), { json: true });
23
23
  config.node.sk = security.encrypt(config.node.sk, config.node.did, fs.readFileSync(file));
24
24
  fs.writeFileSync(configFile, yaml.dump(config));
25
25
  await states.node.updateNodeInfo({ sk: config.node.sk });
@@ -41,7 +41,7 @@ module.exports = async ({ states, config, configFile, printInfo }) => {
41
41
  printInfo('Try to update node config to 1.0.21...');
42
42
  let changed = false;
43
43
 
44
- const rawConfig = yaml.safeLoad(fs.readFileSync(configFile).toString());
44
+ const rawConfig = yaml.load(fs.readFileSync(configFile).toString());
45
45
  const info = await states.node.read();
46
46
 
47
47
  const updates = [
@@ -863,88 +863,6 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
863
863
  return changes.some(Boolean);
864
864
  };
865
865
 
866
- /**
867
- * Add system routing rules for blocklet in dashboard site
868
- *
869
- * @returns {boolean} if routing changed
870
- * TODO: do we still need this?
871
- */
872
- const _ensureBlockletRulesForDashboardSite = async (blocklet, sites, nodeInfo, context = {}) => {
873
- // blocklet level duplication detection
874
- const findRulesByInterface = (site, value) =>
875
- site.rules.filter((x) => x.isProtected && x.to.did === blocklet.meta.did && get(x, 'to.interfaceName') === value);
876
-
877
- const ipSite = sites.find((x) => x.domain === DOMAIN_FOR_IP_SITE);
878
- if (!ipSite) {
879
- logger.warn(`Routing rule for dashboard not found, abort on ensure rules for blocklet ${blocklet.meta.did}`);
880
- return false;
881
- }
882
-
883
- // If we have rule for legacy path prefix, just return
884
- const pathPrefixLegacy = normalizePathPrefix(`/${nodeInfo.routing.adminPath}/${blocklet.meta.name}`);
885
- if (hasRuleByPrefix(ipSite, pathPrefixLegacy)) {
886
- return false;
887
- }
888
-
889
- const removedRuleIds = [];
890
- const changes = await Promise.all(
891
- blocklet.meta.interfaces.map(async (x) => {
892
- let changed = false; // if state db was mutated
893
- let pathPrefix = '';
894
- if (x.prefix === BLOCKLET_DYNAMIC_PATH_PREFIX) {
895
- // pathPrefix for dynamic path: `/{adminPath}/{blockletName}/{interfaceName}`
896
- pathPrefix = normalizePathPrefix(`/${pathPrefixLegacy}/${x.name}`);
897
- } else {
898
- // pathPrefix for fixed path: `/{interfacePrefix}`
899
- pathPrefix = normalizePathPrefix(x.prefix);
900
- }
901
-
902
- // Remove if we have rule with same interface
903
- const exists = findRulesByInterface(ipSite, x.name);
904
- if (exists.length) {
905
- // Note: we need to keep a list of removed rules since we might remove multiple rules in the loop
906
- exists.forEach((e) => removedRuleIds.push(e.id));
907
- await states.site.update(
908
- { _id: ipSite.id },
909
- { $set: { rules: ipSite.rules.filter((r) => removedRuleIds.includes(r.id) === false) } }
910
- );
911
- changed = true;
912
- } else if (hasRuleByPrefix(ipSite, pathPrefix)) {
913
- // Skip same pathPrefix rules
914
- return changed;
915
- }
916
-
917
- // 不在 dashboard site 中添加 path 和 prefix 都是根路径(/) 的路由
918
- if (x.path === '/' && x.prefix === '/') {
919
- return changed;
920
- }
921
-
922
- await routerManager.addRoutingRule(
923
- {
924
- id: ipSite.id,
925
- rule: {
926
- from: { pathPrefix },
927
- to: {
928
- type: ROUTING_RULE_TYPES.BLOCKLET,
929
- port: findInterfacePortByName(blocklet, x.name),
930
- did: blocklet.meta.did, // root blocklet did
931
- interfaceName: x.name, // root blocklet interface
932
- },
933
- isProtected: true,
934
- },
935
- skipCheckDynamicBlacklist: true,
936
- },
937
- context
938
- );
939
- changed = true;
940
-
941
- return changed;
942
- })
943
- );
944
-
945
- return changes.some(Boolean);
946
- };
947
-
948
866
  const _removeBlockletSites = async (blocklet, nodeInfo, context = {}) => {
949
867
  let changed = false;
950
868
 
@@ -980,14 +898,6 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
980
898
  _ensureBlockletSites(blocklet, sites, nodeInfo, context),
981
899
  ];
982
900
 
983
- if (nodeInfo.mode === NODE_MODES.DEBUG || ['e2e', 'test'].includes(process.env.NODE_ENV)) {
984
- logger.info('Add blocklet endpoint in dashboard site', {
985
- nodeMode: nodeInfo.mode,
986
- NODE_ENV: process.env.NODE_ENV,
987
- });
988
- tasks.push(_ensureBlockletRulesForDashboardSite(blocklet, sites, nodeInfo, context));
989
- }
990
-
991
901
  const changes = await Promise.all(tasks);
992
902
 
993
903
  return changes.some(Boolean);
@@ -1131,6 +1041,16 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1131
1041
  return (sites || []).find((x) => x.domain === domain);
1132
1042
  }
1133
1043
 
1044
+ async function resetSiteByDid(did, { refreshRouterProvider = true } = {}) {
1045
+ const blocklet = await states.blocklet.getBlocklet(did);
1046
+ await removeBlockletRouting(blocklet);
1047
+ await ensureBlockletRouting(blocklet);
1048
+ if (refreshRouterProvider) {
1049
+ const hash = await takeRoutingSnapshot({ message: `Reset blocklet ${did}`, dryRun: false });
1050
+ logger.info('reset blocklet routing rules', { did, hash });
1051
+ }
1052
+ }
1053
+
1134
1054
  const providers = {}; // we need to keep reference for different router instances
1135
1055
  const handleRouting = async (nodeInfo) => {
1136
1056
  const providerName = get(nodeInfo, 'routing.provider', null);
@@ -1382,6 +1302,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1382
1302
  handleRouting,
1383
1303
  getRoutingRulesByDid,
1384
1304
  getSiteByDid,
1305
+ resetSiteByDid,
1385
1306
  updateNodeRouting,
1386
1307
  takeRoutingSnapshot,
1387
1308
  getRoutingSites,
@@ -110,7 +110,7 @@ class Router {
110
110
  await this.provider.update({
111
111
  routingTable: this.routingTable,
112
112
  certificates,
113
- globalHeaders: headers,
113
+ commonHeaders: headers,
114
114
  services,
115
115
  nodeInfo: pick(nodeInfo, ['name', 'version', 'port', 'mode', 'enableWelcomePage', 'routing']),
116
116
  });
@@ -222,7 +222,7 @@ class RouterManager extends EventEmitter {
222
222
  }
223
223
 
224
224
  // let custom domain in front of protected domain
225
- const domainAliases = [{ value: domainAlias, isProtected: false }, ...dbSite.domainAliases];
225
+ const domainAliases = [{ value: domainAlias, isProtected: false }, ...(dbSite.domainAliases || [])];
226
226
 
227
227
  const updateResult = await states.site.update({ _id: id }, { $set: { domainAliases } });
228
228
  logger.debug('add domain alias update result', { id, updateResult, domainAlias });
@@ -420,7 +420,7 @@ class RouterManager extends EventEmitter {
420
420
  }
421
421
 
422
422
  async getMatchedCert(domain) {
423
- const certs = await this.certManager.getAll();
423
+ const certs = await this.certManager.getAllNormal();
424
424
  const matchedCert = certs.find((cert) => this.isCertMatchedDomain(cert, domain));
425
425
 
426
426
  if (matchedCert) {
@@ -546,7 +546,7 @@ class RouterManager extends EventEmitter {
546
546
  getRoutingParams: async () => ({
547
547
  sites: await ensureLatestInfo(sites),
548
548
  certificates,
549
- globalHeaders: get(info, 'routing.headers', {}),
549
+ commonHeaders: get(info, 'routing.headers', {}),
550
550
  services: [], // TODO: do we need to add some item here?
551
551
  nodeInfo: info,
552
552
  }),
@@ -66,6 +66,16 @@ const formatBlocklet = (blocklet, phase, dek) => {
66
66
  return blocklet;
67
67
  };
68
68
 
69
+ const fixChildren = (children) => {
70
+ if (!children) {
71
+ return;
72
+ }
73
+
74
+ children.forEach((child) => {
75
+ child.mode = child.mode || BLOCKLET_MODES.PRODUCTION;
76
+ });
77
+ };
78
+
69
79
  class BlockletState extends BaseState {
70
80
  /**
71
81
  * Creates an instance of BlockletState
@@ -163,6 +173,8 @@ class BlockletState extends BaseState {
163
173
  defaultPort: getMaxPort(ports),
164
174
  });
165
175
 
176
+ fixChildren(children);
177
+
166
178
  // add to db
167
179
  this.db.insert(
168
180
  {
@@ -244,6 +256,8 @@ class BlockletState extends BaseState {
244
256
  logger.info('Fill children ports when when upgrading blocklet', { name: doc.meta.name, did: doc.meta.did });
245
257
  await this.fillChildrenPorts(children, { oldChildren: doc.children, defaultPort: getMaxPort(ports) });
246
258
 
259
+ fixChildren(children);
260
+
247
261
  // add to db
248
262
  const newDoc = await this.updateBlocklet(meta.did, {
249
263
  meta: omit(sanitized, ['htmlAst']),
@@ -506,7 +520,7 @@ class BlockletState extends BaseState {
506
520
 
507
521
  const newChildren = [...oldChildren];
508
522
  for (const child of children) {
509
- const { meta, mountPoint, sourceUrl = '', source = '', deployedFrom = '' } = child;
523
+ const { meta, mountPoint, sourceUrl = '', source = '', deployedFrom = '', mode } = child;
510
524
 
511
525
  if (!mountPoint) {
512
526
  throw new Error(`mountPoint is required when adding component ${getDisplayName(child, true)}`);
@@ -524,9 +538,12 @@ class BlockletState extends BaseState {
524
538
  sourceUrl,
525
539
  source,
526
540
  deployedFrom,
541
+ mode,
527
542
  dynamic: true,
528
543
  status: BlockletStatus.added,
529
544
  });
545
+
546
+ fixChildren(newChildren);
530
547
  }
531
548
 
532
549
  // use upgradeBlocklet to assign ports to children and write new data to db
@@ -267,6 +267,19 @@ class TeamManager extends EventEmitter {
267
267
  }
268
268
  }
269
269
 
270
+ async getOwner(did) {
271
+ let owner;
272
+ if (this.isNodeTeam(did)) {
273
+ const nodeInfo = await this.states.node.read();
274
+ owner = get(nodeInfo, 'nodeOwner');
275
+ } else {
276
+ const settings = await this.states.blockletExtras.getSettings(did);
277
+ owner = get(settings, 'owner');
278
+ }
279
+
280
+ return owner;
281
+ }
282
+
270
283
  // =======
271
284
  // Private
272
285
  // =======
@@ -48,7 +48,6 @@ const { validate: validateEngine, get: getEngine } = require('../blocklet/manage
48
48
 
49
49
  const isRequirementsSatisfied = require('./requirement');
50
50
  const { getDidDomainForBlocklet } = require('./get-domain-for-blocklet');
51
- const { getServices } = require('./service');
52
51
  const {
53
52
  isBeforeInstalled,
54
53
  expandBundle,
@@ -104,7 +103,7 @@ const PRIVATE_NODE_ENVS = [
104
103
  * appMain: app entry file or script (run appMain to start blocklet process)
105
104
  * appCwd: cwd of appMain
106
105
  */
107
- const getBlockletDirs = (blocklet, { rootBlocklet, dataDirs, ensure = false } = {}) => {
106
+ const getBlockletDirs = (blocklet, { rootBlocklet, dataDirs, ensure = false, e2eMode = false } = {}) => {
108
107
  if (!rootBlocklet) {
109
108
  // eslint-disable-next-line no-param-reassign
110
109
  rootBlocklet = blocklet;
@@ -126,8 +125,13 @@ const getBlockletDirs = (blocklet, { rootBlocklet, dataDirs, ensure = false } =
126
125
 
127
126
  const { main, group } = blocklet.meta;
128
127
 
129
- const startFromDevEntry =
130
- blocklet.mode === BLOCKLET_MODES.DEVELOPMENT && blocklet.meta.scripts && blocklet.meta.scripts.dev;
128
+ let startFromDevEntry = '';
129
+ if (blocklet.mode === BLOCKLET_MODES.DEVELOPMENT && blocklet.meta.scripts) {
130
+ startFromDevEntry = blocklet.meta.scripts.dev;
131
+ if (e2eMode && blocklet.meta.scripts.e2eDev) {
132
+ startFromDevEntry = blocklet.meta.scripts.e2eDev;
133
+ }
134
+ }
131
135
 
132
136
  if (!main && !startFromDevEntry && group !== BlockletGroup.gateway) {
133
137
  throw new Error('Incorrect blocklet manifest: missing `main` field');
@@ -174,7 +178,7 @@ const getBlockletDirs = (blocklet, { rootBlocklet, dataDirs, ensure = false } =
174
178
  let appMain = null;
175
179
  let appCwd = null;
176
180
  if (startFromDevEntry) {
177
- appMain = blocklet.meta.scripts.dev;
181
+ appMain = startFromDevEntry;
178
182
  appCwd = appDir;
179
183
  } else if (group === 'dapp') {
180
184
  appMain = getBlockletEngine(blocklet.meta).script || BLOCKLET_ENTRY_FILE;
@@ -414,7 +418,7 @@ const getBlockletMetaFromUrl = async (url) => {
414
418
  * Start all precesses of a blocklet
415
419
  * @param {*} blocklet should contain env props
416
420
  */
417
- const startBlockletProcess = async (blocklet, { preStart = noop, nodeEnvironments, nodeInfo } = {}) => {
421
+ const startBlockletProcess = async (blocklet, { preStart = noop, nodeEnvironments, nodeInfo, e2eMode } = {}) => {
418
422
  if (!blocklet) {
419
423
  throw new Error('blocklet should not be empty');
420
424
  }
@@ -463,7 +467,7 @@ const startBlockletProcess = async (blocklet, { preStart = noop, nodeEnvironment
463
467
  }
464
468
 
465
469
  if (b.mode === BLOCKLET_MODES.DEVELOPMENT) {
466
- options.env.NODE_ENV = 'development';
470
+ options.env.NODE_ENV = e2eMode ? 'e2e' : 'development';
467
471
  options.env.BROWSER = 'none';
468
472
  options.env.PORT = options.env[BLOCKLET_DEFAULT_PORT_NAME];
469
473
  options.script = appMain;
@@ -1019,7 +1023,7 @@ const fixAndVerifyBlockletMeta = (meta, did) => {
1019
1023
  }
1020
1024
 
1021
1025
  // this step comes last because it has side effects: the meta is changed
1022
- return fixAndValidateService(meta, getServices());
1026
+ return fixAndValidateService(meta);
1023
1027
  };
1024
1028
 
1025
1029
  const getUpdateMetaList = (oldMetas = [], newMetas = []) => {
@@ -23,4 +23,4 @@ const getDidDomainForBlocklet = ({ name, daemonDid, didDomain }) => {
23
23
  return `${prefix}-${daemonDid.toLowerCase()}.${didDomain}`;
24
24
  };
25
25
 
26
- module.exports = { getIpDnsDomainForBlocklet, getDidDomainForBlocklet };
26
+ module.exports = { getIpDnsDomainForBlocklet, getDidDomainForBlocklet, formatName };
package/lib/util/index.js CHANGED
@@ -35,7 +35,6 @@ const DEFAULT_WELLKNOWN_PORT = 8088;
35
35
 
36
36
  const logger = require('@abtnode/logger')('@abtnode/core:util');
37
37
 
38
- const { getServices } = require('./service');
39
38
  const request = require('./request');
40
39
 
41
40
  const validateOwner = (owner) => {
@@ -496,13 +495,9 @@ const lib = {
496
495
  ensureDataDirs,
497
496
  getDataDirs,
498
497
  getBaseUrls,
499
- getBlockletMeta: (dir, opts = {}) =>
500
- parseBlockletMeta(dir, {
501
- serviceMetas: getServices(),
502
- ...opts,
503
- }),
498
+ getBlockletMeta: parseBlockletMeta,
504
499
  validateBlockletMeta: (meta, opts = {}) => {
505
- fixAndValidateService(meta, getServices(), opts.fix);
500
+ fixAndValidateService(meta);
506
501
  return validateMeta(meta, opts);
507
502
  },
508
503
  getBlockletMetaByUrl,
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.7.7",
6
+ "version": "1.7.10",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,32 +19,31 @@
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
- "@abtnode/certificate-manager": "1.7.7",
23
- "@abtnode/constant": "1.7.7",
24
- "@abtnode/cron": "1.7.7",
25
- "@abtnode/db": "1.7.7",
26
- "@abtnode/logger": "1.7.7",
27
- "@abtnode/queue": "1.7.7",
28
- "@abtnode/rbac": "1.7.7",
29
- "@abtnode/router-provider": "1.7.7",
30
- "@abtnode/static-server": "1.7.7",
31
- "@abtnode/timemachine": "1.7.7",
32
- "@abtnode/util": "1.7.7",
33
- "@arcblock/did": "^1.16.0",
34
- "@arcblock/did-motif": "^1.1.4",
35
- "@arcblock/event-hub": "1.16.0",
22
+ "@abtnode/certificate-manager": "1.7.10",
23
+ "@abtnode/constant": "1.7.10",
24
+ "@abtnode/cron": "1.7.10",
25
+ "@abtnode/db": "1.7.10",
26
+ "@abtnode/logger": "1.7.10",
27
+ "@abtnode/queue": "1.7.10",
28
+ "@abtnode/rbac": "1.7.10",
29
+ "@abtnode/router-provider": "1.7.10",
30
+ "@abtnode/static-server": "1.7.10",
31
+ "@abtnode/timemachine": "1.7.10",
32
+ "@abtnode/util": "1.7.10",
33
+ "@arcblock/did": "^1.16.5",
34
+ "@arcblock/did-motif": "^1.1.5",
35
+ "@arcblock/event-hub": "1.16.5",
36
36
  "@arcblock/pm2-events": "^0.0.5",
37
- "@arcblock/vc": "^1.16.0",
38
- "@blocklet/meta": "1.7.7",
37
+ "@arcblock/vc": "^1.16.5",
38
+ "@blocklet/meta": "1.7.10",
39
39
  "@fidm/x509": "^1.2.1",
40
40
  "@nedb/core": "^1.2.2",
41
41
  "@nedb/multi": "^1.2.2",
42
- "@ocap/mcrypto": "^1.16.0",
43
- "@ocap/util": "^1.16.0",
44
- "@ocap/wallet": "^1.16.0",
42
+ "@ocap/mcrypto": "^1.16.5",
43
+ "@ocap/util": "^1.16.5",
44
+ "@ocap/wallet": "^1.16.5",
45
45
  "@slack/webhook": "^5.0.3",
46
- "ajv": "^7.0.3",
47
- "axios": "^0.25.0",
46
+ "axios": "^0.26.1",
48
47
  "axon": "^2.0.3",
49
48
  "chalk": "^4.0.0",
50
49
  "deep-diff": "^1.0.2",
@@ -56,7 +55,7 @@
56
55
  "is-ip": "^3.1.0",
57
56
  "is-url": "^1.2.4",
58
57
  "joi": "^17.6.0",
59
- "js-yaml": "^3.14.0",
58
+ "js-yaml": "^4.1.0",
60
59
  "lodash": "^4.17.21",
61
60
  "lru-cache": "^6.0.0",
62
61
  "pm2": "^5.1.2",
@@ -78,5 +77,5 @@
78
77
  "express": "^4.17.1",
79
78
  "jest": "^27.4.5"
80
79
  },
81
- "gitHead": "619db37ea7a91c64a9bf30836a55c70d55325e73"
80
+ "gitHead": "8eab10fd39b6183a2fa4d2706f52e8b2ecaa059a"
82
81
  }
@@ -1,81 +0,0 @@
1
- const fs = require('fs-extra');
2
- const cloneDeep = require('lodash/cloneDeep');
3
- const Ajv = require('ajv').default;
4
-
5
- const { NODE_SERVICES } = require('@abtnode/constant');
6
-
7
- const ajv = new Ajv({
8
- useDefaults: true,
9
- removeAdditional: 'all',
10
- });
11
-
12
- const SERVICES = {
13
- AUTH: fs.readJSONSync(require.resolve('@abtnode/blocklet-services/configs/auth.json')),
14
- };
15
-
16
- const getServices = ({ stringifySchema = false } = {}) => {
17
- const list = Object.values(SERVICES).map((x) => {
18
- const data = cloneDeep(x);
19
- if (stringifySchema) {
20
- data.schema = JSON.stringify(x.schema);
21
- }
22
- return data;
23
- });
24
-
25
- // backward compatible
26
- const authService = cloneDeep(list.find((x) => x.name === NODE_SERVICES.AUTH));
27
- authService.name = NODE_SERVICES.AUTH_SERVICE;
28
- list.push(authService);
29
-
30
- return list;
31
- };
32
-
33
- const getServiceMeta = (serviceName) => {
34
- if (!serviceName) {
35
- throw new Error('service name should not be empty');
36
- }
37
- const metas = getServices();
38
- const meta = metas.find((x) => x.name === serviceName);
39
- if (!meta) {
40
- throw new Error(`service ${serviceName} does not exist`);
41
- }
42
-
43
- return meta;
44
- };
45
-
46
- const getServiceConfig = (service, customConfig) => {
47
- const serviceMeta = typeof service === 'string' ? getServiceMeta(service) : service;
48
-
49
- const validate = ajv.compile(serviceMeta.schema.JSONSchema);
50
-
51
- const data = cloneDeep(customConfig || {});
52
- // this method may have side effect thar will fill default value to customConfig
53
- validate(data || {});
54
-
55
- return data;
56
- };
57
-
58
- const getDefaultServiceConfig = (service) => {
59
- const serviceMeta = typeof service === 'string' ? getServiceMeta(service) : service;
60
-
61
- // parse empty custom config to get default config
62
- return getServiceConfig(serviceMeta, {});
63
- };
64
-
65
- const findService = (services, name) => {
66
- const names = [name];
67
-
68
- // backward compatible
69
- if (name === NODE_SERVICES.AUTH) {
70
- names.push(NODE_SERVICES.AUTH_SERVICE);
71
- }
72
-
73
- return (services || []).find((x) => names.includes(x.name));
74
- };
75
-
76
- module.exports = {
77
- getServices,
78
- getServiceConfig,
79
- getDefaultServiceConfig,
80
- findService,
81
- };