@abtnode/core 1.8.66 → 1.8.67-beta-794a8082

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.
@@ -15,20 +15,18 @@ const { isNFTExpired, getNftExpirationDate } = require('@abtnode/util/lib/nft');
15
15
  const didDocument = require('@abtnode/util/lib/did-document');
16
16
  const { sign } = require('@arcblock/jwt');
17
17
  const { isValid: isValidDid } = require('@arcblock/did');
18
- const { verifyPresentation } = require('@arcblock/vc');
19
18
  const { toSvg: createDidLogo } =
20
19
  process.env.NODE_ENV !== 'test' ? require('@arcblock/did-motif') : require('@arcblock/did-motif/dist/did-motif.cjs');
21
20
  const getBlockletInfo = require('@blocklet/meta/lib/info');
22
21
  const sleep = require('@abtnode/util/lib/sleep');
23
22
 
24
23
  const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager');
25
- const { getVcFromPresentation } = require('@abtnode/util/lib/vc');
26
24
  const {
27
- VC_TYPE_BLOCKLET_PURCHASE,
28
25
  WHO_CAN_ACCESS,
29
26
  SERVER_ROLES,
30
27
  WHO_CAN_ACCESS_PREFIX_ROLES,
31
28
  BLOCKLET_INSTALL_TYPE,
29
+ NODE_MODES,
32
30
  } = require('@abtnode/constant');
33
31
 
34
32
  const getBlockletEngine = require('@blocklet/meta/lib/engine');
