@abtnode/core 1.8.31 → 1.8.32

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
@@ -13,8 +13,8 @@ const logger = require('@abtnode/logger')('@abtnode/core:api:node');
13
13
 
14
14
  const IP = require('../util/ip');
15
15
  const { validateNodeInfo, validateUpdateGateway } = require('../validators/node');
16
- const BlockletRegistry = require('../blocklet/registry');
17
16
  const { getAll } = require('../blocklet/manager/engine');
17
+ const StoreUtil = require('../util/store');
18
18
  const { getDelegateState } = require('../util');
19
19
 
20
20
  const sanitizeUrl = (url) => {
@@ -59,9 +59,9 @@ class NodeAPI {
59
59
  throw new Error(`Blocklet registry already exist: ${sanitized}`);
60
60
  }
61
61
 
62
- await BlockletRegistry.validateRegistryURL(sanitized);
62
+ await StoreUtil.validateRegistryURL(sanitized);
63
63
 
64
- const newBlockletRegistry = await BlockletRegistry.getRegistryMeta(sanitized);
64
+ const newBlockletRegistry = await StoreUtil.getRegistryMeta(sanitized);
65
65
  const newBlockletRegistryList = info.blockletRegistryList.map((x) => ({ ...x, selected: false }));
66
66
  newBlockletRegistryList.push({ ...newBlockletRegistry, url: sanitized, selected: true, protected: false });
67
67
 
package/lib/api/team.js CHANGED
@@ -10,6 +10,7 @@ const {
10
10
  WHO_CAN_ACCESS,
11
11
  PASSPORT_STATUS,
12
12
  WELLKNOWN_SERVICE_PATH_PREFIX,
13
+ MAX_USER_PAGE_SIZE,
13
14
  } = require('@abtnode/constant');
14
15
  const { isValid: isValidDid } = require('@arcblock/did');
15
16
  const { BlockletEvents } = require('@blocklet/constant');
@@ -30,8 +31,6 @@ const { validateCreatePermission, validateUpdatePermission } = require('../valid
30
31
 
31
32
  const { getBlocklet } = require('../util/blocklet');
32
33
 
33
- const MAX_USER_PAGE_SIZE = 100;
34
-
35
34
  const validateReservedRole = (role) => {
36
35
  if (Object.values(ROLES).includes(role)) {
37
36
  throw new Error(`The role ${role} is reserved`);
@@ -126,14 +125,33 @@ class TeamAPI extends EventEmitter {
126
125
  return doc;
127
126
  }
128
127
 
129
- async getUsers({ teamDid, query, paging: inputPaging, sort }) {
128
+ async getUsers({ teamDid, query, paging: inputPaging, sort, dids }) {
130
129
  const state = await this.getUserState(teamDid);
131
130
 
132
131
  if (inputPaging?.pageSize > MAX_USER_PAGE_SIZE) {
133
132
  throw new Error(`Length of users should not exceed ${MAX_USER_PAGE_SIZE} per page`);
134
133
  }
135
134
 
136
- const { list, paging } = await state.getUsers({ query, sort, paging: { pageSize: 20, ...inputPaging } });
135
+ if (dids && dids.length > MAX_USER_PAGE_SIZE) {
136
+ throw new Error(`Length of users should not exceed ${MAX_USER_PAGE_SIZE}`);
137
+ }
138
+
139
+ let list;
140
+ let paging;
141
+
142
+ if (dids) {
143
+ list = await state.getUsersByDids({ query, dids });
144
+ paging = {
145
+ total: list.length,
146
+ pageSize: dids.length,
147
+ pageCount: 1,
148
+ page: 1,
149
+ };
150
+ } else {
151
+ const doc = await state.getUsers({ query, sort, paging: { pageSize: 20, ...inputPaging } });
152
+ list = doc.list;
153
+ paging = doc.paging;
154
+ }
137
155
 
138
156
  return {
139
157
  users: list.map(
@@ -23,7 +23,7 @@ const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager');
23
23
  const downloadFile = require('@abtnode/util/lib/download-file');
24
24
  const Lock = require('@abtnode/util/lib/lock');
25
25
  const { getVcFromPresentation } = require('@abtnode/util/lib/vc');
26
- const { VC_TYPE_BLOCKLET_PURCHASE, WHO_CAN_ACCESS } = require('@abtnode/constant');
26
+ const { VC_TYPE_BLOCKLET_PURCHASE, WHO_CAN_ACCESS, SERVER_ROLES } = require('@abtnode/constant');
27
27
 
28
28
  const getBlockletEngine = require('@blocklet/meta/lib/engine');
29
29
  const {
@@ -38,6 +38,7 @@ const {
38
38
  forEachChild,
39
39
  getComponentId,
40
40
  getComponentBundleId,
41
+ isExternalBlocklet,
41
42
  } = require('@blocklet/meta/lib/util');
42
43
  const getComponentProcessId = require('@blocklet/meta/lib/get-component-process-id');
43
44
  const validateBlockletEntry = require('@blocklet/meta/lib/entry');
@@ -47,6 +48,8 @@ const { update: updateMetaFile } = require('@blocklet/meta/lib/file');
47
48
  const { titleSchema, descriptionSchema, mountPointSchema, logoSchema } = require('@blocklet/meta/lib/schema');
48
49
  const hasReservedKey = require('@blocklet/meta/lib/has-reserved-key');
49
50
 
51
+ const { toExternalBlocklet } = toBlockletDid;
52
+
50
53
  const {
51
54
  BlockletStatus,
52
55
  BlockletSource,
@@ -105,9 +108,8 @@ const {
105
108
  getBlocklet,
106
109
  ensureEnvDefault,
107
110
  } = require('../../util/blocklet');
108
- const { parseSourceUrl } = require('../../util/registry');
111
+ const StoreUtil = require('../../util/store');
109
112
  const states = require('../../states');
110
- const BlockletRegistry = require('../registry');
111
113
  const BaseBlockletManager = require('./base');
112
114
  const { get: getEngine } = require('./engine');
113
115
  const blockletPm2Events = require('./pm2-events');
@@ -169,11 +171,14 @@ const getSkippedProcessIds = ({ newBlocklet, oldBlocklet, context = {} }) => {
169
171
  return res;
170
172
  };
171
173
 
174
+ const getBlockletIndex = (meta, controller) =>
175
+ controller ? toExternalBlocklet(meta.name, controller.id) : { did: meta.did, name: meta.name };
176
+
172
177
  class BlockletManager extends BaseBlockletManager {
173
178
  /**
174
179
  * @param {*} dataDirs generate by ../../util:getDataDirs
175
180
  */
176
- constructor({ dataDirs, registry, startQueue, installQueue, daemon = false, teamManager }) {
181
+ constructor({ dataDirs, startQueue, installQueue, daemon = false, teamManager }) {
177
182
  super();
178
183
 
179
184
  this.dataDirs = dataDirs;
@@ -190,7 +195,6 @@ class BlockletManager extends BaseBlockletManager {
190
195
  * { [download-did-version]: Lock }
191
196
  */
192
197
  this.downloadLocks = {};
193
- this.registry = registry;
194
198
 
195
199
  // cached installed blocklets for performance
196
200
  this.cachedBlocklets = null;
@@ -198,7 +202,7 @@ class BlockletManager extends BaseBlockletManager {
198
202
  // cached blocklet latest versions from each registries
199
203
  this.cachedBlockletVersions = new LRU({
200
204
  max: 40, // cache at most 40 blocklets
201
- maxAge: process.env.NODE_ENV === 'test' ? 500 : 5 * 60 * 1000, // cache for 5 minute
205
+ maxAge: process.env.NODE_ENV === 'test' ? 500 : 0.5 * 60 * 1000, // cache for 0.5 minute
202
206
  });
203
207
 
204
208
  if (daemon) {
@@ -216,8 +220,17 @@ class BlockletManager extends BaseBlockletManager {
216
220
  *
217
221
  * @param {{
218
222
  * url: string;
219
- * sync: boolean = false;
220
- * downloadTokenList: Array<{did: string, token: string};
223
+ * file: string;
224
+ * diffVersion: string;
225
+ * deleteSet: Array<string>;
226
+ * did: string;
227
+ * title: string;
228
+ * description: string;
229
+ * sync: boolean = false; // download synchronously, not use queue
230
+ * delay: number; // push download task to queue after a delay
231
+ * downloadTokenList: Array<{did: string, token: string}>;
232
+ * startImmediately: boolean;
233
+ * controller: Controller
221
234
  * }} params
222
235
  * @param {{
223
236
  * [key: string]: any
@@ -245,7 +258,8 @@ class BlockletManager extends BaseBlockletManager {
245
258
  }
246
259
 
247
260
  if (source === BlockletSource.url) {
248
- return this._installFromUrl({ url: params.url, sync: params.sync }, context);
261
+ const { url, controller, sync, delay } = params;
262
+ return this._installFromUrl({ url, controller, sync, delay }, context);
249
263
  }
250
264
 
251
265
  if (source === BlockletSource.upload) {
@@ -254,7 +268,8 @@ class BlockletManager extends BaseBlockletManager {
254
268
  }
255
269
 
256
270
  if (source === BlockletSource.registry) {
257
- return this._installFromStore({ did: params.did }, context);
271
+ const { did, controller, sync, delay } = params;
272
+ return this._installFromStore({ did, controller, sync, delay }, context);
258
273
  }
259
274
 
260
275
  if (source === BlockletSource.custom) {
@@ -343,7 +358,7 @@ class BlockletManager extends BaseBlockletManager {
343
358
 
344
359
  if (checkPrice && !isFree && meta.nftFactory) {
345
360
  try {
346
- const registryMeta = await BlockletRegistry.getRegistryMeta(new URL(url).origin);
361
+ const registryMeta = await StoreUtil.getRegistryMeta(new URL(url).origin);
347
362
 
348
363
  if (registryMeta.chainHost) {
349
364
  const state = await getFactoryState(registryMeta.chainHost, meta.nftFactory);
@@ -356,12 +371,33 @@ class BlockletManager extends BaseBlockletManager {
356
371
  }
357
372
  }
358
373
 
359
- const { inStore, registryUrl } = await parseSourceUrl(url);
374
+ const { inStore, registryUrl } = await StoreUtil.parseSourceUrl(url);
375
+
376
+ return { meta, isFree, inStore, registryUrl };
377
+ }
378
+
379
+ async getBlockletByBundle({ did, name }, context) {
380
+ if (toBlockletDid(name) !== did) {
381
+ throw new Error('did and name does not match');
382
+ }
383
+
384
+ if (!context?.user) {
385
+ throw new Error('user does not exist');
386
+ }
387
+
388
+ let blockletDid = did;
389
+ let isExternal = false;
390
+
391
+ if (context.user.role === SERVER_ROLES.EXTERNAL_BLOCKLET_CONTROLLER) {
392
+ blockletDid = toExternalBlocklet(name, context.user.did, { didOnly: true });
393
+ isExternal = true;
394
+ }
395
+
396
+ const blocklet = await states.blocklet.getBlocklet(blockletDid);
360
397
 
361
- const blocklet = await states.blocklet.getBlocklet(meta.did);
362
398
  const isRunning = blocklet ? blocklet.status === BlockletStatus.running : false;
363
399
 
364
- return { meta, isFree, isInstalled: !!blocklet, isRunning, inStore, registryUrl };
400
+ return { isInstalled: !!blocklet, isRunning, blockletDid, isExternal };
365
401
  }
366
402
 
367
403
  async installBlockletFromVc({ vcPresentation, challenge }, context) {
@@ -388,6 +424,15 @@ class BlockletManager extends BaseBlockletManager {
388
424
  const blocklet = await this.ensureBlocklet(did, { e2eMode });
389
425
 
390
426
  try {
427
+ // blocklet may be manually stopped durning starting
428
+ // so error message would not be sent if blocklet is stopped
429
+ // so we need update status first
430
+ await states.blocklet.setBlockletStatus(did, BlockletStatus.starting);
431
+ blocklet.status = BlockletStatus.starting;
432
+
433
+ // validate requirement and engine
434
+ await validateBlocklet(blocklet);
435
+
391
436
  if (!hasRunnableComponent(blocklet)) {
392
437
  throw new Error('No runnable component found');
393
438
  }
@@ -400,9 +445,6 @@ class BlockletManager extends BaseBlockletManager {
400
445
  );
401
446
  }
402
447
 
403
- // update status
404
- await states.blocklet.setBlockletStatus(did, BlockletStatus.starting);
405
- blocklet.status = BlockletStatus.starting;
406
448
  this.emit(BlockletEvents.statusChange, blocklet);
407
449
 
408
450
  if (blocklet.mode === BLOCKLET_MODES.DEVELOPMENT) {
@@ -811,36 +853,46 @@ class BlockletManager extends BaseBlockletManager {
811
853
  return this.attachRuntimeInfo({ did, nodeInfo, diskInfo: true, context });
812
854
  }
813
855
 
814
- async list({ includeRuntimeInfo = true, useCache = true } = {}, context) {
856
+ async list({ includeRuntimeInfo = true, useCache = true, filter } = {}, context) {
815
857
  const blocklets = await states.blocklet.getBlocklets();
816
- if (!includeRuntimeInfo) {
817
- return blocklets;
818
- }
858
+ let list = blocklets;
859
+
860
+ if (includeRuntimeInfo) {
861
+ const nodeInfo = await states.node.read();
862
+ const updated = (
863
+ await Promise.all(
864
+ blocklets.map((x) => {
865
+ if (isBeforeInstalled(x.status)) {
866
+ return x;
867
+ }
819
868
 
820
- const nodeInfo = await states.node.read();
821
- const updated = (
822
- await Promise.all(
823
- blocklets.map((x) => {
824
- if (isBeforeInstalled(x.status)) {
825
- return x;
826
- }
869
+ const cachedBlocklet =
870
+ useCache && this.cachedBlocklets ? this.cachedBlocklets.find((y) => y.meta.did === x.meta.did) : null;
827
871
 
828
- const cachedBlocklet =
829
- useCache && this.cachedBlocklets ? this.cachedBlocklets.find((y) => y.meta.did === x.meta.did) : null;
872
+ return this.attachRuntimeInfo({
873
+ did: x.meta.did,
874
+ nodeInfo,
875
+ diskInfo: false,
876
+ context,
877
+ cachedBlocklet,
878
+ });
879
+ })
880
+ )
881
+ ).filter(Boolean);
830
882
 
831
- return this.attachRuntimeInfo({
832
- did: x.meta.did,
833
- nodeInfo,
834
- diskInfo: false,
835
- context,
836
- cachedBlocklet,
837
- });
838
- })
839
- )
840
- ).filter(Boolean);
883
+ this.cachedBlocklets = cloneDeep(updated);
884
+ list = updated;
885
+ }
841
886
 
842
- this.cachedBlocklets = cloneDeep(updated);
843
- return updated;
887
+ if (filter === 'external-excluded') {
888
+ return list.filter((x) => !isExternalBlocklet(x));
889
+ }
890
+
891
+ if (filter === 'external-only') {
892
+ return list.filter(isExternalBlocklet);
893
+ }
894
+
895
+ return list;
844
896
  }
845
897
 
846
898
  // eslint-disable-next-line no-unused-vars
@@ -1067,13 +1119,11 @@ class BlockletManager extends BaseBlockletManager {
1067
1119
 
1068
1120
  const upgradeFromRegistry = registryUrl || blocklet.deployedFrom;
1069
1121
 
1070
- const newVersionMeta = await this.registry.getBlockletMeta(
1071
- {
1072
- did: blocklet.meta.bundleDid,
1073
- registryUrl: upgradeFromRegistry,
1074
- },
1075
- context
1076
- );
1122
+ let newVersionMeta = await StoreUtil.getBlockletMeta({
1123
+ did: blocklet.meta.bundleDid,
1124
+ registryUrl: upgradeFromRegistry,
1125
+ });
1126
+ newVersionMeta = ensureMeta(newVersionMeta, { name: blocklet.meta.name, did: blocklet.meta.did });
1077
1127
 
1078
1128
  function getSignature(signatures = [], oldSignatures = []) {
1079
1129
  // if blocklet installed from local, upload, url, the signature is undefined, should return null
@@ -1359,13 +1409,24 @@ class BlockletManager extends BaseBlockletManager {
1359
1409
 
1360
1410
  const oldBlocklet = { children: children.filter((x) => x.dynamic) }; // let downloader skip re-downloading dynamic blocklet
1361
1411
  await this._downloadBlocklet(added, oldBlocklet);
1362
- await states.blocklet.setBlockletStatus(did, BlockletStatus.installed);
1363
1412
 
1364
- // Add environments
1413
+ // Add Config
1365
1414
  await this._setConfigsFromMeta(did);
1415
+ let blocklet = await this.ensureBlocklet(did);
1416
+
1417
+ // pre install
1418
+ await this._runPreInstallHook(blocklet);
1419
+
1420
+ // Add environments
1366
1421
  await this.updateBlockletEnvironment(did);
1422
+ blocklet = await this.ensureBlocklet(did);
1367
1423
 
1368
- const blocklet = await this.ensureBlocklet(did);
1424
+ // post install
1425
+ await this._runPostInstallHook(blocklet);
1426
+
1427
+ await states.blocklet.setBlockletStatus(did, BlockletStatus.installed);
1428
+
1429
+ blocklet = await this.ensureBlocklet(did);
1369
1430
 
1370
1431
  await fs.writeFile(path.join(blocklet.env.dataDir, 'logo.svg'), createDidLogo(blocklet.meta.did));
1371
1432
 
@@ -1397,36 +1458,44 @@ class BlockletManager extends BaseBlockletManager {
1397
1458
  }
1398
1459
 
1399
1460
  const defaultPath = formatName(meta.name);
1400
- const children = [
1401
- {
1402
- meta: ensureMeta(meta),
1403
- mountPoint: mountPoint || `/${defaultPath}`,
1404
- source: BlockletSource.local,
1405
- deployedFrom: folder,
1406
- status: BlockletStatus.installed,
1407
- mode: BLOCKLET_MODES.DEVELOPMENT,
1408
- dynamic: true,
1409
- children: await parseChildrenFromMeta(meta, { ancestors: [{ mountPoint: mountPoint || `/${defaultPath}` }] }),
1410
- },
1411
- ];
1412
- await validateBlocklet(children[0]);
1413
- await states.blocklet.addChildren(rootDid, children);
1461
+ const component = {
1462
+ meta: ensureMeta(meta),
1463
+ mountPoint: mountPoint || `/${defaultPath}`,
1464
+ source: BlockletSource.local,
1465
+ deployedFrom: folder,
1466
+ status: BlockletStatus.installed,
1467
+ mode: BLOCKLET_MODES.DEVELOPMENT,
1468
+ dynamic: true,
1469
+ children: await parseChildrenFromMeta(meta, { ancestors: [{ mountPoint: mountPoint || `/${defaultPath}` }] }),
1470
+ };
1471
+ await validateBlocklet(component);
1472
+ await states.blocklet.addChildren(rootDid, [component]);
1414
1473
 
1415
1474
  const newBlocklet = await states.blocklet.getBlocklet(rootDid);
1416
1475
  await this._downloadBlocklet(newBlocklet, existRoot);
1417
1476
  await states.blocklet.setBlockletStatus(rootDid, BlockletStatus.stopped);
1418
1477
 
1419
- await this._upsertDynamicNavigation(existRoot.meta.did, { skipNavigation });
1478
+ await this._upsertDynamicNavigation(existRoot.meta.did, component, { skipNavigation });
1420
1479
 
1421
- // Add environments
1480
+ // Add Config
1422
1481
  await this._setConfigsFromMeta(rootDid);
1482
+ let blocklet = await this.ensureBlocklet(rootDid);
1483
+
1484
+ // pre install
1485
+ await this._runPreInstallHook(blocklet);
1486
+
1487
+ // Add environments
1423
1488
  await this.updateBlockletEnvironment(rootDid);
1489
+ blocklet = await this.ensureBlocklet(rootDid);
1490
+
1491
+ // post install
1492
+ await this._runPostInstallHook(blocklet);
1424
1493
 
1425
1494
  logger.info('add blocklet component for dev', { did, version, meta });
1426
1495
 
1427
- const rootBlocklet = await this.ensureBlocklet(rootDid);
1496
+ blocklet = await this.ensureBlocklet(rootDid);
1428
1497
 
1429
- return rootBlocklet;
1498
+ return blocklet;
1430
1499
  }
1431
1500
 
1432
1501
  async ensureBlocklet(did, opts = {}) {
@@ -1881,13 +1950,15 @@ class BlockletManager extends BaseBlockletManager {
1881
1950
  * did: string;
1882
1951
  * registry: string;
1883
1952
  * sync: boolean;
1953
+ * delay: number;
1954
+ * controller: Controller;
1884
1955
  * }} params
1885
1956
  * @param {*} context
1886
1957
  * @return {*}
1887
1958
  * @memberof BlockletManager
1888
1959
  */
1889
1960
  async _installFromStore(params, context) {
1890
- const { did, registry, sync } = params;
1961
+ const { did, registry, sync, delay, controller } = params;
1891
1962
 
1892
1963
  logger.debug('start install blocklet', { did });
1893
1964
  if (!isValidDid(did)) {
@@ -1895,67 +1966,96 @@ class BlockletManager extends BaseBlockletManager {
1895
1966
  }
1896
1967
 
1897
1968
  const registryUrl = registry || (await states.node.getBlockletRegistry());
1898
- const info = await BlockletRegistry.getRegistryMeta(registryUrl);
1899
- const meta = await this.registry.getBlocklet(did, registryUrl);
1969
+ const info = await StoreUtil.getRegistryMeta(registryUrl);
1970
+ const meta = await StoreUtil.getBlockletMeta({ did, registryUrl });
1900
1971
  if (!meta) {
1901
1972
  throw new Error('Can not install blocklet that not found in registry');
1902
1973
  }
1903
1974
 
1904
- const state = await states.blocklet.getBlocklet(meta.did);
1975
+ const { did: blockletDid, name: blockletName } = getBlockletIndex(meta, controller);
1976
+
1977
+ const state = await states.blocklet.getBlocklet(blockletDid);
1978
+
1905
1979
  if (state) {
1906
1980
  throw new Error('Can not install an already installed blocklet');
1907
1981
  }
1908
1982
 
1983
+ if (controller?.vcId) {
1984
+ // sometimes nedb will throw error if use states.blocklet.count({ ['controller.vcId']: controller.vcId })
1985
+ const blocklets = await states.blocklet.find({}, { controller: 1 });
1986
+ const count = blocklets.filter((x) => x.controller?.vcId === controller.vcId).length;
1987
+
1988
+ if (count >= (controller.appMaxCount || 1)) {
1989
+ throw new Error(
1990
+ `You can only install ${controller.appMaxCount} blocklet with this credential: ${controller.vcId}`
1991
+ );
1992
+ }
1993
+ }
1994
+
1909
1995
  // install
1910
1996
  return this._install({
1911
- meta,
1997
+ meta: ensureMeta(meta, { did: blockletDid, name: blockletName }),
1912
1998
  source: BlockletSource.registry,
1913
1999
  deployedFrom: info.cdnUrl || registryUrl,
1914
2000
  sync,
2001
+ delay,
2002
+ controller,
1915
2003
  context,
1916
2004
  });
1917
2005
  }
1918
2006
 
1919
2007
  /**
1920
- *
2008
+ * @type {{
2009
+ * id: string;
2010
+ * vcId: string;
2011
+ * expireDate: Date;
2012
+ * }} Controller
1921
2013
  *
1922
2014
  * @param {{
1923
2015
  * url: string;
1924
2016
  * sync: boolean;
2017
+ * delay: number;
2018
+ * controller: Controller;
1925
2019
  * }} params
1926
2020
  * @param {{}} context
1927
2021
  * @return {*}
1928
2022
  * @memberof BlockletManager
1929
2023
  */
1930
2024
  async _installFromUrl(params, context) {
1931
- const { url, sync } = params;
2025
+ const { url, sync, delay, controller } = params;
1932
2026
 
1933
2027
  logger.debug('start install blocklet', { url });
1934
2028
 
1935
- const { inStore, registryUrl, blockletDid } = await parseSourceUrl(url);
2029
+ const bundleMeta = await getBlockletMetaFromUrl(url);
2030
+
2031
+ if (!bundleMeta) {
2032
+ throw new Error(`Can not install blocklet that not found by url: ${url}`);
2033
+ }
2034
+
2035
+ const { did: blockletDid, name: blockletName } = getBlockletIndex(bundleMeta, controller);
2036
+
2037
+ // install from store if url is a store url
2038
+ const { inStore, registryUrl, blockletDid: bundleDid } = await StoreUtil.parseSourceUrl(url, controller);
1936
2039
  if (inStore) {
1937
2040
  const exist = await states.blocklet.getBlocklet(blockletDid);
1938
2041
  if (exist) {
1939
- return this.upgrade({ did: blockletDid, registryUrl }, context);
2042
+ return this.upgrade({ did: blockletDid, registryUrl, sync, delay }, context);
1940
2043
  }
1941
2044
 
1942
- return this._installFromStore({ did: blockletDid, registry: registryUrl }, context);
2045
+ return this._installFromStore({ did: bundleDid, registry: registryUrl, controller, sync, delay }, context);
1943
2046
  }
1944
2047
 
1945
- const meta = await getBlockletMetaFromUrl(url);
1946
-
1947
- if (!meta) {
1948
- throw new Error(`Can not install blocklet that not found by url: ${url}`);
1949
- }
2048
+ const meta = ensureMeta(bundleMeta, { name: blockletName, did: blockletDid });
1950
2049
 
1951
2050
  // upgrade
1952
- const exist = await states.blocklet.getBlocklet(meta.did);
2051
+ const exist = await states.blocklet.getBlocklet(blockletDid);
1953
2052
  if (exist) {
1954
2053
  return this._upgrade({
1955
2054
  meta,
1956
2055
  source: BlockletSource.url,
1957
2056
  deployedFrom: url,
1958
2057
  sync,
2058
+ delay,
1959
2059
  context,
1960
2060
  });
1961
2061
  }
@@ -1966,6 +2066,8 @@ class BlockletManager extends BaseBlockletManager {
1966
2066
  source: BlockletSource.url,
1967
2067
  deployedFrom: url,
1968
2068
  sync,
2069
+ delay,
2070
+ controller,
1969
2071
  context,
1970
2072
  });
1971
2073
  }
@@ -1987,7 +2089,7 @@ class BlockletManager extends BaseBlockletManager {
1987
2089
  throw new Error('Root blocklet does not exist');
1988
2090
  }
1989
2091
 
1990
- const { inStore } = await parseSourceUrl(url);
2092
+ const { inStore } = await StoreUtil.parseSourceUrl(url);
1991
2093
 
1992
2094
  const meta = await getBlockletMetaFromUrl(url);
1993
2095
 
@@ -2125,7 +2227,7 @@ class BlockletManager extends BaseBlockletManager {
2125
2227
  .pipe(writeStream);
2126
2228
  readStream.on('error', (error) => {
2127
2229
  logger.error('File upload read stream failed', { error });
2128
- writeStream.destroy(new Error('File upload read stream failed'));
2230
+ writeStream.destroy(new Error(`File upload read stream failed, ${error.message}`));
2129
2231
  });
2130
2232
  writeStream.on('error', (error) => {
2131
2233
  logger.error('File upload write stream failed', { error });
@@ -2437,12 +2539,14 @@ class BlockletManager extends BaseBlockletManager {
2437
2539
  * deployedFrom: string;
2438
2540
  * context: {}
2439
2541
  * sync: boolean = false;
2542
+ * delay: number;
2543
+ * controller: Controller;
2440
2544
  * }} params
2441
2545
  * @return {*}
2442
2546
  * @memberof BlockletManager
2443
2547
  */
2444
2548
  async _install(params) {
2445
- const { meta, source, deployedFrom, context, sync } = params;
2549
+ const { meta, source, deployedFrom, context, sync, delay, controller } = params;
2446
2550
 
2447
2551
  validateBlockletMeta(meta, { ensureDist: true });
2448
2552
 
@@ -2458,6 +2562,7 @@ class BlockletManager extends BaseBlockletManager {
2458
2562
  source,
2459
2563
  deployedFrom,
2460
2564
  children,
2565
+ controller,
2461
2566
  });
2462
2567
 
2463
2568
  await validateBlocklet(blocklet);
@@ -2493,31 +2598,33 @@ class BlockletManager extends BaseBlockletManager {
2493
2598
  return states.blocklet.getBlocklet(did);
2494
2599
  }
2495
2600
 
2496
- const ticket = this.installQueue.push(
2497
- {
2498
- entity: 'blocklet',
2499
- action: 'download',
2500
- id: did,
2501
- ...downloadParams,
2502
- },
2503
- did
2504
- );
2505
- ticket.on('failed', async (err) => {
2506
- logger.error('failed to install blocklet', { name, did, version, error: err });
2507
- try {
2508
- await this._rollback('install', did, { extraState: oldExtraState });
2509
- } catch (e) {
2510
- logger.error('failed to remove blocklet on install error', { did: meta.did, error: e });
2511
- }
2601
+ setTimeout(() => {
2602
+ const ticket = this.installQueue.push(
2603
+ {
2604
+ entity: 'blocklet',
2605
+ action: 'download',
2606
+ id: did,
2607
+ ...downloadParams,
2608
+ },
2609
+ did
2610
+ );
2611
+ ticket.on('failed', async (err) => {
2612
+ logger.error('failed to install blocklet', { name, did, version, error: err });
2613
+ try {
2614
+ await this._rollback('install', did, { extraState: oldExtraState });
2615
+ } catch (e) {
2616
+ logger.error('failed to remove blocklet on install error', { did: meta.did, error: e });
2617
+ }
2512
2618
 
2513
- states.notification.create({
2514
- title: 'Blocklet Install Failed',
2515
- description: `Blocklet ${name}@${version} install failed with error: ${err.message || 'queue exception'}`,
2516
- entityType: 'blocklet',
2517
- entityId: did,
2518
- severity: 'error',
2619
+ states.notification.create({
2620
+ title: 'Blocklet Install Failed',
2621
+ description: `Blocklet ${name}@${version} install failed with error: ${err.message || 'queue exception'}`,
2622
+ entityType: 'blocklet',
2623
+ entityId: did,
2624
+ severity: 'error',
2625
+ });
2519
2626
  });
2520
- });
2627
+ }, delay || 0);
2521
2628
 
2522
2629
  return blocklet1;
2523
2630
  } catch (err) {
@@ -2780,33 +2887,14 @@ class BlockletManager extends BaseBlockletManager {
2780
2887
  }
2781
2888
 
2782
2889
  // pre install
2783
- const nodeEnvironments = await states.node.getEnvironments();
2784
- const preInstall = (b) =>
2785
- hooks.preInstall(b.env.processId, {
2786
- hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
2787
- env: { ...nodeEnvironments },
2788
- appDir: b.env.appDir,
2789
- did, // root blocklet did
2790
- notification: states.notification,
2791
- context,
2792
- });
2793
- await forEachBlocklet(blocklet, preInstall, { parallel: true });
2890
+ await this._runPreInstallHook(blocklet, context);
2794
2891
 
2795
2892
  // Add environments
2796
2893
  await this.updateBlockletEnvironment(meta.did);
2797
2894
  blocklet = await this.ensureBlocklet(did);
2798
2895
 
2799
2896
  // post install
2800
- const postInstall = (b, { ancestors }) =>
2801
- hooks.postInstall(b.env.processId, {
2802
- hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
2803
- env: getRuntimeEnvironments(b, nodeEnvironments, ancestors),
2804
- appDir: b.env.appDir,
2805
- did, // root blocklet did
2806
- notification: states.notification,
2807
- context,
2808
- });
2809
- await forEachBlocklet(blocklet, postInstall, { parallel: true });
2897
+ await this._runPostInstallHook(blocklet, context);
2810
2898
 
2811
2899
  await states.blocklet.setBlockletStatus(did, BlockletStatus.installed);
2812
2900
  blocklet = await this.ensureBlocklet(did);
@@ -2892,33 +2980,14 @@ class BlockletManager extends BaseBlockletManager {
2892
2980
  let blocklet = await this.ensureBlocklet(did);
2893
2981
 
2894
2982
  // pre install
2895
- const nodeEnvironments = await states.node.getEnvironments();
2896
- const preInstall = (b) =>
2897
- hooks.preInstall(b.env.processId, {
2898
- hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
2899
- env: { ...nodeEnvironments },
2900
- appDir: b.env.appDir,
2901
- did, // root blocklet did
2902
- notification: states.notification,
2903
- context,
2904
- });
2905
- await forEachBlocklet(blocklet, preInstall, { parallel: true });
2983
+ await this._runPreInstallHook(blocklet, context);
2906
2984
 
2907
2985
  // Add environments
2908
2986
  await this.updateBlockletEnvironment(did);
2909
2987
  blocklet = await this.ensureBlocklet(did);
2910
2988
 
2911
2989
  // post install
2912
- const postInstall = (b, { ancestors }) =>
2913
- hooks.postInstall(b.env.processId, {
2914
- hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
2915
- env: getRuntimeEnvironments(b, nodeEnvironments, ancestors),
2916
- appDir: b.env.appDir,
2917
- did, // root blocklet did
2918
- notification: states.notification,
2919
- context,
2920
- });
2921
- await forEachBlocklet(blocklet, postInstall, { parallel: true });
2990
+ await this._runPostInstallHook(blocklet, context);
2922
2991
 
2923
2992
  logger.info('start migration');
2924
2993
  try {
@@ -2926,6 +2995,7 @@ class BlockletManager extends BaseBlockletManager {
2926
2995
  forEachBlockletSync(oldBlocklet, (b, { id }) => {
2927
2996
  oldVersions[id] = b.meta.version;
2928
2997
  });
2998
+ const nodeEnvironments = await states.node.getEnvironments();
2929
2999
  const runMigration = (b, { id, ancestors }) => {
2930
3000
  return runMigrationScripts({
2931
3001
  blocklet: b,
@@ -3275,11 +3345,7 @@ class BlockletManager extends BaseBlockletManager {
3275
3345
  logger.info('Download Blocklet', { name, did, bundles: metas.map((x) => get(x, 'dist.tarball')) });
3276
3346
  const tasks = [];
3277
3347
  for (const meta of metas) {
3278
- const url = await this.registry.resolveTarballURL({
3279
- did,
3280
- tarball: get(meta, 'dist.tarball'),
3281
- registryUrl: blocklet.source === BlockletSource.registry ? blocklet.deployedFrom : undefined,
3282
- });
3348
+ const url = meta.dist.tarball;
3283
3349
  tasks.push(this._downloadBundle(meta, did, url, context));
3284
3350
  }
3285
3351
  const results = await Promise.all(tasks);
@@ -3473,24 +3539,20 @@ class BlockletManager extends BaseBlockletManager {
3473
3539
  }
3474
3540
 
3475
3541
  async _getLatestBlockletVersionFromStore({ blocklet, version }) {
3542
+ const { deployedFrom: registryUrl } = blocklet;
3476
3543
  const { did, bundleDid } = blocklet.meta;
3477
3544
 
3478
3545
  let versions = this.cachedBlockletVersions.get(did);
3479
3546
 
3480
3547
  if (!versions) {
3481
- const { blockletRegistryList } = await states.node.read();
3482
- const tasks = blockletRegistryList.map((registry) =>
3483
- this.registry
3484
- .getBlockletMeta({ did: bundleDid, registryUrl: registry.url })
3485
- .then((item) => ({ did, version: item.version, registryUrl: registry.url }))
3486
- .catch((error) => {
3487
- if (error.response && error.response.status === 404) {
3488
- return;
3489
- }
3490
- logger.error('get blocklet meta from registry failed', { did, error });
3491
- })); // prettier-ignore
3548
+ const item = await StoreUtil.getBlockletMeta({ did: bundleDid, registryUrl });
3549
+
3550
+ if (!item) {
3551
+ return null;
3552
+ }
3553
+
3554
+ versions = [{ did, version: item.version }];
3492
3555
 
3493
- versions = await Promise.all(tasks);
3494
3556
  this.cachedBlockletVersions.set(did, versions);
3495
3557
  }
3496
3558
 
@@ -3543,6 +3605,38 @@ class BlockletManager extends BaseBlockletManager {
3543
3605
 
3544
3606
  return versions[0];
3545
3607
  }
3608
+
3609
+ async _runPreInstallHook(blocklet, context) {
3610
+ const nodeEnvironments = await states.node.getEnvironments();
3611
+
3612
+ const preInstall = (b) =>
3613
+ hooks.preInstall(b.env.processId, {
3614
+ hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
3615
+ env: { ...nodeEnvironments },
3616
+ appDir: b.env.appDir,
3617
+ did: blocklet.meta.did, // root blocklet did
3618
+ notification: states.notification,
3619
+ context,
3620
+ });
3621
+
3622
+ await forEachBlocklet(blocklet, preInstall, { parallel: true });
3623
+ }
3624
+
3625
+ async _runPostInstallHook(blocklet, context) {
3626
+ const nodeEnvironments = await states.node.getEnvironments();
3627
+
3628
+ const postInstall = (b, { ancestors }) =>
3629
+ hooks.postInstall(b.env.processId, {
3630
+ hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
3631
+ env: getRuntimeEnvironments(b, nodeEnvironments, ancestors),
3632
+ appDir: b.env.appDir,
3633
+ did: blocklet.meta.did, // root blocklet did
3634
+ notification: states.notification,
3635
+ context,
3636
+ });
3637
+
3638
+ await forEachBlocklet(blocklet, postInstall, { parallel: true });
3639
+ }
3546
3640
  }
3547
3641
 
3548
3642
  module.exports = BlockletManager;
package/lib/index.js CHANGED
@@ -11,7 +11,6 @@ const { listProviders } = require('@abtnode/router-provider');
11
11
  const { DEFAULT_CERTIFICATE_EMAIL, EVENTS } = require('@abtnode/constant');
12
12
 
13
13
  const RoutingSnapshot = require('./states/routing-snapshot');
14
- const BlockletRegistry = require('./blocklet/registry');
15
14
  const BlockletManager = require('./blocklet/manager/disk');
16
15
  const RouterManager = require('./router/manager');
17
16
  const getRouterHelpers = require('./router/helper');
@@ -27,6 +26,7 @@ const DomainStatus = require('./util/domain-status');
27
26
  const Upgrade = require('./util/upgrade');
28
27
  const resetNode = require('./util/reset-node');
29
28
  const DiskMonitor = require('./util/disk-monitor');
29
+ const StoreUtil = require('./util/store');
30
30
  const createQueue = require('./queue');
31
31
  const createEvents = require('./event');
32
32
  const pm2Events = require('./blocklet/manager/pm2-events');
@@ -123,15 +123,12 @@ function ABTNode(options) {
123
123
  },
124
124
  });
125
125
 
126
- const blockletRegistry = new BlockletRegistry();
127
-
128
126
  const teamManager = new TeamManager({ nodeDid: options.nodeDid, dataDirs, states });
129
127
 
130
128
  const blockletManager = new BlockletManager({
131
129
  dataDirs,
132
130
  startQueue,
133
131
  installQueue,
134
- registry: blockletRegistry,
135
132
  daemon: options.daemon,
136
133
  teamManager,
137
134
  });
@@ -207,6 +204,7 @@ function ABTNode(options) {
207
204
  upgradeComponents: blockletManager.updateChildren.bind(blockletManager),
208
205
  getLatestBlockletVersion: blockletManager.getLatestBlockletVersion.bind(blockletManager),
209
206
  getBlockletMetaFromUrl: blockletManager.getMetaFromUrl.bind(blockletManager),
207
+ getBlockletByBundle: blockletManager.getBlockletByBundle.bind(blockletManager),
210
208
  resetBlocklet: blockletManager.reset.bind(blockletManager),
211
209
  deleteBlockletProcess: blockletManager.deleteProcess.bind(blockletManager),
212
210
  configPublicToStore: blockletManager.configPublicToStore.bind(blockletManager),
@@ -226,9 +224,9 @@ function ABTNode(options) {
226
224
  updateAllBlockletEnvironment: blockletManager.updateAllBlockletEnvironment.bind(blockletManager),
227
225
  setBlockletInitialized: blockletManager.setInitialized.bind(blockletManager),
228
226
 
229
- // Registry
230
- getBlockletMeta: blockletRegistry.getBlockletMeta.bind(blockletRegistry),
231
- getRegistryMeta: BlockletRegistry.getRegistryMeta,
227
+ // Store
228
+ getBlockletMeta: StoreUtil.getBlockletMeta,
229
+ getRegistryMeta: StoreUtil.getRegistryMeta,
232
230
 
233
231
  // Node State
234
232
  getNodeInfo: nodeAPI.getInfo.bind(nodeAPI),
@@ -33,3 +33,6 @@ All Blocklet Server states are managed by files in this folder.
33
33
  - `children`: component of app, same structure as parent, can be nested
34
34
  - `mountPoint` mount path of web site. _component only_
35
35
  - `dynamic` is dynamically added. _component only_
36
+ - `controller`
37
+ - `tokens`
38
+ - `downloadTokenList`
@@ -23,6 +23,7 @@ const logger = require('@abtnode/logger')('state-blocklet');
23
23
  const BaseState = require('./base');
24
24
  const { checkDuplicateComponents, ensureMeta } = require('../util/blocklet');
25
25
  const { validateBlockletMeta } = require('../util');
26
+ const { validateBlockletController } = require('../validators/blocklet');
26
27
 
27
28
  const lock = new Lock('blocklet-port-assign-lock');
28
29
 
@@ -180,11 +181,12 @@ class BlockletState extends BaseState {
180
181
  deployedFrom = '',
181
182
  mode = BLOCKLET_MODES.PRODUCTION,
182
183
  children: rawChildren = [],
184
+ controller,
183
185
  } = {}) {
184
186
  return this.getBlocklet(meta.did).then(
185
- (doc) =>
187
+ (exist) =>
186
188
  new Promise(async (resolve, reject) => {
187
- if (doc) {
189
+ if (exist) {
188
190
  reject(new Error('Blocklet already added'));
189
191
  return;
190
192
  }
@@ -208,29 +210,33 @@ class BlockletState extends BaseState {
208
210
 
209
211
  fixChildren(children);
210
212
 
213
+ const data = {
214
+ appDid: null, // will updated later when updating blocklet environments
215
+ mode,
216
+ meta: sanitized,
217
+ status,
218
+ source,
219
+ deployedFrom,
220
+ ports,
221
+ environments: [],
222
+ children,
223
+ controller,
224
+ };
225
+
226
+ if (controller) {
227
+ data.controller = await validateBlockletController(controller);
228
+ }
229
+
211
230
  // add to db
212
- this.insert(
213
- {
214
- appDid: null, // will updated later when updating blocklet environments
215
- mode,
216
- meta: sanitized,
217
- status,
218
- source,
219
- deployedFrom,
220
- ports,
221
- environments: [],
222
- children,
223
- },
224
- (err, newDoc) => {
225
- lock.release();
226
- if (err) {
227
- return reject(err);
228
- }
229
-
230
- this.emit('add', newDoc);
231
- return resolve(newDoc);
231
+ this.insert(data, (err, doc) => {
232
+ lock.release();
233
+ if (err) {
234
+ return reject(err);
232
235
  }
233
- );
236
+
237
+ this.emit('add', doc);
238
+ return resolve(doc);
239
+ });
234
240
  } catch (err) {
235
241
  lock.release();
236
242
  reject(err);
@@ -124,6 +124,22 @@ class User extends BaseState {
124
124
  };
125
125
  }
126
126
 
127
+ async getUsersByDids({ dids, query } = {}) {
128
+ const { approved } = query || {};
129
+ const didList = dids || [];
130
+
131
+ const queryParam = {
132
+ did: { $in: didList },
133
+ };
134
+
135
+ if (!isNullOrUndefined(approved)) {
136
+ queryParam.approved = !!approved;
137
+ }
138
+
139
+ // get data
140
+ return this.find(queryParam);
141
+ }
142
+
127
143
  async getUser(did) {
128
144
  const doc = await this.findOne({ did });
129
145
  return doc;
@@ -1,8 +1,10 @@
1
1
  const semver = require('semver');
2
2
 
3
- let { version: actualVersion } = require('../../package.json');
3
+ let { version: actualServerVersion } = require('../../package.json');
4
4
 
5
- const defaults = { abtnode: '>= 1.1.0', os: '*', cpu: '*' };
5
+ const { version: actualNodejsVersion } = process;
6
+
7
+ const defaults = { abtnode: '>= 1.1.0', os: '*', cpu: '*', nodejs: '*' };
6
8
 
7
9
  const isSatisfied = (requirements, throwOnUnsatisfied = true) => {
8
10
  if (!requirements || typeof requirements !== 'object') {
@@ -10,15 +12,23 @@ const isSatisfied = (requirements, throwOnUnsatisfied = true) => {
10
12
  }
11
13
 
12
14
  const { platform: actualOs, arch: actualCpu } = process;
13
- const { os: expectedOs, cpu: expectedCpu, abtnode, server } = Object.assign(defaults, requirements);
14
- const expectedVersion = server || abtnode;
15
+ const {
16
+ os: expectedOs,
17
+ cpu: expectedCpu,
18
+ abtnode,
19
+ server,
20
+ nodejs: expectedNodejsVersion,
21
+ } = Object.assign(defaults, requirements);
22
+ const expectedServerVersion = server || abtnode;
15
23
 
16
24
  const isOsSatisfied =
17
25
  expectedOs === '*' || expectedOs === actualOs || (Array.isArray(expectedOs) && expectedOs.includes(actualOs));
18
26
  const isCpuSatisfied =
19
27
  expectedCpu === '*' || expectedCpu === actualCpu || (Array.isArray(expectedCpu) && expectedCpu.includes(actualCpu));
20
28
 
21
- const isVersionSatisfied = semver.satisfies(semver.coerce(actualVersion), expectedVersion);
29
+ const isServerVersionSatisfied = semver.satisfies(semver.coerce(actualServerVersion), expectedServerVersion);
30
+
31
+ const isNodejsVersionSatisfied = semver.satisfies(semver.coerce(actualNodejsVersion), expectedNodejsVersion);
22
32
 
23
33
  if (throwOnUnsatisfied) {
24
34
  if (isOsSatisfied === false) {
@@ -27,19 +37,25 @@ const isSatisfied = (requirements, throwOnUnsatisfied = true) => {
27
37
  if (isCpuSatisfied === false) {
28
38
  throw new Error(`Your blocklet is not supported on architecture ${actualCpu}`);
29
39
  }
30
- if (isVersionSatisfied === false) {
40
+ if (isServerVersionSatisfied === false) {
41
+ throw new Error(
42
+ `Expected server version for the blocklet is ${expectedServerVersion}, but your server version is ${actualServerVersion}, please upgrade or downgrade your server to continue.`
43
+ );
44
+ }
45
+ if (isNodejsVersionSatisfied === false) {
31
46
  throw new Error(
32
- `Expected server version for the blocklet is ${expectedVersion}, but your server version is ${actualVersion}, please upgrade or downgrade your server to continue.`
47
+ `Expected Nodejs version for the blocklet is ${expectedNodejsVersion}, but your Nodejs version is ${actualNodejsVersion}, please upgrade or downgrade Nodejs and restart your server.`
33
48
  );
34
49
  }
35
50
  }
36
51
 
37
- return isOsSatisfied && isCpuSatisfied && isVersionSatisfied;
52
+ return isOsSatisfied && isCpuSatisfied && isServerVersionSatisfied;
38
53
  };
39
54
 
40
55
  // Just for test
41
- isSatisfied._setActualVersion = (d) => {
42
- actualVersion = d;
56
+ isSatisfied._setActualServerVersion = (d) => {
57
+ actualServerVersion = d;
43
58
  };
59
+ isSatisfied._actualNodejsVersion = actualNodejsVersion;
44
60
 
45
61
  module.exports = isSatisfied;
@@ -8,6 +8,7 @@ const { name } = require('../../package.json');
8
8
  const logger = require('@abtnode/logger')(`${name}:util:registry`); // eslint-disable-line
9
9
 
10
10
  const request = require('./request');
11
+ const { fixAndVerifyMetaFromStore } = require('./blocklet');
11
12
 
12
13
  const validateRegistryURL = async (registry) => {
13
14
  const url = joinURL(registry, BLOCKLET_STORE_API_PREFIX, `/blocklets.json?__t__=${Date.now()}`);
@@ -83,8 +84,58 @@ const parseSourceUrl = async (url) => {
83
84
  };
84
85
  };
85
86
 
87
+ const resolveTarballURL = async ({ did, tarball = '', registryUrl = '' }) => {
88
+ if (!tarball) {
89
+ return '';
90
+ }
91
+
92
+ if (tarball.startsWith('file://')) {
93
+ return decodeURIComponent(tarball);
94
+ }
95
+
96
+ if (tarball.startsWith('http://') || tarball.startsWith('https://')) {
97
+ return tarball;
98
+ }
99
+
100
+ if (!registryUrl) {
101
+ return '';
102
+ }
103
+
104
+ if (!did) {
105
+ return '';
106
+ }
107
+
108
+ return joinURL(registryUrl, 'api', 'blocklets', did, tarball);
109
+ };
110
+
111
+ const getBlockletMeta = async ({ did, registryUrl }) => {
112
+ const url = joinURL(registryUrl, BLOCKLET_STORE_API_PREFIX, `/blocklets/${did}/blocklet.json?__t__=${Date.now()}`);
113
+
114
+ const { data } = await request.get(url);
115
+ try {
116
+ if (data.did !== did) {
117
+ throw new Error('Invalid blocklet meta: did does not match');
118
+ }
119
+
120
+ const meta = fixAndVerifyMetaFromStore(data);
121
+
122
+ meta.dist.tarball = await resolveTarballURL({
123
+ did,
124
+ tarball: meta.dist.tarball,
125
+ registryUrl,
126
+ });
127
+
128
+ return meta;
129
+ } catch (err) {
130
+ logger.error('failed to get blocklet meta', { did, data, error: err });
131
+ throw err;
132
+ }
133
+ };
134
+
86
135
  module.exports = {
87
136
  validateRegistryURL,
88
137
  getRegistryMeta,
89
138
  parseSourceUrl,
139
+ getBlockletMeta,
140
+ resolveTarballURL,
90
141
  };
@@ -0,0 +1,15 @@
1
+ const JOI = require('joi');
2
+ const { didExtension } = require('@blocklet/meta/lib/extension');
3
+
4
+ const Joi = JOI.extend(didExtension);
5
+
6
+ const blockletController = Joi.object({
7
+ id: Joi.DID().required(), // userDid
8
+ vcId: Joi.DID().required(),
9
+ appMaxCount: Joi.number().required().min(1),
10
+ expireDate: Joi.date(),
11
+ }).options({ stripUnknown: true });
12
+
13
+ module.exports = {
14
+ validateBlockletController: (entity) => blockletController.validateAsync(entity),
15
+ };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.8.31",
6
+ "version": "1.8.32",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,32 +19,32 @@
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
- "@abtnode/auth": "1.8.31",
23
- "@abtnode/certificate-manager": "1.8.31",
24
- "@abtnode/constant": "1.8.31",
25
- "@abtnode/cron": "1.8.31",
26
- "@abtnode/db": "1.8.31",
27
- "@abtnode/logger": "1.8.31",
28
- "@abtnode/queue": "1.8.31",
29
- "@abtnode/rbac": "1.8.31",
30
- "@abtnode/router-provider": "1.8.31",
31
- "@abtnode/static-server": "1.8.31",
32
- "@abtnode/timemachine": "1.8.31",
33
- "@abtnode/util": "1.8.31",
34
- "@arcblock/did": "1.17.23",
22
+ "@abtnode/auth": "1.8.32",
23
+ "@abtnode/certificate-manager": "1.8.32",
24
+ "@abtnode/constant": "1.8.32",
25
+ "@abtnode/cron": "1.8.32",
26
+ "@abtnode/db": "1.8.32",
27
+ "@abtnode/logger": "1.8.32",
28
+ "@abtnode/queue": "1.8.32",
29
+ "@abtnode/rbac": "1.8.32",
30
+ "@abtnode/router-provider": "1.8.32",
31
+ "@abtnode/static-server": "1.8.32",
32
+ "@abtnode/timemachine": "1.8.32",
33
+ "@abtnode/util": "1.8.32",
34
+ "@arcblock/did": "1.18.1",
35
35
  "@arcblock/did-motif": "^1.1.10",
36
- "@arcblock/did-util": "1.17.23",
37
- "@arcblock/event-hub": "1.17.23",
38
- "@arcblock/jwt": "^1.17.23",
36
+ "@arcblock/did-util": "1.18.1",
37
+ "@arcblock/event-hub": "1.18.1",
38
+ "@arcblock/jwt": "^1.18.1",
39
39
  "@arcblock/pm2-events": "^0.0.5",
40
- "@arcblock/vc": "1.17.23",
41
- "@blocklet/constant": "1.8.31",
42
- "@blocklet/meta": "1.8.31",
43
- "@blocklet/sdk": "1.8.31",
40
+ "@arcblock/vc": "1.18.1",
41
+ "@blocklet/constant": "1.8.32",
42
+ "@blocklet/meta": "1.8.32",
43
+ "@blocklet/sdk": "1.8.32",
44
44
  "@fidm/x509": "^1.2.1",
45
- "@ocap/mcrypto": "1.17.23",
46
- "@ocap/util": "1.17.23",
47
- "@ocap/wallet": "1.17.23",
45
+ "@ocap/mcrypto": "1.18.1",
46
+ "@ocap/util": "1.18.1",
47
+ "@ocap/wallet": "1.18.1",
48
48
  "@slack/webhook": "^5.0.4",
49
49
  "axios": "^0.27.2",
50
50
  "axon": "^2.0.3",
@@ -81,5 +81,5 @@
81
81
  "express": "^4.18.2",
82
82
  "jest": "^27.5.1"
83
83
  },
84
- "gitHead": "f3cc8becdca1b8c5e645eb8cf751fa780cf09cfe"
84
+ "gitHead": "8502244aeda5926b3433c1dd9142e63233e3c23c"
85
85
  }
@@ -1,74 +0,0 @@
1
- const joinURL = require('url-join');
2
- const { BLOCKLET_STORE_API_PREFIX } = require('@abtnode/constant');
3
-
4
- const { name } = require('../../package.json');
5
-
6
- const logger = require('@abtnode/logger')(`${name}:blocklet:registry`); // eslint-disable-line
7
-
8
- const request = require('../util/request');
9
-
10
- const states = require('../states');
11
- const { fixAndVerifyMetaFromStore } = require('../util/blocklet');
12
- const { validateRegistryURL, getRegistryMeta } = require('../util/registry');
13
-
14
- class BlockletRegistry {
15
- constructor() {
16
- this.blocklets = [];
17
- this.cacheId = '';
18
- }
19
-
20
- getBlocklet(id, registryUrl) {
21
- return this.getBlockletMeta({ did: id, registryUrl });
22
- }
23
-
24
- // eslint-disable-next-line no-unused-vars
25
- async getBlockletMeta({ did, registryUrl }, context) {
26
- const defaultRegistryUrl = await states.node.getBlockletRegistry();
27
-
28
- const url = joinURL(
29
- registryUrl || defaultRegistryUrl,
30
- BLOCKLET_STORE_API_PREFIX,
31
- `/blocklets/${did}/blocklet.json?__t__=${Date.now()}`
32
- );
33
-
34
- const { data } = await request.get(url);
35
- try {
36
- if (data.did !== did) {
37
- throw new Error('Invalid blocklet meta: did does not match');
38
- }
39
-
40
- return fixAndVerifyMetaFromStore(data);
41
- } catch (err) {
42
- logger.error('failed to get blocklet meta', { did, data, error: err });
43
- throw err;
44
- }
45
- }
46
-
47
- async resolveTarballURL({ did, tarball = '', registryUrl = '' }) {
48
- if (!tarball) {
49
- return '';
50
- }
51
-
52
- if (tarball.startsWith('file://')) {
53
- return decodeURIComponent(tarball);
54
- }
55
-
56
- if (tarball.startsWith('http://') || tarball.startsWith('https://')) {
57
- return tarball;
58
- }
59
-
60
- const defaultRegistryUrl = await states.node.getBlockletRegistry();
61
- return joinURL(registryUrl || defaultRegistryUrl, 'api', 'blocklets', did, tarball);
62
- }
63
-
64
- clearCache() {
65
- this.cacheId = '';
66
- this.blocklets = [];
67
- }
68
- }
69
-
70
- BlockletRegistry.validateRegistryURL = validateRegistryURL;
71
-
72
- BlockletRegistry.getRegistryMeta = getRegistryMeta;
73
-
74
- module.exports = BlockletRegistry;