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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,160 @@
1
+ /* eslint-disable no-console */
2
+ const fs = require('fs-extra');
3
+ const axon = require('axon');
4
+ const path = require('path');
5
+ const shell = require('shelljs');
6
+ const semver = require('semver');
7
+
8
+ const VERBOSE = '';
9
+ // const VERBOSE = '--verbose';
10
+
11
+ const BINARY_NAME = process.env.ABT_NODE_BINARY_NAME;
12
+ const COMMAND_NAME = process.env.ABT_NODE_COMMAND_NAME;
13
+ const PACKAGE_NAME = process.env.ABT_NODE_PACKAGE_NAME;
14
+ const VALID_BINARY_NAMES = ['blocklet', 'abtnode'];
15
+
16
+ console.info(`binary name: ${BINARY_NAME}`);
17
+ console.info(`command name: ${COMMAND_NAME}`);
18
+ console.info(`package name: ${PACKAGE_NAME}`);
19
+
20
+ // async and promise style of shelljs.exec
21
+ const runAsync = (command, options) =>
22
+ new Promise((resolve) => {
23
+ console.info(`Run command: ${command}`);
24
+ shell.exec(command, { async: true, ...options }, (code, stdout, stderr) => {
25
+ resolve({ code, stdout, stderr });
26
+ });
27
+ });
28
+
29
+ const getInstaller = () => {
30
+ const { stdout: pnpmPath } = shell.which('pnpm') || {};
31
+ if (pnpmPath) {
32
+ const { stdout: expectedDir } = shell.exec('pnpm bin -g', { silent: true });
33
+ const { stdout: binPath } = shell.which(BINARY_NAME);
34
+ if (expectedDir && binPath && expectedDir.trim() === path.dirname(binPath)) {
35
+ return 'pnpm';
36
+ }
37
+ }
38
+
39
+ const { stdout: yarnPath } = shell.which('yarn') || {};
40
+ if (yarnPath) {
41
+ const { stdout: binaryDir } = shell.exec(`yarn global bin ${BINARY_NAME}`, { silent: true });
42
+ const binaryPath = path.join(binaryDir, BINARY_NAME);
43
+ if (fs.existsSync(binaryPath)) {
44
+ return 'yarn';
45
+ }
46
+ }
47
+
48
+ return 'npm';
49
+ };
50
+
51
+ const getBinaryDir = () => {
52
+ const installer = getInstaller();
53
+ if (installer === 'pnpm') {
54
+ const { stdout: binaryDir } = shell.exec('pnpm bin -g', { silent: true });
55
+ return binaryDir.trim();
56
+ }
57
+
58
+ if (installer === 'yarn') {
59
+ const { stdout: binaryDir } = shell.exec(`yarn global bin ${BINARY_NAME}`, { silent: true });
60
+ return binaryDir.trim();
61
+ }
62
+
63
+ const { stdout: binaryDir } = shell.exec(`npm bin -g ${BINARY_NAME}`, { silent: true });
64
+ return binaryDir.trim();
65
+ };
66
+
67
+ const installBlockletServer = async (version) => {
68
+ if (VALID_BINARY_NAMES.includes(BINARY_NAME) === false) {
69
+ return { code: 1, stderr: 'Abort because you are not using a standard @blocklet/cli setup' };
70
+ }
71
+
72
+ const installer = getInstaller();
73
+ const commands = {
74
+ pnpm: `pnpm add -g ${PACKAGE_NAME}@${version} ${VERBOSE}`,
75
+ npm: `npm install -g ${PACKAGE_NAME}@${version} ${VERBOSE}`,
76
+ yarn: `yarn global add ${PACKAGE_NAME}@${version} ${VERBOSE}`,
77
+ };
78
+ const command = commands[installer];
79
+ console.info('Installing blocklet server', { version, installer, command });
80
+ const result = await runAsync(command);
81
+ console.info('Installing blocklet server done', { version });
82
+ return result;
83
+ };
84
+
85
+ const verifyBlockletServer = async (version) => {
86
+ console.info('Verifying blocklet server', { version });
87
+ const result = await runAsync(`${BINARY_NAME} --version`);
88
+ console.info('Verifying blocklet server done', { version });
89
+
90
+ if (result.code === 0) {
91
+ const actual = semver.coerce(result.stdout);
92
+ if (actual && actual.version === version) {
93
+ return result;
94
+ }
95
+
96
+ return { code: 1, stderr: `Blocklet server version is not expected ${version}` };
97
+ }
98
+
99
+ return result;
100
+ };
101
+
102
+ // Restart Blocklet Server
103
+ const restartBlockletServer = async (dataDir) => {
104
+ // We put this in the event loop because it will terminate the current node process
105
+ // But we need to wait for the session state to transition from restarting to cleanup
106
+ process.nextTick(async () => {
107
+ const binaryDir = getBinaryDir();
108
+ console.info('Restarting blocklet server', { dataDir, binaryDir });
109
+
110
+ if (!binaryDir) {
111
+ console.error('Can not restart blocklet server because no binaryDir found');
112
+ return;
113
+ }
114
+
115
+ // restart the server
116
+ if (VALID_BINARY_NAMES.includes(BINARY_NAME)) {
117
+ await runAsync(`${binaryDir}/${COMMAND_NAME} start`, { cwd: path.dirname(dataDir) });
118
+ } else {
119
+ await runAsync(`${COMMAND_NAME} start`, { cwd: path.dirname(dataDir) });
120
+ }
121
+ console.info('Restarting blocklet server done');
122
+ });
123
+
124
+ return dataDir;
125
+ };
126
+
127
+ // Start the server
128
+ const sock = axon.socket('rep');
129
+ const port = Number(process.env.ABT_NODE_UPDATER_PORT);
130
+ sock.bind(port, '127.0.0.1');
131
+ sock.on('message', async (raw, reply) => {
132
+ console.info('receive', { raw });
133
+
134
+ try {
135
+ const message = JSON.parse(raw);
136
+ if (message.command === 'install') {
137
+ const result = await installBlockletServer(message.version);
138
+ reply(result);
139
+ } else if (message.command === 'verify') {
140
+ const result = await verifyBlockletServer(message.version);
141
+ reply(result);
142
+ } else if (message.command === 'restart') {
143
+ const result = await restartBlockletServer(message.dataDir);
144
+ reply(result);
145
+ } else if (message.command === 'shutdown') {
146
+ reply('Terminate myself after this message');
147
+ setTimeout(() => {
148
+ process.exit(0);
149
+ }, 500);
150
+ } else if (message.command === 'ping') {
151
+ reply('pong');
152
+ } else {
153
+ reply({ error: 'Unknown command' });
154
+ }
155
+ } catch (err) {
156
+ console.error('failed to process command', { error: err });
157
+ }
158
+ });
159
+
160
+ console.info(`Blocklet Server Updater process is ready on port ${port}`);
@@ -61,13 +61,13 @@ const {
61
61
  isGatewayCacheEnabled,
62
62
  isBlockletSite,
63
63
  } = require('../util');