@@ -49,7 +47,7 @@ const getComponentProcessId = require('@blocklet/meta/lib/get-component-process-
49
47
  const toBlockletDid = require('@blocklet/meta/lib/did');
50
48
  const { validateMeta } = require('@blocklet/meta/lib/validate');
51
49
  const { update: updateMetaFile } = require('@blocklet/meta/lib/file');
52
- const { titleSchema, mountPointSchema, environmentNameSchema } = require('@blocklet/meta/lib/schema');
50
+ const { titleSchema, updateMountPointSchema, environmentNameSchema } = require('@blocklet/meta/lib/schema');
53
51
  const hasReservedKey = require('@blocklet/meta/lib/has-reserved-key');
54
52
  const Lock = require('@abtnode/util/lib/lock');
55
53
 
@@ -111,6 +109,8 @@ const {
111
109
  validateAppConfig,
112
110
  checkDuplicateAppSk,
113
111
  checkDuplicateMountPoint,
112
+ validateStore,
113
+ validateInServerless,
114
114
  } = require('../../util/blocklet');
115
115
  const StoreUtil = require('../../util/store');
116
116
  const states = require('../../states');
@@ -242,6 +242,8 @@ class BlockletManager extends BaseBlockletManager {
242
242
  * did: string;
243
243
  * title: string;
244
244
  * description: string;
245
+ * storeUrl: string;
246
+ * appSk: string;
245
247
  * sync: boolean = false; // download synchronously, not use queue
246
248
  * delay: number; // push download task to queue after a delay
247
249
  * downloadTokenList: Array<{did: string, token: string}>;
@@ -278,28 +280,31 @@ class BlockletManager extends BaseBlockletManager {
278
280
  context.startImmediately = !!params.startImmediately;
279
281
  }
280
282
 
283
+ const { appSk } = params;
284
+
281
285
  if (type === BLOCKLET_INSTALL_TYPE.URL) {
282
286
  const { url, controller, sync, delay } = params;
283
- return this._installFromUrl({ url, controller, sync, delay }, context);
287
+ return this._installFromUrl({ url, controller, sync, delay, appSk }, context);
284
288
  }
285
289
 
286
290
  if (type === BLOCKLET_INSTALL_TYPE.UPLOAD) {
287
291
  const { file, did, diffVersion, deleteSet } = params;
288
- return this._installFromUpload({ file, did, diffVersion, deleteSet, context });
292
+ return this._installFromUpload({ file, did, diffVersion, deleteSet, appSk }, context);
289
293
  }
290
294
 
291
295
  if (type === BLOCKLET_INSTALL_TYPE.STORE) {
292
296
  const { did, controller, sync, delay, storeUrl } = params;
293
- return this._installFromStore({ did, controller, sync, delay, storeUrl }, context);
297
+ return this._installFromStore({ did, controller, sync, delay, storeUrl, appSk }, context);
294
298
  }
295
299
 
296
300
  if (type === BLOCKLET_INSTALL_TYPE.CREATE) {
297
- return this._installFromCreate({ title: params.title, description: params.description }, context);
301
+ const { title, description } = params;
302
+ return this._installFromCreate({ title, description, appSk }, context);
298
303
  }
299
304
 
300
305
  if (type === BLOCKLET_INSTALL_TYPE.RESTORE) {
301
- const { url, blockletSecretKey } = params;
302
- return this._installFromBackup({ url, blockletSecretKey }, context);
306
+ const { url } = params;
307
+ return this._installFromBackup({ url, appSk }, context);
303
308
  }
304
309
 
305
310
  // should not be here
@@ -343,10 +348,16 @@ class BlockletManager extends BaseBlockletManager {
343
348
  },
344
349
  context = {}
345
350
  ) {
346
- const mountPoint = await mountPointSchema.validateAsync(tmpMountPoint);
351
+ const mountPoint = await updateMountPointSchema.validateAsync(tmpMountPoint);
347
352
  logger.debug('start install component', { rootDid, mountPoint, url });
348
353
 
349
354
  if (file) {
355
+ // TODO: 如何触发这种场景?
356
+ const info = await states.node.read();
357
+ if (info.mode === NODE_MODES.SERVERLESS) {
358
+ throw new Error("Can't install component in serverless-mode server via upload");
359
+ }
360
+
350
361
  return this._installComponentFromUpload({
351
362
  rootDid,
352
363
  mountPoint,
@@ -360,6 +371,11 @@ class BlockletManager extends BaseBlockletManager {
360
371
  }
361
372
 
362
373
  if (url) {
374
+ const info = await states.node.read();
375
+ if (info.mode === NODE_MODES.SERVERLESS) {
376
+ validateStore(info, url);
377
+ }
378
+
363
379
  return this._installComponentFromUrl({
364
380
  rootDid,
365
381
  mountPoint,
@@ -431,25 +447,6 @@ class BlockletManager extends BaseBlockletManager {
431
447
  return { isInstalled: !!blocklet, isRunning, blockletDid, isExternal };
432
448
  }
433
449
 
434
- async installBlockletFromVc({ vcPresentation, challenge }, context) {
435
- logger.info('Install from vc');
436
- const vc = getVcFromPresentation(vcPresentation);
437
-
438
- // FIXME: 这里的 trustedIssuers 相当于相信任何 VC,需要想更安全的方法
439
- verifyPresentation({ presentation: vcPresentation, trustedIssuers: [get(vc, 'issuer.id')], challenge });
440
-
441
- if (!vc.type.includes(VC_TYPE_BLOCKLET_PURCHASE)) {
442
- throw new Error(`Expect ${VC_TYPE_BLOCKLET_PURCHASE} VC type`);
443
- }
444
-
445
- const blockletUrl = get(vc, 'credentialSubject.purchased.blocklet.url');
446
- const urlObject = new URL(blockletUrl);
447
- const did = get(vc, 'credentialSubject.purchased.blocklet.id');
448
- const registry = urlObject.origin;
449
-
450
- return this._installFromStore({ did, registry }, context);
451
- }
452
-
453
450
  async start({ did, throwOnError, checkHealthImmediately = false, e2eMode = false }, context) {
454
451
  logger.info('start blocklet', { did });
455
452
  // should check blocklet integrity
@@ -631,7 +628,7 @@ class BlockletManager extends BaseBlockletManager {
631
628
  // FIXME: 需要改成队列执行,本次失败,下次还需要重试,页面刷新后也能知道最新的状态
632
629
  await this._installFromBackup({
633
630
  url: `file://${spacesRestore.blockletRestoreDir}`,
634
- blockletSecretKey: spacesRestore.blockletWallet.secretKey,
631
+ appSk: spacesRestore.blockletWallet.secretKey,
635
632
  moveDir: true,
636
633
  });
637
634
  }
@@ -1004,7 +1001,7 @@ class BlockletManager extends BaseBlockletManager {
1004
1001
  }
1005
1002
 
1006
1003
  // eslint-disable-next-line no-unused-vars
1007
- async config({ did, configs: newConfigs, skipHook }, context) {
1004
+ async config({ did, configs: newConfigs, skipHook, skipDidDocument }, context) {
1008
1005
  if (!Array.isArray(newConfigs)) {
1009
1006
  throw new Error('configs list is not an array');
1010
1007
  }
@@ -1064,7 +1061,7 @@ class BlockletManager extends BaseBlockletManager {
1064
1061
  [BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK, BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_WALLET_TYPE].includes(item.key)
1065
1062
  );
1066
1063
 
1067
- if (isSkOrWalletTypeChanged) {
1064
+ if (isSkOrWalletTypeChanged && !skipDidDocument) {
1068
1065
  await this._updateDidDocument(blocklet);
1069
1066
  }
1070
1067
 
@@ -1198,7 +1195,7 @@ class BlockletManager extends BaseBlockletManager {
1198
1195
  }
1199
1196
 
1200
1197
  async updateComponentMountPoint({ did, rootDid: inputRootDid, mountPoint: tmpMountPoint }, context) {
1201
- const mountPoint = await mountPointSchema.validateAsync(tmpMountPoint);
1198
+ const mountPoint = await updateMountPointSchema.validateAsync(tmpMountPoint);
1202
1199
 
1203
1200
  const blocklet = await states.blocklet.getBlocklet(inputRootDid);
1204
1201
 
@@ -1679,13 +1676,13 @@ class BlockletManager extends BaseBlockletManager {
1679
1676
  * /blocklet.json
1680
1677
  * /blocklet_extras.json
1681
1678
  * @see Blocklet 端到端备份方案的设计与实现(一期) https://team.arcblock.io/comment/discussions/e62084d5-fafa-489e-91d5-defcd6e93223
1682
- * @param {{ url: string, blockletSecretKey: string, moveDir: boolean}} [{ url }={}]
1679
+ * @param {{ url: string, appSk: string, moveDir: boolean}} [{ url }={}]
1683
1680
  * @param {Record<string, string>} [context={}]
1684
1681
  * @return {Promise<any>}
1685
1682
  * @memberof BlockletManager
1686
1683
  */
1687
- async _installFromBackup({ url, blockletSecretKey, moveDir } = {}, context = {}) {
1688
- return installFromBackup({ url, blockletSecretKey, moveDir, context, manager: this, states });
1684
+ async _installFromBackup({ url, appSk, moveDir } = {}, context = {}) {
1685
+ return installFromBackup({ url, appSk, moveDir, context, manager: this, states });
1689
1686
  }
1690
1687
 
1691
1688
  async ensureBlocklet(did, opts = {}) {
@@ -2084,17 +2081,18 @@ class BlockletManager extends BaseBlockletManager {
2084
2081
  *
2085
2082
  * @param {{
2086
2083
  * did: string;
2087
- * registry: string;
2088
2084
  * sync: boolean;
2089
2085
  * delay: number;
2090
2086
  * controller: Controller;
2087
+ * appSk: string;
2088
+ * storeUrl: string;
2091
2089
  * }} params
2092
2090
  * @param {*} context
2093
2091
  * @return {*}
2094
2092
  * @memberof BlockletManager
2095
2093
  */
2096
2094
  async _installFromStore(params, context) {
2097
- const { did, storeUrl, sync, delay, controller } = params;
2095
+ const { did, storeUrl, sync, delay, controller, appSk } = params;
2098
2096
 
2099
2097
  logger.debug('start install blocklet', { did });
2100
2098
  if (!isValidDid(did)) {
@@ -2138,6 +2136,7 @@ class BlockletManager extends BaseBlockletManager {
2138
2136
  sync,
2139
2137
  delay,
2140
2138
  controller,
2139
+ appSk,
2141
2140
  context,
2142
2141
  });
2143
2142
  }
@@ -2154,13 +2153,14 @@ class BlockletManager extends BaseBlockletManager {
2154
2153
  * sync: boolean;
2155
2154
  * delay: number;
2156
2155
  * controller: Controller;
2156
+ * appSk: string;
2157
2157
  * }} params
2158
2158
  * @param {{}} context
2159
2159
  * @return {*}
2160
2160
  * @memberof BlockletManager
2161
2161
  */
2162
2162
  async _installFromUrl(params, context) {
2163
- const { url, sync, delay, controller } = params;
2163
+ const { url, sync, delay, controller, appSk } = params;
2164
2164
 
2165
2165
  logger.debug('start install blocklet', { url });
2166
2166
 
@@ -2174,13 +2174,18 @@ class BlockletManager extends BaseBlockletManager {
2174
2174
 
2175
2175
  // install from store if url is a store url
2176
2176
  const { inStore, registryUrl, blockletDid: bundleDid } = await StoreUtil.parseSourceUrl(url, controller);
2177
+
2178
+ const nodeInfo = await states.node.read();
2179
+
2180
+ await validateStore(nodeInfo, registryUrl);
2181
+
2177
2182
  if (inStore) {
2178
2183
  const exist = await states.blocklet.getBlocklet(blockletDid);
2179
2184
  if (exist) {
2180
2185
  return this.upgrade({ did: blockletDid, storeUrl: registryUrl, sync, delay }, context);
2181
2186
  }
2182
2187
 
2183
- return this._installFromStore({ did: bundleDid, storeUrl: registryUrl, controller, sync, delay }, context);
2188
+ return this._installFromStore({ did: bundleDid, storeUrl: registryUrl, controller, sync, delay, appSk }, context);
2184
2189
  }
2185
2190
 
2186
2191
  const meta = ensureMeta(bundleMeta, { name: blockletName, did: blockletDid });
@@ -2206,6 +2211,7 @@ class BlockletManager extends BaseBlockletManager {
2206
2211
  sync,
2207
2212
  delay,
2208
2213
  controller,
2214
+ appSk,
2209
2215
  context,
2210
2216
  });
2211
2217
  }
@@ -2310,7 +2316,7 @@ class BlockletManager extends BaseBlockletManager {
2310
2316
  );
2311
2317
  }
2312
2318
 
2313
- async _installFromCreate({ title, description }, context = {}) {
2319
+ async _installFromCreate({ title, description, appSk }, context = {}) {
2314
2320
  logger.debug('create blocklet', { title, description });
2315
2321
 
2316
2322
  await joi.string().label('title').max(20).required().validateAsync(title);
@@ -2338,11 +2344,9 @@ class BlockletManager extends BaseBlockletManager {
2338
2344
  };
2339
2345
  const meta = validateMeta(rawMeta);
2340
2346
 
2341
- await states.blocklet.addBlocklet({
2342
- meta,
2343
- source: BlockletSource.custom,
2344
- });
2347
+ await states.blocklet.addBlocklet({ meta, source: BlockletSource.custom });
2345
2348
  await this._setConfigsFromMeta(did);
2349
+ await this._setAppSk(did, appSk);
2346
2350
 
2347
2351
  // check duplicate appSk
2348
2352
  await checkDuplicateAppSk({ did, states });
@@ -2382,7 +2386,7 @@ class BlockletManager extends BaseBlockletManager {
2382
2386
  return { cwd, tarFile };
2383
2387
  }
2384
2388
 
2385
- async _installFromUpload({ file, did, diffVersion, deleteSet, context }) {
2389
+ async _installFromUpload({ file, did, diffVersion, deleteSet, appSk }, context) {
2386
2390
  logger.info('install blocklet', { from: 'upload file' });
2387
2391
  const { tarFile } = await this._downloadFromUpload(file);
2388
2392
 
@@ -2480,6 +2484,7 @@ class BlockletManager extends BaseBlockletManager {
2480
2484
  const oldState = { extraState: oldExtraState };
2481
2485
  try {
2482
2486
  await this._setConfigsFromMeta(meta.did);
2487
+ await this._setAppSk(appSk);
2483
2488
  await validateBlocklet(blocklet);
2484
2489
 
2485
2490
  // check duplicate appSk
@@ -2776,6 +2781,7 @@ class BlockletManager extends BaseBlockletManager {
2776
2781
  * meta: {}; // blocklet meta
2777
2782
  * source: number; // example: BlockletSource.registry,
2778
2783
  * deployedFrom: string;
2784
+ * appSk: string;
2779
2785
  * context: {}
2780
2786
  * sync: boolean = false;
2781
2787
  * delay: number;
@@ -2785,10 +2791,15 @@ class BlockletManager extends BaseBlockletManager {
2785
2791
  * @memberof BlockletManager
2786
2792
  */
2787
2793
  async _install(params) {
2788
- const { meta, source, deployedFrom, context, sync, delay, controller } = params;
2794
+ const { meta, source, deployedFrom, context, sync, delay, controller, appSk } = params;
2789
2795
 
2790
2796
  validateBlockletMeta(meta, { ensureDist: true });
2791
2797
 
2798
+ const info = await states.node.read();
2799
+ if (info.mode === NODE_MODES.SERVERLESS) {
2800
+ validateInServerless({ blockletMeta: meta });
2801
+ }
2802
+
2792
2803
  const { name, did, version } = meta;
2793
2804
 
2794
2805
  const oldExtraState = await states.blockletExtras.findOne({ did: meta.did });
@@ -2808,6 +2819,7 @@ class BlockletManager extends BaseBlockletManager {
2808
2819
  await states.blockletExtras.addMeta({ did, meta: { did, name }, controller });
2809
2820
 
2810
2821
  await this._setConfigsFromMeta(did);
2822
+ await this._setAppSk(did, appSk);
2811
2823
 
2812
2824
  // check duplicate appSk
2813
2825
  await checkDuplicateAppSk({ did, states });
@@ -3405,12 +3417,31 @@ class BlockletManager extends BaseBlockletManager {
3405
3417
  }
3406
3418
  }
3407
3419
 
3420
+ async _setAppSk(did, appSk, context) {
3421
+ if (process.env.NODE_ENV === 'production' && !appSk) {
3422
+ throw new Error(`appSk for blocklet ${did} is required`);
3423
+ }
3424
+
3425
+ if (appSk) {
3426
+ await this.config(
3427
+ {
3428
+ did,
3429
+ configs: [{ key: 'BLOCKLET_APP_SK', value: appSk, secure: true }],
3430
+ skipHook: true,
3431
+ skipDidDocument: true,
3432
+ },
3433
+ context
3434
+ );
3435
+ }
3436
+ }
3437
+
3408
3438
  async _findNextCustomBlockletName(leftTimes = 10) {
3409
3439
  if (leftTimes <= 0) {
3410
3440
  throw new Error('Generate custom blocklet did too many times');
3411
3441
  }
3412
3442
  const number = await states.node.increaseCustomBlockletNumber();
3413
3443
  const name = `custom-${number}`;
3444
+ // MEMO: 空壳 APP可以保留原有的 did 生成逻辑
3414
3445
  const did = toBlockletDid(name);
3415
3446
  const blocklet = await states.blocklet.getBlocklet(did);
3416
3447
  if (blocklet) {
@@ -19,7 +19,7 @@ const { validateBlocklet, checkDuplicateAppSk, getAppDirs } = require('../../../
19
19
  * }} param0
20
20
  * @returns
21
21
  */
22
- module.exports = async ({ url, blockletSecretKey, moveDir, context = {}, states, manager } = {}) => {
22
+ module.exports = async ({ url, appSk, moveDir, context = {}, states, manager } = {}) => {
23
23
  // TODO: support more url schema feature (http, did-spaces)
24
24
  if (!url.startsWith('file://')) {
25
25
  throw new Error('url must starts with file://');
@@ -71,17 +71,17 @@ module.exports = async ({ url, blockletSecretKey, moveDir, context = {}, states,
71
71
  throw new Error('blocklet is already exist');
72
72
  }
73
73
 
74
- if (blockletSecretKey) {
74
+ if (appSk) {
75
75
  extra.configs = extra.configs || [];
76
76
  const skConfig = extra.configs.find((x) => x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK);
77
77
  if (skConfig) {
78
- skConfig.value = blockletSecretKey;
78
+ skConfig.value = appSk;
79
79
  skConfig.secure = true;
80
80
  skConfig.shared = false;
81
81
  } else {
82
82
  extra.configs.push({
83
83
  key: BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK,
84
- value: blockletSecretKey,
84
+ value: appSk,
85
85
  secure: true,
86
86
  shared: false,
87
87
  });
@@ -150,10 +150,10 @@ class SpacesBackup {
150
150
  const { errorCount } = await spaceClient.send(
151
151
  new SyncFolderPushCommand({
152
152
  source: join(this.blockletBackupDir, '/'),
153
- target: join('.did-objects', this.blocklet.appDid),
153
+ target: join('.did-objects', this.blocklet.appDid, '/'),
154
154
  debug: true,
155
- concurrency: 64,
156
- retryCount: 100,
155
+ concurrency: 32,
156
+ retryCount: 10,
157
157
  filter: (object) => {
158
158
  return object.name !== '.DS_Store';
159
159
  },
@@ -1,4 +1,4 @@
1
- const { existsSync, remove } = require('fs-extra');
1
+ const { existsSync, remove, ensureDirSync } = require('fs-extra');
2
2
  const { join } = require('path');
3
3
  const fg = require('fast-glob');
4
4
  const { BaseRestore } = require('./base');
@@ -10,8 +10,11 @@ class BlockletsRestore extends BaseRestore {
10
10
  async import() {
11
11
  const blockletsDir = join(this.blockletRestoreDir, this.filename);
12
12
 
13
+ // blockletsDir 可以不存在, 因为还原的 blocklet 可能所有的组件都是来自 store 的
13
14
  if (!existsSync(blockletsDir)) {
14
- throw new Error(`dir not found: ${blockletsDir}`);
15
+ // FIXME: 如果文件夹不存在,需要创建文件夹,因为 installFromBackup 未做容错处理
16
+ ensureDirSync(blockletsDir);
17
+ return;
15
18
  }
16
19
 
17
20
  const paths = await fg('**/*.zip', {
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @typedef {{
3
3
  * endpoint: string;
4
- * blockletSecretKey: string;
4
+ * appSk: string;
5
5
  * }} SpaceRestoreInput
6
6
  */
7
7
 
@@ -98,7 +98,7 @@ class SpacesRestore {
98
98
 
99
99
  async getBlockletWallet() {
100
100
  // @FIXME: blocklet 钱包类型如何得知呢?
101
- const wallet = fromSecretKey(this.input.blockletSecretKey, WalletType({ role: types.RoleType.ROLE_APPLICATION }));
101
+ const wallet = fromSecretKey(this.input.appSk, WalletType({ role: types.RoleType.ROLE_APPLICATION }));
102
102
 
103
103
  return wallet;
104
104
  }
@@ -115,10 +115,10 @@ class SpacesRestore {
115
115
  const { errorCount } = await spaceClient.send(
116
116
  new SyncFolderPullCommand({
117
117
  source: join('.did-objects', this.blockletWallet.address, '/'),
118
- target: this.blockletRestoreDir,
118
+ target: join(this.blockletRestoreDir, '/'),
119
119
  debug: true,
120
- concurrency: 64,
121
- retryCount: 100,
120
+ concurrency: 32,
121
+ retryCount: 10,
122
122
  })
123
123
  );
124
124
 
package/lib/event.js CHANGED
@@ -81,7 +81,7 @@ module.exports = ({
81
81
  }
82
82
  };
83
83
 
84
- const handleBlockletAdd = async (name, { blocklet, context }) => {
84
+ const handleBlockletInstall = async (name, { blocklet, context }) => {
85
85
  try {
86
86
  const changed = await ensureBlockletRouting(blocklet, context);
87
87
  if (changed) {
@@ -173,7 +173,7 @@ module.exports = ({
173
173
  const blocklet = payload.blocklet || payload;
174
174
 
175
175
  if ([BlockletEvents.installed].includes(eventName)) {
176
- await handleBlockletAdd(eventName, payload);
176
+ await handleBlockletInstall(eventName, payload);
177
177
 
178
178
  try {
179
179
  await node.createAuditLog({
package/lib/index.js CHANGED
@@ -192,7 +192,6 @@ function ABTNode(options) {
192
192
 
193
193
  // Blocklet manager
194
194
  installBlocklet: blockletManager.install.bind(blockletManager),
195
- installBlockletFromVc: blockletManager.installBlockletFromVc.bind(blockletManager),
196
195
  installComponent: blockletManager.installComponent.bind(blockletManager),
197
196
  startBlocklet: blockletManager.start.bind(blockletManager),
198
197
  stopBlocklet: blockletManager.stop.bind(blockletManager),
@@ -26,7 +26,6 @@ const {
26
26
  NAME_FOR_WELLKNOWN_SITE,
27
27
  DEFAULT_HTTP_PORT,
28
28
  DEFAULT_HTTPS_PORT,
29
- NODE_MODES,
30
29
  ROUTING_RULE_TYPES,
31
30
  CERTIFICATE_EXPIRES_OFFSET,
32
31
  DEFAULT_SERVICE_PATH,
@@ -56,6 +55,7 @@ const {
56
55
  findInterfacePortByName,
57
56
  getWellknownSitePort,
58
57
  getServerDidDomain,
58
+ isGatewayCacheEnabled,
59
59
  } = require('../util');
60
60
  const { getIpDnsDomainForBlocklet, getDidDomainForBlocklet } = require('../util/get-domain-for-blocklet');
61
61
  const { getFromCache: getAccessibleExternalNodeIp } = require('../util/get-accessible-external-node-ip');
@@ -382,7 +382,7 @@ const ensureBlockletCache = async (sites = [], blocklets) => {
382
382
  const clone = cloneDeep(rule);
383
383
  clone.from.pathPrefix = joinUrl(rule.from.pathPrefix, cachePrefix);
384
384
  clone.to.cacheGroup = 'blockletProxy';
385
- clone.to.target = cachePrefix;
385
+ clone.to.targetPrefix = cachePrefix;
386
386
  clone.dynamic = true; // mark as dynamic to avoid redundant generated rules
387
387
  cacheRules.push(clone);
388
388
  });
@@ -518,7 +518,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
518
518
 
519
519
  const ensureDomainCert = async (domain, url) => {
520
520
  const cert = await certManager.getByDomain(domain);
521
- if (!cert) {
521
+ if (!cert || get(cert, 'meta.validTo') <= Date.now()) {
522
522
  await downloadCert({
523
523
  domain,
524
524
  url,
@@ -1115,7 +1115,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1115
1115
  configDir: path.join(dataDirs.router, providerName),
1116
1116
  httpPort: nodeInfo.routing.httpPort || DEFAULT_HTTP_PORT,
1117
1117
  httpsPort: nodeInfo.routing.httpsPort || DEFAULT_HTTPS_PORT,
1118
- cacheDisabled: nodeInfo.mode === NODE_MODES.DEBUG,
1118
+ cacheEnabled: isGatewayCacheEnabled(nodeInfo),
1119
1119
  }),
1120
1120
  getRoutingParams: async () => {
1121
1121
  try {
@@ -1220,7 +1220,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1220
1220
  configDir: path.join(dataDirs.router, info.routing.provider),
1221
1221
  httpPort: info.routing.httpPort || DEFAULT_HTTP_PORT,
1222
1222
  httpsPort: info.routing.httpsPort || DEFAULT_HTTPS_PORT,
1223
- cacheDisabled: info.mode === NODE_MODES.DEBUG,
1223
+ cacheEnabled: isGatewayCacheEnabled(info),
1224
1224
  });
1225
1225
  await providerInstance.stop();
1226
1226
  logger.info('original router stopped:', { provider: info.routing.provider });
@@ -1408,6 +1408,8 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1408
1408
  deleteRoutingRule,
1409
1409
  addDomainAlias,
1410
1410
  deleteDomainAlias,
1411
+
1412
+ isGatewayCacheEnabled,
1411
1413
  };
1412
1414
  };
1413
1415
 
@@ -11,8 +11,11 @@ const {
11
11
  GATEWAY_REQ_LIMIT,
12
12
  } = require('@abtnode/constant');
13
13
  const { BLOCKLET_UI_INTERFACES, BLOCKLET_MODES } = require('@blocklet/constant');
14
+
14
15
  const logger = require('@abtnode/logger')('@abtnode/core:router');
15
16
 
17
+ const { isGatewayCacheEnabled } = require('../util');
18
+
16
19
  const expandSites = (sites = []) => {
17
20
  const result = [];
18
21
 
@@ -117,6 +120,7 @@ class Router {
117
120
  services,
118
121
  nodeInfo: pick(nodeInfo, ['name', 'version', 'port', 'mode', 'enableWelcomePage', 'routing']),
119
122
  requestLimit,
123
+ cacheEnabled: isGatewayCacheEnabled(nodeInfo),
120
124
  });
121
125
  }
122
126
 
@@ -183,6 +187,10 @@ Router.formatSites = (sites = []) => {
183
187
  result.forEach((site) => {
184
188
  if (Array.isArray(site.rules) && site.rules.length > 0) {
185
189
  const rules = cloneDeep(site.rules);
190
+
191
+ let hasRootPathBlockletRule = false;
192
+ let tmpBlockletRule;
193
+
186
194
  rules.forEach((rule) => {
187
195
  if ([ROUTING_RULE_TYPES.BLOCKLET].includes(rule.to.type) === false) {
188
196
  return;
@@ -197,6 +205,11 @@ Router.formatSites = (sites = []) => {
197
205
  }
198
206
 
199
207
  if (daemonRule) {
208
+ if (rule.from.pathPrefix === '/') {
209
+ hasRootPathBlockletRule = true;
210
+ }
211
+ tmpBlockletRule = rule;
212
+
200
213
  // Serve meta js: both prefix and suffix do not contain trailing slash
201
214
  // NOTICE: 这里隐含了一个约定
202
215
  // 如果安装的 blockletA 和 blockletB 都需要 __blocklet__.js
@@ -244,6 +257,24 @@ Router.formatSites = (sites = []) => {
244
257
  });
245
258
  }
246
259
  });
260
+
261
+ // ensure /__blocklet__.js should be proxy to daemon
262
+ if (daemonRule && !hasRootPathBlockletRule && tmpBlockletRule) {
263
+ site.rules.push({
264
+ from: {
265
+ pathPrefix: '/',
266
+ groupPathPrefix: '/',
267
+ pathSuffix: '/__blocklet__.js',
268
+ },
269
+ to: {
270
+ type: ROUTING_RULE_TYPES.DAEMON,
271
+ port: daemonRule.to.port,
272
+ did: tmpBlockletRule.to.did,
273
+ componentId: tmpBlockletRule.to.did,
274
+ cacheGroup: site.mode === BLOCKLET_MODES.PRODUCTION ? 'blockletJs' : '',
275
+ },
276
+ });
277
+ }
247
278
  }
248
279
  });
249
280
 
@@ -559,8 +559,8 @@ class RouterManager extends EventEmitter {
559
559
  await tempRouter.validateConfig();
560
560
  await fse.remove(tmpDir);
561
561
  } catch (error) {
562
+ // 如果出错,保留 Nginx 配置文件,方便定位问题
562
563
  logger.error('validate router config failed', { error, action, data });
563
- await fse.remove(tmpDir);
564
564
  throw error;
565
565
  }
566
566
  }
@@ -239,9 +239,11 @@ const getLogContent = async (action, args, context, result, info, node) => {
239
239
  case 'deleteRoutingRule':
240
240
  return `deleted routing rule from ${site}`; // prettier-ignore
241
241
  case 'updateGateway': {
242
- let message = args.requestLimit.enabled ? `status: enabled, rate: ${args.requestLimit.rate}` : 'status: disabled';
243
- message = `update gateway. ${message}`;
244
-
242
+ const changes = [
243
+ args.requestLimit.enabled ? `rate limit: enabled, rate: ${args.requestLimit.rate}` : 'rate limit: disabled',
244
+ args.cacheEnabled ? `global cache: enabled, rate: ${args.cacheEnabled}` : 'global cache: disabled',
245
+ ];
246
+ const message = `update gateway: ${changes.join('; ')}`;
245
247
  return message;
246
248
  }
247
249
  case 'createTransferInvitation':
@@ -124,7 +124,7 @@ class NodeState extends BaseState {
124
124
  routing,
125
125
  docker,
126
126
  mode,
127
- enableWelcomePage: true,
127
+ enableWelcomePage: mode !== NODE_MODES.SERVERLESS,
128
128
  runtimeConfig,
129
129
  ownerNft,
130
130
  diskAlertThreshold: DISK_ALERT_THRESHOLD_PERCENT,
@@ -310,7 +310,10 @@ class NodeState extends BaseState {
310
310
  }
311
311
 
312
312
  async updateGateway(gateway) {
313
- const [, nodeInfo] = await this.update({}, { $set: { 'routing.requestLimit': gateway.requestLimit } });
313
+ const [, nodeInfo] = await this.update(
314
+ {},
315
+ { $set: { 'routing.requestLimit': gateway.requestLimit, 'routing.cacheEnabled': gateway.cacheEnabled } }
316
+ );
314
317
 
315
318
  this.emit(EVENTS.RELOAD_GATEWAY, nodeInfo);
316
319
 
@@ -31,7 +31,12 @@ const getFolderSize = require('@abtnode/util/lib/get-folder-size');
31
31
  const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
32
32
  const hashFiles = require('@abtnode/util/lib/hash-files');
33
33
  const isPathPrefixEqual = require('@abtnode/util/lib/is-path-prefix-equal');
34
- const { BLOCKLET_MAX_MEM_LIMIT_IN_MB, BLOCKLET_STORE, BLOCKLET_INSTALL_TYPE } = require('@abtnode/constant');
34
+ const {
35
+ BLOCKLET_MAX_MEM_LIMIT_IN_MB,
36
+ BLOCKLET_STORE,
37
+ BLOCKLET_INSTALL_TYPE,
38
+ BLOCKLET_STORE_DEV,
39
+ } = require('@abtnode/constant');
35
40
  const formatBackSlash = require('@abtnode/util/lib/format-back-slash');
36
41
 
37
42
  const SCRIPT_ENGINES_WHITE_LIST = ['npm', 'npx', 'pnpm', 'yarn'];
@@ -89,6 +94,7 @@ const {
89
94
  validateBlockletMeta,
90
95
  prettyURL,
91
96
  getNFTState,
97
+ templateReplace,
92
98
  } = require('./index');
93
99
 
94
100
  const getComponentConfig = (meta) => meta.components || meta.children;
@@ -235,12 +241,12 @@ const getComponentDirs = (
235
241
  const fillBlockletConfigs = (blocklet, configs) => {
236
242
  blocklet.configs = configs || [];
237
243
  blocklet.configObj = blocklet.configs.reduce((acc, x) => {
238
- acc[x.key] = x.value;
244
+ acc[x.key] = templateReplace(x.value, blocklet);
239
245
  return acc;
240
246
  }, {});
241
247
  blocklet.environments = blocklet.environments || [];
242
248
  blocklet.environmentObj = blocklet.environments.reduce((acc, x) => {
243
- acc[x.key] = x.value;
249
+ acc[x.key] = templateReplace(x.value, blocklet);
244
250
  return acc;
245
251
  }, {});
246
252
  };
@@ -835,7 +841,7 @@ const parseChildrenFromMeta = async (src, context = {}) => {
835
841
  };
836
842
 
837
843
  const validateBlocklet = (blocklet) =>
838
- forEachBlocklet(blocklet, (b) => {
844
+ forEachBlocklet(blocklet, async (b) => {
839
845
  isRequirementsSatisfied(b.meta.requirements);
840
846
  validateEngine(getBlockletEngineNameByPlatform(b.meta));
841
847
  });
@@ -1400,12 +1406,14 @@ const getBlocklet = async ({
1400
1406
 
1401
1407
  blocklet.settings.storeList = blocklet.settings.storeList || [];
1402
1408
 
1403
- if (!blocklet.settings.storeList.find((x) => x.url === BLOCKLET_STORE.url)) {
1404
- blocklet.settings.storeList.unshift({
1405
- ...BLOCKLET_STORE,
1406
- protected: true,
1407
- });
1408
- }
1409
+ [BLOCKLET_STORE_DEV, BLOCKLET_STORE].forEach((store) => {
1410
+ if (!blocklet.settings.storeList.find((x) => x.url === store.url)) {
1411
+ blocklet.settings.storeList.unshift({
1412
+ ...store,
1413
+ protected: true,
1414
+ });
1415
+ }
1416
+ });
1409
1417
 
1410
1418
  // app site
1411
1419
  blocklet.site = await states.site.findOneByBlocklet(blocklet.meta.did);
@@ -1690,6 +1698,32 @@ const checkDuplicateMountPoint = (blocklet, mountPoint) => {
1690
1698
  }
1691
1699
  };
1692
1700
 
1701
+ const validateStore = (nodeInfo, storeUrl) => {
1702
+ if (nodeInfo.mode !== 'serverless') {
1703
+ return;
1704
+ }
1705
+
1706
+ const inStoreList = nodeInfo.blockletRegistryList.find((item) => {
1707
+ const itemURLObj = new URL(item.url);
1708
+ const storeUrlObj = new URL(storeUrl);
1709
+
1710
+ return itemURLObj.host === storeUrlObj.host;
1711
+ });
1712
+
1713
+ if (!inStoreList) {
1714
+ throw new Error('Must be installed from the compliant blocklet store list');
1715
+ }
1716
+ };
1717
+
1718
+ const validateInServerless = ({ blockletMeta }) => {
1719
+ const { interfaces } = blockletMeta;
1720
+ const externalPortInterfaces = (interfaces || []).filter((item) => !!item.port?.external);
1721
+
1722
+ if (externalPortInterfaces.length > 0) {
1723
+ throw new Error('Blocklets with exposed ports cannot be installed');
1724
+ }
1725
+ };
1726
+
1693
1727
  module.exports = {
1694
1728
  consumeServerlessNFT,
1695
1729
  forEachBlocklet,
@@ -1737,4 +1771,6 @@ module.exports = {
1737
1771
  validateAppConfig,
1738
1772
  checkDuplicateAppSk,
1739
1773
  checkDuplicateMountPoint,
1774
+ validateStore,
1775
+ validateInServerless,
1740
1776
  };
package/lib/util/index.js CHANGED
@@ -27,6 +27,7 @@ const {
27
27
  DEFAULT_HTTP_PORT,
28
28
  DEFAULT_HTTPS_PORT,
29
29
  SLOT_FOR_IP_DNS_SITE,
30
+ NODE_MODES,
30
31
  } = require('@abtnode/constant');
31
32
 
32
33
  const DEFAULT_WELLKNOWN_PORT = 8088;
@@ -452,6 +453,27 @@ const prettyURL = (url, isHttps = true) => {
452
453
  return isHttps ? `https://${url}` : `http://${url}`;
453
454
  };
454
455
 
456
+ const templateReplace = (str, vars = {}) => {
457
+ if (typeof str === 'string') {
458
+ return str.replace(/{([.\w]+)}/g, (m, key) => get(vars, key));
459
+ }
460
+
461
+ return str;
462
+ };
463
+
464
+ const isGatewayCacheEnabled = (info) => {
465
+ if (info.mode === NODE_MODES.DEBUG) {
466
+ return false;
467
+ }
468
+
469
+ const cacheEnabled = get(info, 'routing.cacheEnabled');
470
+ if (typeof cacheEnabled === 'boolean') {
471
+ return cacheEnabled;
472
+ }
473
+
474
+ return true;
475
+ };
476
+
455
477
  const lib = {
456
478
  validateOwner,
457
479
  getProviderFromNodeInfo,
@@ -489,6 +511,8 @@ const lib = {
489
511
  getNFTState,
490
512
  getServerDidDomain,
491
513
  prettyURL,
514
+ templateReplace,
515
+ isGatewayCacheEnabled,
492
516
  };
493
517
 
494
518
  module.exports = lib;
@@ -52,6 +52,7 @@ const updateGatewaySchema = Joi.object({
52
52
  .when('requestLimit.enabled', { is: true, then: Joi.required() }),
53
53
  ipHeader: Joi.string().allow('').trim(),
54
54
  }),
55
+ cacheEnabled: Joi.bool().optional().default(true),
55
56
  });
56
57
 
57
58
  module.exports = {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.8.66",
6
+ "version": "1.8.67-beta-794a8082",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,33 +19,33 @@
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
- "@abtnode/auth": "1.8.66",
23
- "@abtnode/certificate-manager": "1.8.66",
24
- "@abtnode/constant": "1.8.66",
25
- "@abtnode/cron": "1.8.66",
26
- "@abtnode/db": "1.8.66",
27
- "@abtnode/logger": "1.8.66",
28
- "@abtnode/queue": "1.8.66",
29
- "@abtnode/rbac": "1.8.66",
30
- "@abtnode/router-provider": "1.8.66",
31
- "@abtnode/static-server": "1.8.66",
32
- "@abtnode/timemachine": "1.8.66",
33
- "@abtnode/util": "1.8.66",
34
- "@arcblock/did": "1.18.42",
22
+ "@abtnode/auth": "1.8.67-beta-794a8082",
23
+ "@abtnode/certificate-manager": "1.8.67-beta-794a8082",
24
+ "@abtnode/constant": "1.8.67-beta-794a8082",
25
+ "@abtnode/cron": "1.8.67-beta-794a8082",
26
+ "@abtnode/db": "1.8.67-beta-794a8082",
27
+ "@abtnode/logger": "1.8.67-beta-794a8082",
28
+ "@abtnode/queue": "1.8.67-beta-794a8082",
29
+ "@abtnode/rbac": "1.8.67-beta-794a8082",
30
+ "@abtnode/router-provider": "1.8.67-beta-794a8082",
31
+ "@abtnode/static-server": "1.8.67-beta-794a8082",
32
+ "@abtnode/timemachine": "1.8.67-beta-794a8082",
33
+ "@abtnode/util": "1.8.67-beta-794a8082",
34
+ "@arcblock/did": "1.18.54",
35
35
  "@arcblock/did-motif": "^1.1.10",
36
- "@arcblock/did-util": "1.18.42",
37
- "@arcblock/event-hub": "1.18.42",
38
- "@arcblock/jwt": "^1.18.42",
36
+ "@arcblock/did-util": "1.18.54",
37
+ "@arcblock/event-hub": "1.18.54",
38
+ "@arcblock/jwt": "^1.18.54",
39
39
  "@arcblock/pm2-events": "^0.0.5",
40
- "@arcblock/vc": "1.18.42",
41
- "@blocklet/constant": "1.8.66",
42
- "@blocklet/meta": "1.8.66",
43
- "@blocklet/sdk": "1.8.66",
44
- "@did-space/client": "^0.1.66",
40
+ "@arcblock/vc": "1.18.54",
41
+ "@blocklet/constant": "1.8.67-beta-794a8082",
42
+ "@blocklet/meta": "1.8.67-beta-794a8082",
43
+ "@blocklet/sdk": "1.8.67-beta-794a8082",
44
+ "@did-space/client": "^0.1.76",
45
45
  "@fidm/x509": "^1.2.1",
46
- "@ocap/mcrypto": "1.18.42",
47
- "@ocap/util": "1.18.42",
48
- "@ocap/wallet": "1.18.42",
46
+ "@ocap/mcrypto": "1.18.54",
47
+ "@ocap/util": "1.18.54",
48
+ "@ocap/wallet": "1.18.54",
49
49
  "@slack/webhook": "^5.0.4",
50
50
  "archiver": "^5.3.1",
51
51
  "axios": "^0.27.2",
@@ -72,7 +72,6 @@
72
72
  "pm2": "^5.2.0",
73
73
  "semver": "^7.3.8",
74
74
  "shelljs": "^0.8.5",
75
- "slugify": "^1.6.5",
76
75
  "ssri": "^8.0.1",
77
76
  "stream-throttle": "^0.1.3",
78
77
  "stream-to-promise": "^3.0.0",
@@ -90,5 +89,5 @@
90
89
  "express": "^4.18.2",
91
90
  "jest": "^27.5.1"
92
91
  },
93
- "gitHead": "282247fd0ce6702e1d6920e90e2b3be0408cf879"
92
+ "gitHead": "f4ad32bea4d80b12971fb6bef941bdbe2af6a834"
94
93
  }