@abtnode/core 1.8.0 → 1.8.1

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/node.js CHANGED
@@ -7,6 +7,7 @@ const isGitpod = require('@abtnode/util/lib/is-gitpod');
7
7
  const getFolderSize = require('@abtnode/util/lib/get-folder-size');
8
8
  const canPackageReadWrite = require('@abtnode/util/lib/can-pkg-rw');
9
9
  const { toDelegateAddress } = require('@arcblock/did-util');
10
+ const { STORE_DETAIL_PAGE_PATH_PREFIX } = require('@abtnode/constant');
10
11
 
11
12
  const logger = require('@abtnode/logger')('@abtnode/core:api:node');
12
13
 
@@ -40,7 +41,19 @@ class NodeAPI {
40
41
  // eslint-disable-next-line no-unused-vars
41
42
  async addRegistry({ url }, context) {
42
43
  logger.info('add registry', { url });
43
- const sanitized = sanitizeUrl(url);
44
+
45
+ const urlObj = new URL(url);
46
+ let newUrl = urlObj.origin;
47
+
48
+ // if the pathname is store blocklet list or blocklet detail
49
+ if (urlObj.pathname?.includes(STORE_DETAIL_PAGE_PATH_PREFIX)) {
50
+ const lastIndex = urlObj.pathname.lastIndexOf(STORE_DETAIL_PAGE_PATH_PREFIX);
51
+ const pathnamePrefix = urlObj.pathname.substring(0, lastIndex);
52
+ newUrl = `${newUrl}${pathnamePrefix || ''}`;
53
+ }
54
+
55
+ const sanitized = sanitizeUrl(newUrl);
56
+
44
57
  const info = await this.state.read();
45
58
  const exist = info.blockletRegistryList.find((x) => x.url === sanitized);
46
59
  if (exist) {
@@ -22,6 +22,7 @@ const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager');
22
22
  const downloadFile = require('@abtnode/util/lib/download-file');
23
23
  const Lock = require('@abtnode/util/lib/lock');
24
24
  const { getVcFromPresentation } = require('@abtnode/util/lib/vc');
25
+ const handleInstanceInStore = require('@abtnode/util/lib/public-to-store');
25
26
  const { VC_TYPE_BLOCKLET_PURCHASE } = require('@abtnode/constant');
26
27
 
27
28
  const getBlockletEngine = require('@blocklet/meta/lib/engine');
@@ -216,6 +217,7 @@ class BlockletManager extends BaseBlockletManager {
216
217
  * @param {{
217
218
  * url: string;
218
219
  * sync: boolean = false;
220
+ * downloadToken: string;
219
221
  * }} params
220
222
  * @param {{
221
223
  * [key: string]: any
@@ -223,9 +225,22 @@ class BlockletManager extends BaseBlockletManager {
223
225
  * @return {*}
224
226
  * @memberof BlockletManager
225
227
  */
226
- async install(params, context) {
228
+ async install(params, context = {}) {
227
229
  logger.debug('install blocklet', { params, context });
228
230
 
231
+ if (params.downloadToken) {
232
+ const info = await states.node.read();
233
+
234
+ context.headers = Object.assign(context?.headers || {}, {
235
+ 'x-server-did': info.did,
236
+ 'x-download-token': params.downloadToken,
237
+ 'x-server-publick-key': info.pk,
238
+ 'x-server-signature': sign(info.did, info.sk, {
239
+ exp: (Date.now() + 5 * 60 * 1000) / 1000,
240
+ }),
241
+ });
242
+ }
243
+
229
244
  const source = getSourceFromInstallParams(params);
230
245
  if (typeof context.startImmediately === 'undefined') {
231
246
  context.startImmediately = !!params.startImmediately;
@@ -319,10 +334,12 @@ class BlockletManager extends BaseBlockletManager {
319
334
  }
320
335
  }
321
336
 
337
+ const { inStore, registryUrl } = await parseSourceUrl(url);
338
+
322
339
  const blocklet = await states.blocklet.getBlocklet(meta.did);
323
340
  const isRunning = blocklet ? blocklet.status === BlockletStatus.running : false;
324
341
 
325
- return { meta, isFree, isInstalled: !!blocklet, isRunning };
342
+ return { meta, isFree, isInstalled: !!blocklet, isRunning, inStore, registryUrl };
326
343
  }
327
344
 
328
345
  async installBlockletFromVc({ vcPresentation, challenge }, context) {
@@ -423,7 +440,7 @@ class BlockletManager extends BaseBlockletManager {
423
440
 
424
441
  await this.deleteProcess({ did });
425
442
  const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.error);
426
- this.emit(BlockletEvents.startFailed, res);
443
+ this.emit(BlockletEvents.startFailed, { ...res, error: { message: error.message } });
427
444
 
428
445
  if (throwOnError) {
429
446
  throw new Error(description);
@@ -601,7 +618,7 @@ class BlockletManager extends BaseBlockletManager {
601
618
  async deleteComponent({ did, rootDid, keepData, keepState }, context) {
602
619
  logger.info('delete blocklet component', { did, rootDid, keepData });
603
620
 
604
- const blocklet = await this.ensureBlocklet(rootDid);
621
+ const blocklet = await this.ensureBlocklet(rootDid, { validateEnv: false });
605
622
  const child = blocklet.children.find((x) => x.meta.did === did);
606
623
  if (!child) {
607
624
  throw new Error('Component does not exist');
@@ -851,6 +868,16 @@ class BlockletManager extends BaseBlockletManager {
851
868
  return newState;
852
869
  }
853
870
 
871
+ async configPublicToStore({ did, publicToStore = false }) {
872
+ const blocklet = await this.ensureBlocklet(did);
873
+
874
+ await handleInstanceInStore(blocklet, { publicToStore });
875
+ await states.blockletExtras.setSettings(did, { publicToStore });
876
+
877
+ const newState = await this.ensureBlocklet(did);
878
+ return newState;
879
+ }
880
+
854
881
  /**
855
882
  * upgrade blocklet from registry
856
883
  */
@@ -1495,7 +1522,12 @@ class BlockletManager extends BaseBlockletManager {
1495
1522
 
1496
1523
  preDownloadLock.release();
1497
1524
 
1498
- this.emit(BlockletEvents.downloadFailed, { meta: did });
1525
+ this.emit(BlockletEvents.downloadFailed, {
1526
+ meta: { did },
1527
+ error: {
1528
+ message: err.message,
1529
+ },
1530
+ });
1499
1531
  states.notification.create({
1500
1532
  title: 'Blocklet Download Failed',
1501
1533
  description: `Blocklet ${name}@${version} download failed with error: ${err.message}`,
@@ -1786,10 +1818,12 @@ class BlockletManager extends BaseBlockletManager {
1786
1818
  throw new Error('Root blocklet does not exist');
1787
1819
  }
1788
1820
 
1821
+ const { inStore } = await parseSourceUrl(url);
1822
+
1789
1823
  const meta = await getBlockletMetaFromUrl(url);
1790
1824
 
1791
- // 如果是一个付费的blocklet,需要携带token才能下载成功
1792
- if (!isFreeBlocklet(meta)) {
1825
+ // 如果是一个付费的blocklet,并且url来源为Store, 需要携带token才能下载成功
1826
+ if (!isFreeBlocklet(meta) && inStore) {
1793
1827
  const info = await states.node.read();
1794
1828
 
1795
1829
  // eslint-disable-next-line no-param-reassign
@@ -2625,7 +2659,12 @@ class BlockletManager extends BaseBlockletManager {
2625
2659
  logger.error('failed to install blocklet', { name, did, version, error: err });
2626
2660
  try {
2627
2661
  await this._rollback('install', did, oldBlocklet);
2628
- this.emit(BlockletEvents.installFailed, { meta: { did } });
2662
+ this.emit(BlockletEvents.installFailed, {
2663
+ meta: { did },
2664
+ error: {
2665
+ message: err.message,
2666
+ },
2667
+ });
2629
2668
  states.notification.create({
2630
2669
  title: 'Blocklet Install Failed',
2631
2670
  description: `Blocklet ${meta.name}@${meta.version} install failed with error: ${err.message}`,
package/lib/cert.js CHANGED
@@ -103,14 +103,16 @@ class Cert extends EventEmitter {
103
103
 
104
104
  async add(data) {
105
105
  if (!data.certificate || !data.privateKey) {
106
- throw new Error('certificate and privateKey is required');
106
+ throw new Error('certificate and privateKey are required');
107
107
  }
108
108
 
109
109
  Cert.fixCertificate(data);
110
110
 
111
- await this.manager.add(data);
112
- logger.info('add certificate result', { name: data.name });
113
- this.emit('cert.added', data);
111
+ const result = await this.manager.add(data);
112
+ logger.info('add certificate result', { name: result.name });
113
+ this.emit('cert.added', result);
114
+
115
+ return result;
114
116
  }
115
117
 
116
118
  async issue({ domain }) {
@@ -120,7 +122,10 @@ class Cert extends EventEmitter {
120
122
  }
121
123
 
122
124
  async upsertByDomain(data) {
123
- return this.manager.upsertByDomain(data);
125
+ const result = await this.manager.upsertByDomain(data);
126
+ this.emit('cert.updated', result);
127
+
128
+ return result;
124
129
  }
125
130
 
126
131
  async update(data) {
package/lib/event.js CHANGED
@@ -5,6 +5,7 @@ const { wipeSensitiveData } = require('@blocklet/meta/lib/util');
5
5
  const logger = require('@abtnode/logger')('@abtnode/core:event');
6
6
  const { BLOCKLET_MODES, BlockletStatus, BlockletSource, BlockletEvents } = require('@blocklet/meta/lib/constants');
7
7
  const { EVENTS } = require('@abtnode/constant');
8
+ const handleInstanceInStore = require('@abtnode/util/lib/public-to-store');
8
9
 
9
10
  const eventHub =
10
11
  process.env.NODE_ENV === 'test' ? require('@arcblock/event-hub/single') : require('@arcblock/event-hub');
@@ -160,6 +161,13 @@ module.exports = ({
160
161
  await handleBlockletUpgrade(eventName, payload);
161
162
  } else if ([BlockletEvents.removed].includes(eventName)) {
162
163
  await handleBlockletRemove(eventName, payload);
164
+ } else if ([BlockletEvents.started].includes(eventName)) {
165
+ try {
166
+ const blocklet = await blockletManager.ensureBlocklet(payload.meta.did);
167
+ await handleInstanceInStore(blocklet);
168
+ } catch (error) {
169
+ logger.error('handleInstanceInStore failed', { error });
170
+ }
163
171
  }
164
172
 
165
173
  if (payload.blocklet && !payload.meta) {
package/lib/index.js CHANGED
@@ -226,6 +226,7 @@ function ABTNode(options) {
226
226
  getLatestBlockletVersion: blockletManager.getLatestBlockletVersion.bind(blockletManager),
227
227
  getBlockletMetaFromUrl: blockletManager.getMetaFromUrl.bind(blockletManager),
228
228
  resetBlocklet: blockletManager.reset.bind(blockletManager),
229
+ configPublicToStore: blockletManager.configPublicToStore.bind(blockletManager),
229
230
 
230
231
  deleteBlockletProcess: blockletManager.deleteProcess.bind(blockletManager),
231
232
 
@@ -434,7 +434,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
434
434
  isProtected: true,
435
435
  });
436
436
 
437
- logger.info('dashboard certificate updated');
437
+ logger.info('dashboard certificate updated', { domain });
438
438
  } catch (error) {
439
439
  logger.error('update dashboard certificate failed', { error });
440
440
  throw error;
@@ -1103,6 +1103,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1103
1103
  certManager.on('cert.added', () => providers[providerName].reload());
1104
1104
  certManager.on('cert.removed', () => providers[providerName].reload());
1105
1105
  certManager.on('cert.issued', () => providers[providerName].reload());
1106
+ certManager.on('cert.updated', () => providers[providerName].reload());
1106
1107
 
1107
1108
  await providers[providerName].start();
1108
1109
  }
@@ -8,7 +8,6 @@ const path = require('path');
8
8
  const os = require('os');
9
9
  const fse = require('fs-extra');
10
10
  const get = require('lodash/get');
11
- const { Certificate, PrivateKey } = require('@fidm/x509');
12
11
  const { EventEmitter } = require('events');
13
12
  const uuid = require('uuid');
14
13
  const isUrl = require('is-url');
@@ -564,39 +563,6 @@ class RouterManager extends EventEmitter {
564
563
  }
565
564
  }
566
565
 
567
- validateCertificate(cert, domain) {
568
- const certificate = Certificate.fromPEM(cert.certificate);
569
- const privateKey = PrivateKey.fromPEM(cert.privateKey);
570
-
571
- const data = Buffer.allocUnsafe(100);
572
- const signature = privateKey.sign(data, 'sha512');
573
- if (!certificate.publicKey.verify(data, signature, 'sha512')) {
574
- throw new Error('Invalid certificate: signature verify failed');
575
- }
576
-
577
- const certDomain = get(certificate, 'subject.commonName', '');
578
- if (domain && domain !== certDomain) {
579
- throw new Error('Invalid certificate: domain does not match');
580
- }
581
-
582
- const validFrom = get(certificate, 'validFrom', '');
583
- if (!validFrom || new Date(validFrom).getTime() > Date.now()) {
584
- throw new Error('Invalid certificate: not in valid period');
585
- }
586
- const validTo = get(certificate, 'validTo', '');
587
- if (!validTo || new Date(validTo).getTime() < Date.now()) {
588
- throw new Error('Invalid certificate: not in valid period');
589
- }
590
-
591
- return certificate;
592
- }
593
-
594
- fixCertificate(entity) {
595
- // Hack: this logic exists because gql does not allow line breaks in arg values
596
- entity.privateKey = entity.privateKey.split('|').join('\n');
597
- entity.certificate = entity.certificate.split('|').join('\n');
598
- }
599
-
600
566
  fixRootBlockletRule(rule) {
601
567
  if (!rule.id) {
602
568
  rule.id = uuid.v4();
@@ -114,7 +114,11 @@ const getLogContent = async (action, args, context, result, info, node) => {
114
114
  return `upgraded blocklet ${getBlockletInfo(result, info)} to v${result.meta.version}`;
115
115
  case 'updateChildBlocklets':
116
116
  return `upgraded components for blocklet ${getBlockletInfo(result, info)}`;
117
-
117
+ case 'configPublicToStore':
118
+ if (args.publicToStore) {
119
+ return `set publicToStore to true for blocklet ${getBlockletInfo(result, info)}`;
120
+ }
121
+ return `set publicToStore to false for blocklet ${getBlockletInfo(result, info)}`;
118
122
  // store
119
123
  case 'addBlockletStore':
120
124
  return `added blocklet store ${args.url}`;
@@ -237,6 +241,7 @@ const getLogCategory = (action) => {
237
241
  case 'configBlocklet':
238
242
  case 'upgradeBlocklet':
239
243
  case 'updateChildBlocklets':
244
+ case 'configPublicToStore':
240
245
  return 'blocklet';
241
246
 
242
247
  // store
@@ -10,7 +10,13 @@ class SessionState extends BaseState {
10
10
 
11
11
  this.db.ensureIndex({ fieldName: 'createdAt' }, (error) => {
12
12
  if (error) {
13
- logger.error('ensure index failed', { error });
13
+ logger.error('ensure createdAt index failed', { error });
14
+ }
15
+ });
16
+
17
+ this.db.ensureIndex({ fieldName: 'expireDate', expireAfterSeconds: 0 }, (error) => {
18
+ if (error) {
19
+ logger.error('ensure expireDate index failed', { error });
14
20
  }
15
21
  });
16
22
  }
@@ -934,11 +934,6 @@ const pruneBlockletBundle = async ({ blocklets, installDir, blockletSettings })
934
934
 
935
935
  const fillAppDirs = async (dir, level = 'root') => {
936
936
  if (level === 'version') {
937
- if (!fs.existsSync(path.join(dir, 'blocklet.yml'))) {
938
- logger.error('blocklet.yml does not exist in blocklet bundle dir', { dir });
939
- return;
940
- }
941
-
942
937
  appDirs.push({
943
938
  key: path.relative(installDir, dir),
944
939
  dir,
package/lib/util/index.js CHANGED
@@ -398,7 +398,10 @@ const validateUrl = async (url, expectedHttpResTypes = ['application/json', 'tex
398
398
  throw new Error(`Cannot get content-type from ${url}: ${err.message}`);
399
399
  }
400
400
 
401
- if (expectedHttpResTypes.some((x) => res.headers['content-type'].includes(x)) === false) {
401
+ if (
402
+ res.headers['content-type'] &&
403
+ expectedHttpResTypes.some((x) => res.headers['content-type'].includes(x)) === false
404
+ ) {
402
405
  throw new Error(`Unexpected content-type from ${url}: ${res.headers['content-type']}`);
403
406
  }
404
407
 
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.8.0",
6
+ "version": "1.8.1",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,17 +19,17 @@
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
- "@abtnode/certificate-manager": "1.8.0",
23
- "@abtnode/constant": "1.8.0",
24
- "@abtnode/cron": "1.8.0",
25
- "@abtnode/db": "1.8.0",
26
- "@abtnode/logger": "1.8.0",
27
- "@abtnode/queue": "1.8.0",
28
- "@abtnode/rbac": "1.8.0",
29
- "@abtnode/router-provider": "1.8.0",
30
- "@abtnode/static-server": "1.8.0",
31
- "@abtnode/timemachine": "1.8.0",
32
- "@abtnode/util": "1.8.0",
22
+ "@abtnode/certificate-manager": "1.8.1",
23
+ "@abtnode/constant": "1.8.1",
24
+ "@abtnode/cron": "1.8.1",
25
+ "@abtnode/db": "1.8.1",
26
+ "@abtnode/logger": "1.8.1",
27
+ "@abtnode/queue": "1.8.1",
28
+ "@abtnode/rbac": "1.8.1",
29
+ "@abtnode/router-provider": "1.8.1",
30
+ "@abtnode/static-server": "1.8.1",
31
+ "@abtnode/timemachine": "1.8.1",
32
+ "@abtnode/util": "1.8.1",
33
33
  "@arcblock/did": "1.17.0",
34
34
  "@arcblock/did-motif": "^1.1.10",
35
35
  "@arcblock/did-util": "1.17.0",
@@ -37,10 +37,10 @@
37
37
  "@arcblock/jwt": "^1.17.0",
38
38
  "@arcblock/pm2-events": "^0.0.5",
39
39
  "@arcblock/vc": "1.17.0",
40
- "@blocklet/meta": "1.8.0",
40
+ "@blocklet/meta": "1.8.1",
41
41
  "@fidm/x509": "^1.2.1",
42
- "@nedb/core": "^1.2.2",
43
- "@nedb/multi": "^1.2.2",
42
+ "@nedb/core": "^1.3.1",
43
+ "@nedb/multi": "^1.3.1",
44
44
  "@ocap/mcrypto": "1.17.0",
45
45
  "@ocap/util": "1.17.0",
46
46
  "@ocap/wallet": "1.17.0",
@@ -81,5 +81,5 @@
81
81
  "express": "^4.17.1",
82
82
  "jest": "^27.4.5"
83
83
  },
84
- "gitHead": "6446a85fb33721abc24bb1045d59158e6b96c241"
84
+ "gitHead": "c970b8a386bebd7fe6dbc8b8eedf8bd8328b4bb5"
85
85
  }