64
- const { getIpDnsDomainForBlocklet, getDidDomainForBlocklet } = require('../util/get-domain-for-blocklet');
64
+ const { getIpDnsDomainForBlocklet } = require('../util/get-domain-for-blocklet');
65
65
  const { getFromCache: getAccessibleExternalNodeIp } = require('../util/get-accessible-external-node-ip');
66
66
 
67
67
  const Router = require('./index');
68
68
  const states = require('../states');
69
69
  const { getBlockletDomainGroupName, getDidFromDomainGroupName } = require('../util/router');
70
- const { getBlockletKnownAs } = require('../util/blocklet');
70
+ const { getBlockletKnownAs, getBlockletDidDomainList } = require('../util/blocklet');
71
71
 
72
72
  const isServiceFeDevelopment = process.env.ABT_NODE_SERVICE_FE_PORT;
73
73
 
@@ -839,13 +839,10 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
839
839
  });
840
840
 
841
841
  if (!existSite) {
842
- const domainAliases = [];
843
-
844
- const didDomain = getDidDomainForBlocklet({ appPid: blocklet.appPid, didDomain: nodeInfo.didDomain });
845
- const ipEchoDnsDomain = getIpDnsDomainForBlocklet(blocklet);
842
+ const domainAliases = getBlockletDidDomainList(blocklet, nodeInfo);
846
843
 
847
844
  // let didDomain in front of ipEchoDnsDomain
848
- domainAliases.push({ value: didDomain, isProtected: true }, { value: ipEchoDnsDomain, isProtected: true });
845
+ domainAliases.push({ value: getIpDnsDomainForBlocklet(blocklet), isProtected: true });
849
846
 
850
847
  await routerManager.addRoutingSite(
851
848
  {
@@ -125,6 +125,7 @@ class NodeState extends BaseState {
125
125
  docker,
126
126
  mode,
127
127
  enableWelcomePage: mode !== NODE_MODES.SERVERLESS,
128
+ enableBetaRelease: false,
128
129
  runtimeConfig,
129
130
  ownerNft,
130
131
  diskAlertThreshold: DISK_ALERT_THRESHOLD_PERCENT,
@@ -1,11 +1,17 @@
1
1
  const pickBy = require('lodash/pickBy');
2
2
  const escapeStringRegexp = require('escape-string-regexp');
3
-
3
+ const pick = require('lodash/pick');
4
+ const cloneDeep = require('lodash/cloneDeep');
4
5
  const logger = require('@abtnode/logger')('@abtnode/core:states:user');
5
6
  const { isValid } = require('@arcblock/did');
6
7
  const { PASSPORT_STATUS } = require('@abtnode/constant');
8
+ const { upsertToPassports } = require('@abtnode/auth/lib/passport');
9
+ const { fromAppDid } = require('@arcblock/did-ext');
10
+ const { types } = require('@arcblock/did');
11
+
7
12
  const BaseState = require('./base');
8
13
  const { validateOwner } = require('../util');
14
+ const { loginSchema } = require('../validators/user');
9
15
 
10
16
  const isNullOrUndefined = (x) => x === undefined || x === null;
11
17
 
@@ -39,15 +45,25 @@ const isNullOrUndefined = (x) => x === undefined || x === null;
39
45
  * WalletAccount
40
46
  * @typedef {Object} WalletAccount
41
47
  * @property {'wallet'} provider
42
- * @property {string} did - did for auth0 account
43
- * @property {string} pk - pk for auth0 account
48
+ * @property {string} did - did for wallet account
49
+ * @property {string} pk - pk for wallet account
50
+ * @property {string} lastLoginAt - Last login time new Date().toISOString()
51
+ * @property {string} firstLoginAt - First login time new Date().toISOString()
52
+ */
53
+
54
+ /**
55
+ * NFTAccount
56
+ * @typedef {Object} NFTAccount
57
+ * @property {'nft'} provider
58
+ * @property {string} did - did for nft account
59
+ * @property {string} owner - owner for nft account
44
60
  * @property {string} lastLoginAt - Last login time new Date().toISOString()
45
61
  * @property {string} firstLoginAt - First login time new Date().toISOString()
46
62
  */
47
63
 
48
64
  /**
49
65
  * ConnectedAccount
50
- * @typedef {(Auth0Account|WalletAccount)} ConnectedAccount
66
+ * @typedef {(Auth0Account|WalletAccount|NFTAccount)} ConnectedAccount
51
67
  */
52
68
 
53
69
  /**
@@ -59,7 +75,6 @@ const isNullOrUndefined = (x) => x === undefined || x === null;
59
75
  * @property {string} avatar - url of user's avatar, eg: bn://avatar/7f8848569405f8cdf8b1b2788ebf7d0f.jpg
60
76
  * @property {string} did - user's did -> 实际上变为 uid, 是不可变的;真正的 wallet-did 转移到 extraConfigs 中去了
61
77
  * @property {string} pk - user's publicKey
62
- * @property {('owner'|'admin'|'member'|'guest'|string)} role - user's role
63
78
  * @property {UserPassport[]} passports - user's passport list
64
79
  * @property {boolean} approved - enable user to login
65
80
  * @property {string} locale - locale
@@ -72,8 +87,36 @@ const isNullOrUndefined = (x) => x === undefined || x === null;
72
87
  * @property {string} lastLoginIp - lastLoginIp
73
88
  * @property {Date} createdAt - createdAt
74
89
  * @property {Date} updatedAt - updatedAt
90
+ * @property {('owner'|'admin'|'member'|'guest'|string)} role - user's role deprecated
75
91
  */
76
92
 
93
+ function updateConnectedAccount(connectedAccounts = [], connectedAccount = {}) {
94
+ const now = new Date().toISOString();
95
+ const filterAccounts = connectedAccounts.filter(Boolean);
96
+ const updated = cloneDeep(filterAccounts);
97
+ const updates = Array.isArray(connectedAccount) ? connectedAccount : [connectedAccount];
98
+ updates.filter(Boolean).forEach((x) => {
99
+ if (x.provider && x.did) {
100
+ const findAccountIndex = updated.findIndex((item) => item.provider === x.provider && item.did === x.did);
101
+ if (findAccountIndex > -1) {
102
+ updated[findAccountIndex] = {
103
+ ...filterAccounts[findAccountIndex],
104
+ ...x,
105
+ lastLoginAt: now,
106
+ };
107
+ } else {
108
+ updated.push({
109
+ ...x,
110
+ firstLoginAt: now,
111
+ lastLoginAt: now,
112
+ });
113
+ }
114
+ }
115
+ });
116
+
117
+ return updated;
118
+ }
119
+
77
120
  class User extends BaseState {
78
121
  constructor(baseDir, config = {}) {
79
122
  super(baseDir, { filename: 'user.db', ...config });
@@ -296,14 +339,124 @@ class User extends BaseState {
296
339
  return this.find(queryParam);
297
340
  }
298
341
 
342
+ /**
343
+ * user 版本标准化,统一将 user 转换为最新的结构,结构参考 core/state/lib/states/user.js L12-L75
344
+ */
345
+ // 1: 没有 extraConfigs 字段
346
+ // 2: 有 source 和 extraConfigs 字段,oauth 账号的 extraConfigs 中包含 sourceId 和 sourceProvider 字段
347
+ // 3: 只有 extraConfigs 字段,且 extraConfigs.connectedAccounts 中包含 extraConfigs.sourceProvider 的 account
348
+ async normalize(user, { blockletSk }) {
349
+ if (!user) {
350
+ return user;
351
+ }
352
+ let version = 1;
353
+ if (user.extraConfigs) {
354
+ version = 2;
355
+
356
+ const connectedAccounts = user.extraConfigs?.connectedAccounts || [];
357
+ const sourceProvider = user.extraConfigs?.sourceProvider || '';
358
+ if (connectedAccounts.some((item) => item.provider === sourceProvider)) {
359
+ version = 3;
360
+ }
361
+ }
362
+
363
+ const versionMap = {
364
+ 1: async () => {
365
+ const connectedAccounts = [
366
+ {
367
+ provider: 'wallet',
368
+ did: user.did,
369
+ pk: user.pk,
370
+ firstLoginAt: user.firstLoginAt,
371
+ lastLoginAt: user.lastLoginAt,
372
+ },
373
+ ];
374
+ const sourceProvider = 'wallet';
375
+ const updatedUser = await this.update({
376
+ did: user.did,
377
+ pk: user.pk,
378
+ extraConfigs: {
379
+ sourceProvider,
380
+ connectedAccounts,
381
+ },
382
+ });
383
+ return updatedUser;
384
+ },
385
+ 2: async () => {
386
+ // eslint-disable-next-line prefer-const
387
+ let { sourceId, sourceProvider, connectedAccounts = [] } = user.extraConfigs || {};
388
+ if (sourceId && sourceProvider) {
389
+ connectedAccounts = [
390
+ {
391
+ provider: sourceProvider,
392
+ id: sourceId,
393
+ did: user.did,
394
+ pk: user.pk,
395
+ firstLoginAt: user.firstLoginAt,
396
+ lastLoginAt: user.lastLoginAt,
397
+ },
398
+ ];
399
+ } else {
400
+ sourceProvider = 'wallet';
401
+ connectedAccounts.forEach((account) => {
402
+ if (account.id && blockletSk) {
403
+ const accountWallet = fromAppDid(account.id, blockletSk, types.RoleType.ROLE_ACCOUNT);
404
+ account.did = accountWallet.address;
405
+ account.pk = accountWallet.publicKey;
406
+ account.firstLoginAt = account.firstLoginAt || new Date().toISOString();
407
+ account.lastLoginAt = account.lastLoginAt || new Date().toISOString();
408
+ }
409
+ });
410
+ connectedAccounts.unshift({
411
+ provider: 'wallet',
412
+ did: user.did,
413
+ pk: user.pk,
414
+ firstLoginAt: user.firstLoginAt,
415
+ lastLoginAt: user.lastLoginAt,
416
+ });
417
+ }
418
+ const updatedUser = await this.update({
419
+ did: user.did,
420
+ pk: user.pk,
421
+ extraConfigs: {
422
+ sourceProvider,
423
+ connectedAccounts,
424
+ },
425
+ source: undefined,
426
+ });
427
+ return updatedUser;
428
+ },
429
+ 3: () => {
430
+ return user;
431
+ },
432
+ };
433
+ return versionMap[version]();
434
+ }
435
+
299
436
  /**
300
437
  * get user by did
301
438
  * @param {string} did user's did
302
439
  * @returns {BlockletUser}
303
440
  */
304
- async getUser(did) {
305
- const doc = await this.findOne({ did });
306
- return doc;
441
+ async getUser(did, { enableConnectedAccout = false, enableNormalize = false, blockletSk } = {}) {
442
+ let user = await this.findOne({ did });
443
+ if (enableConnectedAccout) {
444
+ if (!user) {
445
+ user = await this.findOne({
446
+ 'extraConfigs.connectedAccounts.did': did,
447
+ });
448
+ } else if (user.extraConfig?.bindDid) {
449
+ // @deprecated 已经不存在 bindDid 字段
450
+ ({ user } = await this.findOne({ did: user.extraConfig.bindDid }));
451
+ }
452
+ }
453
+ if (enableNormalize) {
454
+ if (!blockletSk) {
455
+ throw new Error('Normalize user must provide blockletSk params');
456
+ }
457
+ return this.normalize(user, { blockletSk });
458
+ }
459
+ return user;
307
460
  }
308
461
 
309
462
  /**
@@ -335,6 +488,82 @@ class User extends BaseState {
335
488
 
336
489
  return doc;
337
490
  }
491
+
492
+ /**
493
+ * 用户登录
494
+ * @param {Object} user
495
+ * @param {string} user.did
496
+ * @param {string} user.pk
497
+ * @param {ConnectedAccount} user.connectedAccount
498
+ * @param {passport} user.passport
499
+ * @param {string} user.fullName - user profile's name
500
+ * @param {string} user.avatar - url of user's avatar, eg: bn://avatar/7f8848569405f8cdf8b1b2788ebf7d0f.jpg
501
+ * @param {string} user.locale - locale
502
+ * @param {Object} [user.extra] - extra data of user
503
+ * @param {string} user.lastLoginIp - lastLoginIp
504
+ * @param {('owner'|'admin'|'member'|'guest'|string)} user.role - deprecated user's role
505
+ */
506
+ async login(_user) {
507
+ const { error, value: user } = loginSchema.validate(_user);
508
+ if (error) {
509
+ throw new Error(error);
510
+ }
511
+
512
+ let updatedUser;
513
+ let action = '';
514
+ const now = new Date().toISOString();
515
+ const oldUser = await this.getUser(user.did, {
516
+ enableConnectedAccout: true,
517
+ });
518
+ const cloneData = cloneDeep(user);
519
+ const passports = upsertToPassports(
520
+ oldUser?.passports || [],
521
+ cloneData.passport && {
522
+ ...cloneData.passport,
523
+ lastLoginAt: now,
524
+ }
525
+ );
526
+ const mergeData = {
527
+ ...pick(cloneData, ['type', 'fullName', 'email', 'avatar', 'role', 'locale', 'extra', 'lastLoginIp', 'remark']),
528
+ did: user.did,
529
+ pk: user.pk,
530
+ passports,
531
+ lastLoginAt: now,
532
+ extraConfigs: {
533
+ sourceProvider: 'wallet',
534
+ connectedAccounts: [],
535
+ },
536
+ };
537
+
538
+ if (oldUser) {
539
+ // update
540
+ action = 'update';
541
+ const extraConfigs = {
542
+ sourceProvider: oldUser?.extraConfigs?.sourceProvider || 'wallet',
543
+ connectedAccounts: updateConnectedAccount(
544
+ oldUser?.extraConfigs?.connectedAccounts || [],
545
+ cloneData.connectedAccount
546
+ ),
547
+ };
548
+ mergeData.extraConfigs = extraConfigs;
549
+ updatedUser = await this.update(mergeData);
550
+ } else {
551
+ // insert
552
+ action = 'add';
553
+ mergeData.firstLoginAt = now;
554
+ mergeData.approved = true;
555
+ const connectedAccount = Array.isArray(cloneData.connectedAccount)
556
+ ? cloneData.connectedAccount.filter(Boolean)[0]
557
+ : cloneData.connectedAccount;
558
+
559
+ mergeData.extraConfigs = {
560
+ sourceProvider: connectedAccount.provider,
561
+ connectedAccounts: updateConnectedAccount([], cloneData.connectedAccount),
562
+ };
563
+ updatedUser = await this.add(mergeData);
564
+ }
565
+ return { ...updatedUser, _action: action };
566
+ }
338
567
  }
339
568
 
340
569
  module.exports = User;
@@ -8,6 +8,8 @@ const os = require('os');
8
8
  const pRetry = require('p-retry');
9
9
  const tar = require('tar');
10
10
  const get = require('lodash/get');
11
+ const uniq = require('lodash/uniq');
12
+ const toLower = require('lodash/toLower');
11
13
  const isEmpty = require('lodash/isEmpty');
12
14
  const streamToPromise = require('stream-to-promise');
13
15
  const { Throttle } = require('stream-throttle');
@@ -18,9 +20,10 @@ const isUrl = require('is-url');
18
20
  const semver = require('semver');
19
21
  const axios = require('@abtnode/util/lib/axios');
20
22
  const { stableStringify } = require('@arcblock/vc');
23
+ const { chainInfo: chainInfoSchema } = require('@arcblock/did-auth/lib/schema');
21
24
 
22
25
  const { fromSecretKey } = require('@ocap/wallet');
23
- const { toHex, toBase58, isHex, toDid } = require('@ocap/util');
26
+ const { toHex, toBase58, isHex, toDid, toAddress } = require('@ocap/util');
24
27
  const { isValid: isValidDid, isEthereumDid } = require('@arcblock/did');
25
28
  const logger = require('@abtnode/logger')('@abtnode/core:util:blocklet');
26
29
  const pm2 = require('@abtnode/util/lib/async-pm2');
@@ -78,6 +81,7 @@ const {
78
81
  isEnvShareable,
79
82
  getBlockletAppIdList,
80
83
  isBeforeInstalled,
84
+ getChainInfo,
81
85
  } = require('@blocklet/meta/lib/util');
82
86
  const toBlockletDid = require('@blocklet/meta/lib/did');
83
87
  const { titleSchema, descriptionSchema, logoSchema } = require('@blocklet/meta/lib/schema');
@@ -294,19 +298,19 @@ const getAppSystemEnvironments = (blocklet, nodeInfo) => {
294
298
  * 2. 如果没有,再拼接
295
299
  */
296
300
 
297
- let appUrl = '';
298
-
301
+ const pidDomain = getDidDomainForBlocklet({ appPid, didDomain: nodeInfo.didDomain });
299
302
  const domainAliases = get(blocklet, 'site.domainAliases') || [];
300
- const didDomainAlias = domainAliases.find(
301
- (item) => item.value.endsWith(nodeInfo.didDomain) || item.value.endsWith('did.staging.arcblock.io') // did.staging.arcblock.io 是旧 did domain, 但主要存在于比较旧的节点中, 需要做兼容
302
- );
303
303
 
304
- if (didDomainAlias) {
305
- appUrl = prettyURL(didDomainAlias.value, true);
306
- } else {
307
- appUrl = `https://${getDidDomainForBlocklet({ appPid, didDomain: nodeInfo.didDomain })}`;
304
+ let didDomain = domainAliases.find((item) => toLower(item.value) === toLower(pidDomain));
305
+
306
+ if (!didDomain) {
307
+ didDomain = domainAliases.find(
308
+ (item) => item.value.endsWith(nodeInfo.didDomain) || item.value.endsWith('did.staging.arcblock.io') // did.staging.arcblock.io 是旧 did domain, 但主要存在于比较旧的节点中, 需要做兼容
309
+ );
308
310
  }
309
311
 
312
+ const appUrl = didDomain ? prettyURL(didDomain.value, true) : `https://${pidDomain}`;
313
+
310
314
  return {
311
315
  BLOCKLET_DID: did, // BLOCKLET_DID is always same as BLOCKLET_APP_PID in structV2 application
312
316
  BLOCKLET_APP_SK: appSk,
@@ -884,6 +888,21 @@ const validateBlocklet = (blocklet) =>
884
888
  validateEngine(getBlockletEngineNameByPlatform(b.meta));
885
889
  });
886
890
 
891
+ const validateBlockletChainInfo = (blocklet) => {
892
+ const chainInfo = getChainInfo({
893
+ CHAIN_TYPE: blocklet.configObj[BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_CHAIN_TYPE],
894
+ CHAIN_ID: blocklet.configObj[BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_CHAIN_ID],
895
+ CHAIN_HOST: blocklet.configObj[BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_CHAIN_HOST],
896
+ });
897
+
898
+ const { error } = chainInfoSchema.validate(chainInfo);
899
+ if (error) {
900
+ throw error;
901
+ }
902
+
903
+ return chainInfo;
904
+ };
905
+
887
906
  const checkBlockletProcessHealthy = async (blocklet, { minConsecutiveTime, timeout } = {}) => {
888
907
  await forEachBlocklet(
889
908
  blocklet,
@@ -1885,6 +1904,41 @@ const updateBlockletFallbackLogo = async (blocklet) => {
1885
1904
  }
1886
1905
  };
1887
1906
 
1907
+ const ensureAppLogo = async (blocklet, blockletsDir) => {
1908
+ if (!blocklet) {
1909
+ return;
1910
+ }
1911
+
1912
+ if (
1913
+ blocklet.source === BlockletSource.custom &&
1914
+ (blocklet.children || [])[0]?.meta?.logo &&
1915
+ blocklet.children[0].env.appDir
1916
+ ) {
1917
+ const fileName = blocklet.children[0].meta.logo;
1918
+ const src = path.join(blocklet.children[0].env.appDir, fileName);
1919
+ const dist = path.join(getBundleDir(blockletsDir, blocklet.meta), fileName);
1920
+
1921
+ if (fs.existsSync(src)) {
1922
+ await fs.copy(src, dist);
1923
+ }
1924
+ }
1925
+ };
1926
+
1927
+ const getBlockletDidDomainList = (blocklet, nodeInfo) => {
1928
+ const domainAliases = [];
1929
+ const alsoKnownAs = getBlockletKnownAs(blocklet);
1930
+
1931
+ const dids = [blocklet.appPid, blocklet.appDid, ...alsoKnownAs].filter(Boolean).map((did) => toAddress(did));
1932
+
1933
+ uniq(dids).forEach((did) => {
1934
+ const domain = getDidDomainForBlocklet({ appPid: did, didDomain: nodeInfo.didDomain });
1935
+
1936
+ domainAliases.push({ value: domain, isProtected: true });
1937
+ });
1938
+
1939
+ return domainAliases;
1940
+ };
1941
+
1888
1942
  module.exports = {
1889
1943
  updateBlockletFallbackLogo,
1890
1944
  consumeServerlessNFT,
@@ -1897,6 +1951,7 @@ module.exports = {
1897
1951
  getComponentSystemEnvironments,
1898
1952
  getRuntimeEnvironments,
1899
1953
  validateBlocklet,
1954
+ validateBlockletChainInfo,
1900
1955
  fillBlockletConfigs,
1901
1956
  ensureBlockletExpanded,
1902
1957
  startBlockletProcess,
@@ -1941,4 +1996,6 @@ module.exports = {
1941
1996
  validateBlockletMeta,
1942
1997
  getBlockletKnownAs,
1943
1998
  getFixedBundleSource,
1999
+ ensureAppLogo,
2000
+ getBlockletDidDomainList,
1944
2001
  };
package/lib/util/ip.js CHANGED
@@ -1,9 +1,6 @@
1
1
  const getIp = require('@abtnode/util/lib/get-ip');
2
2
  const logger = require('@abtnode/logger')('@abtnode/core:ip');
3
3
 
4
- const doRpc = require('./rpc');
5
- const { memoizeAsync } = require('./index');
6
-
7
4
  let cache = null;
8
5
  const fetch = async () => {
9
6
  try {
@@ -25,9 +22,6 @@ const cron = {
25
22
  fn: fetch,
26
23
  };
27
24
 
28
- const lookup = memoizeAsync((ip) => doRpc({ command: 'lookup', ip }));
29
-
30
25
  module.exports.fetch = fetch;
31
26
  module.exports.get = get;
32
27
  module.exports.cron = cron;
33
- module.exports.lookup = lookup;