@abtnode/core 1.16.28-next-5a717317 → 1.16.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/api/team.js CHANGED
@@ -2,7 +2,7 @@ const { EventEmitter } = require('events');
2
2
  const pick = require('lodash/pick');
3
3
  const defaults = require('lodash/defaults');
4
4
  const cloneDeep = require('lodash/cloneDeep');
5
- const joinUrl = require('url-join');
5
+ const { joinURL } = require('ufo');
6
6
 
7
7
  const logger = require('@abtnode/logger')('@abtnode/core:api:team');
8
8
  const {
@@ -511,7 +511,7 @@ class TeamAPI extends EventEmitter {
511
511
  role,
512
512
  }),
513
513
  endpoint: getPassportStatusEndpoint({
514
- baseUrl: joinUrl(appUrl, WELLKNOWN_SERVICE_PATH_PREFIX),
514
+ baseUrl: joinURL(appUrl, WELLKNOWN_SERVICE_PATH_PREFIX),
515
515
  userDid,
516
516
  teamDid,
517
517
  }),
@@ -966,7 +966,7 @@ class TeamAPI extends EventEmitter {
966
966
  role,
967
967
  }),
968
968
  endpoint: getPassportStatusEndpoint({
969
- baseUrl: joinUrl(appUrl, WELLKNOWN_SERVICE_PATH_PREFIX),
969
+ baseUrl: joinURL(appUrl, WELLKNOWN_SERVICE_PATH_PREFIX),
970
970
  userDid,
971
971
  teamDid,
972
972
  }),
@@ -1,11 +1,13 @@
1
1
  const get = require('lodash/get');
2
2
  const camelCase = require('lodash/camelCase');
3
3
  const runScript = require('@abtnode/util/lib/run-script');
4
+ const { getSecurityNodeOptions } = require('@abtnode/util/lib/security');
4
5
 
5
6
  // eslint-disable-next-line global-require
6
7
  const logger = require('@abtnode/logger')(`${require('../../package.json').name}:blocklet:hooks`);
7
8
 
8
9
  const { getSafeEnv } = require('../util');
10
+ const states = require('../states');
9
11
 
10
12
  const runUserHook = async (label, hookName, args) => {
11
13
  const {
@@ -27,10 +29,16 @@ const runUserHook = async (label, hookName, args) => {
27
29
  }
28
30
 
29
31
  logger.info(`run hook:${hookName}:`, { label, hook });
32
+
33
+ const nodeInfo = await states.node.read();
30
34
  // FIXME @linchen timeout 应该动态设置或不设置
31
35
  await runScript(hook, [label, hookName].join(':'), {
32
36
  cwd: appDir,
33
- env: { ...getSafeEnv(env), BLOCKLET_HOOK_NAME: hookName },
37
+ env: {
38
+ ...getSafeEnv(env),
39
+ BLOCKLET_HOOK_NAME: hookName,
40
+ NODE_OPTIONS: getSecurityNodeOptions({ environmentObj: env, ...args }, nodeInfo.enableFileSystemIsolation),
41
+ },
34
42
  silent,
35
43
  output: outputFile,
36
44
  error: errorFile,
@@ -16,7 +16,7 @@ const { sign } = require('@arcblock/jwt');
16
16
  const { isInServerlessMode } = require('@abtnode/util/lib/serverless');
17
17
  const sleep = require('@abtnode/util/lib/sleep');
18
18
  const getBlockletInfo = require('@blocklet/meta/lib/info');
19
- const joinUrl = require('url-join');
19
+ const { joinURL } = require('ufo');
20
20
  const { sendToUser } = require('@blocklet/sdk/lib/util/send-notification');
21
21
 
22
22
  const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager');
@@ -2799,7 +2799,7 @@ class DiskBlockletManager extends BaseBlockletManager {
2799
2799
  * @type {import('@abtnode/client').NodeState}
2800
2800
  */
2801
2801
  const node = await states.node.read();
2802
- referrer = joinUrl(
2802
+ referrer = joinURL(
2803
2803
  `https://${encode(node.did)}.${DEFAULT_DID_DOMAIN}`,
2804
2804
  node.routing.adminPath,
2805
2805
  `/blocklets/${did}/storage`
@@ -3253,7 +3253,8 @@ class DiskBlockletManager extends BaseBlockletManager {
3253
3253
  logo: '',
3254
3254
  };
3255
3255
  } else {
3256
- throw new Error(`engine component ${engineId} not found`);
3256
+ // Note: the component maybe in dev mode or removed
3257
+ logger.warn(`engine component ${engineId} not found for ${did}`);
3257
3258
  }
3258
3259
  } else {
3259
3260
  component.engine = getEngine(engine.interpreter)?.describe();
@@ -4486,7 +4487,7 @@ class FederatedBlockletManager extends DiskBlockletManager {
4486
4487
  async joinFederatedLogin({ appUrl, did }, context) {
4487
4488
  const url = new URL(appUrl);
4488
4489
  // master service api 的地址
4489
- url.pathname = joinUrl(WELLKNOWN_SERVICE_PATH_PREFIX, '/api/federated/join');
4490
+ url.pathname = joinURL(WELLKNOWN_SERVICE_PATH_PREFIX, '/api/federated/join');
4490
4491
 
4491
4492
  const blocklet = await this.getBlocklet(did);
4492
4493
  const nodeInfo = await states.node.read();
@@ -4503,7 +4504,7 @@ class FederatedBlockletManager extends DiskBlockletManager {
4503
4504
  aliasDomain: domainAliases.map((x) => x.value),
4504
4505
  appLogo:
4505
4506
  blocklet.environmentObj.BLOCKLET_APP_LOGO ||
4506
- normalizePathPrefix(joinUrl(WELLKNOWN_SERVICE_PATH_PREFIX, '/blocklet/logo')) ||
4507
+ normalizePathPrefix(joinURL(WELLKNOWN_SERVICE_PATH_PREFIX, '/blocklet/logo')) ||
4507
4508
  '/',
4508
4509
  appLogoRect: blocklet.environmentObj.BLOCKLET_APP_LOGO_RECT,
4509
4510
  did: permanentWallet.address,
@@ -95,9 +95,11 @@ const installComponentFromUrl = async ({
95
95
  const { dynamicComponents } = await parseComponents(newChild);
96
96
 
97
97
  const index = blocklet.children.findIndex((child) => child.meta.did === meta.did);
98
+
98
99
  if (index >= 0) {
99
100
  // if upgrade, do not update mountPoint and title
100
- newChild.mountPoint = blocklet.children[index].mountPoint;
101
+ newChild.mountPoint =
102
+ blocklet.children[index].mountPoint || formatName(newChildMeta.title) || formatName(newChildMeta.name);
101
103
  // 更新版本时, title 应该更新
102
104
  // newChild.meta.title = blocklet.children[index].meta.title;
103
105
  newChild.installedAt = blocklet.children[index].installedAt;
@@ -13,7 +13,7 @@ const { createRelease: createBlockletRelease } = require('@abtnode/util/lib/crea
13
13
  const urlPathFriendly = require('@blocklet/meta/lib/url-path-friendly').default;
14
14
  const { hasStartEngine } = require('@blocklet/meta/lib/util');
15
15
 
16
- const logger = require('@abtnode/logger')('create-resource-blocklet-pack');
16
+ const logger = require('@abtnode/logger')('blocklet-studio-pack');
17
17
 
18
18
  const { createReleaseSchema } = require('../../validators/project');
19
19
 
@@ -228,7 +228,7 @@ const createPackRelease = async ({
228
228
  if (fs.existsSync(extendedMetaFile)) {
229
229
  const extendedMeta = readMetaFile(extendedMetaFile);
230
230
  logger.info('merge extended blocklet.yml', extendedMeta);
231
- Object.assign(meta, pick(extendedMeta, ['environments', 'engine']));
231
+ Object.assign(meta, pick(extendedMeta, ['environments', 'engine', 'capabilities']));
232
232
  }
233
233
 
234
234
  // Reset group for blocklets with engine specified
@@ -10,7 +10,7 @@ const { titleSchema } = require('@blocklet/meta/lib/schema');
10
10
  const { validateNewDid } = require('@blocklet/meta/lib/name');
11
11
  const urlPathFriendly = require('@blocklet/meta/lib/url-path-friendly').default;
12
12
 
13
- const logger = require('@abtnode/logger')('create-resource-blocklet');
13
+ const logger = require('@abtnode/logger')('blocklet-studio');
14
14
 
15
15
  const { createReleaseSchema } = require('../../validators/project');
16
16
  const getIsMultipleTenant = require('./get-is-multiple-tenant');
@@ -19,8 +19,10 @@ const {
19
19
  getLogoFile,
20
20
  exportBlockletResources,
21
21
  getResourceList,
22
+ checkUploadExists,
22
23
  checkResourceExists,
23
24
  getExtendedMetaFile,
25
+ exportUploadedResources,
24
26
  } = require('./util');
25
27
  const createPackRelease = require('./create-pack-release');
26
28
  const connectToStore = require('./connect-to-store');
@@ -172,6 +174,7 @@ const createRelease = async ({
172
174
  blockletIntroduction,
173
175
  blockletLogo,
174
176
  blockletScreenshots,
177
+ uploadedResource,
175
178
  blockletComponents,
176
179
  note,
177
180
  manager,
@@ -196,6 +199,7 @@ const createRelease = async ({
196
199
  blockletLogo,
197
200
  blockletIntroduction,
198
201
  blockletScreenshots,
202
+ uploadedResource,
199
203
  note,
200
204
  };
201
205
 
@@ -235,16 +239,16 @@ const createRelease = async ({
235
239
  if (action === 'update') {
236
240
  const release = await releaseState.findOne({ projectId, id: releaseId });
237
241
  if (!release) {
238
- throw new Error('release not found');
242
+ throw new Error(`Blocklet release ${releaseId} not found for project ${projectId}`);
239
243
  }
240
244
  if (release.status !== 'draft') {
241
- throw new Error('Can not update a published release');
245
+ throw new Error(`Can not update a published release: ${releaseId}`);
242
246
  }
243
247
  }
244
248
 
245
249
  const existVersion = await releaseState.findOne({ projectId, blockletVersion });
246
250
  if (existVersion && (action === 'create' || existVersion.id !== releaseId)) {
247
- throw new Error(`blockletVersion ${blockletVersion} already exists`);
251
+ throw new Error(`Release version ${blockletVersion} already exists`);
248
252
  }
249
253
 
250
254
  const { blockletDid } = project0;
@@ -253,8 +257,14 @@ const createRelease = async ({
253
257
 
254
258
  const projectDir = path.join(blocklet.env.dataDir, PROJECT.DIR, `${projectId}`);
255
259
 
260
+ // validate resources
261
+ const isResourceFromUpload = !!uploadedResource;
256
262
  if (status !== PROJECT.RELEASE_STATUS.draft) {
257
- await checkResourceExists(projectDir, action, releaseId);
263
+ if (isResourceFromUpload) {
264
+ await checkUploadExists(projectDir, action, releaseId, uploadedResource);
265
+ } else {
266
+ await checkResourceExists(projectDir, action, releaseId);
267
+ }
258
268
  }
259
269
 
260
270
  const release = await releaseState.upsertRelease({
@@ -264,6 +274,7 @@ const createRelease = async ({
264
274
  releaseId,
265
275
  status,
266
276
  });
277
+ logger.info('release upsert done', release);
267
278
 
268
279
  const _releaseId = release.id;
269
280
  const releaseDir = path.join(projectDir, PROJECT.RELEASE_DIR, `${_releaseId}`);
@@ -301,7 +312,7 @@ const createRelease = async ({
301
312
  }
302
313
 
303
314
  if (fs.existsSync(releaseFile)) {
304
- logger.error('release file already exists, remove it', releaseFile);
315
+ logger.warn('release file exists, remove it', releaseFile);
305
316
  await fs.remove(releaseFile);
306
317
  }
307
318
 
@@ -338,17 +349,7 @@ const createRelease = async ({
338
349
  });
339
350
  await fs.outputFile(changelogDistFile, changelog.join('\n\n'));
340
351
 
341
- // create resource
342
- await exportBlockletResources({
343
- app: blocklet,
344
- projectId,
345
- releaseId: _releaseId,
346
- exportDir: releaseBundleDir,
347
- blockletDid,
348
- });
349
-
350
352
  // create blocklet.yml
351
- const resourceList = await getResourceList(resourceDir);
352
353
  const meta = {
353
354
  did: blockletDid,
354
355
  name: blockletDid,
@@ -356,20 +357,53 @@ const createRelease = async ({
356
357
  description: blockletDescription,
357
358
  version: blockletVersion,
358
359
  logo: logoFileName,
359
- resource: {
360
- bundles: resourceList.length ? resourceList : undefined,
361
- },
362
360
  components: [],
363
361
  files: project.blockletScreenshots?.length ? ['screenshots'] : [],
364
362
  screenshots: project.blockletScreenshots || [],
365
363
  };
366
364
 
365
+ // create resource
366
+ if (isResourceFromUpload) {
367
+ await exportUploadedResources({
368
+ app: blocklet,
369
+ projectId,
370
+ releaseId: _releaseId,
371
+ exportDir: releaseBundleDir,
372
+ });
373
+
374
+ meta.main = PROJECT.MAIN_DIR;
375
+ meta.engine = {
376
+ interpreter: 'blocklet',
377
+ source: {
378
+ // FIXME: @wangshijun publish this to production store
379
+ store: 'https://test.store.blocklet.dev',
380
+ name: 'z2qa2dGC9EmsjB2WJtUcmuRWx43zTwPUZQF7g',
381
+ version: 'latest',
382
+ },
383
+ };
384
+ } else {
385
+ await exportBlockletResources({
386
+ app: blocklet,
387
+ projectId,
388
+ releaseId: _releaseId,
389
+ exportDir: releaseBundleDir,
390
+ blockletDid,
391
+ });
392
+
393
+ const resourceList = await getResourceList(resourceDir);
394
+ if (resourceList) {
395
+ meta.resource = {
396
+ bundles: resourceList.length ? resourceList : undefined,
397
+ };
398
+ }
399
+ }
400
+
367
401
  // merge extended blocklet.yml
368
402
  const extendedMetaFile = getExtendedMetaFile({ app: blocklet, projectId, releaseId });
369
403
  if (fs.existsSync(extendedMetaFile)) {
370
404
  const extendedMeta = readMetaFile(extendedMetaFile);
371
- logger.info('merge extended blocklet.yml', extendedMeta);
372
- Object.assign(meta, pick(extendedMeta, ['environments', 'engine']));
405
+ logger.info('release extended blocklet.yml', extendedMeta);
406
+ Object.assign(meta, pick(extendedMeta, ['environments', 'engine', 'capabilities']));
373
407
  }
374
408
 
375
409
  // Enable mountPoint for blocklets with engine specified
@@ -411,7 +445,7 @@ const createRelease = async ({
411
445
  files: [releaseFileName],
412
446
  });
413
447
 
414
- logger.info('create release success', res2);
448
+ logger.info('release finalized', res2);
415
449
 
416
450
  return res2;
417
451
  } catch (error) {
@@ -3,6 +3,7 @@ const fs = require('fs-extra');
3
3
  const fg = require('fast-glob');
4
4
  const { isValid: isValidDid } = require('@arcblock/did');
5
5
  const { PROJECT, BLOCKLET_RESOURCE_DIR, BLOCKLET_META_FILE } = require('@blocklet/constant');
6
+ const { expandBundle } = require('../../util');
6
7
 
7
8
  const COMPONENT_CONFIG_MAP_DIR = '.component_config';
8
9
 
@@ -77,6 +78,15 @@ const exportBlockletResources = async ({
77
78
  }
78
79
  };
79
80
 
81
+ const exportUploadedResources = async ({ app, projectId, releaseId, exportDir }) => {
82
+ const projectDir = path.join(app.env.dataDir, PROJECT.DIR, `${projectId}`);
83
+ const releaseDir = path.join(projectDir, PROJECT.RELEASE_DIR, `${releaseId}`);
84
+ const sourceDir = path.join(releaseDir, PROJECT.RESOURCE_DIR, PROJECT.MAIN_DIR);
85
+ const destDir = path.join(exportDir, PROJECT.MAIN_DIR);
86
+ await fs.ensureDir(destDir);
87
+ await fs.copy(sourceDir, destDir);
88
+ };
89
+
80
90
  async function checkIfDirectory(file) {
81
91
  try {
82
92
  const stats = await fs.promises.stat(file);
@@ -135,6 +145,29 @@ const checkResourceExists = async (projectDir, action, releaseId) => {
135
145
  }
136
146
  };
137
147
 
148
+ const checkUploadExists = async (projectDir, action, releaseId, uploadedResource) => {
149
+ const uploadedFile = path.join(projectDir, PROJECT.ASSET_DIR, uploadedResource);
150
+ if (!fs.existsSync(uploadedFile)) {
151
+ throw new Error(`Uploaded resource does not exist: ${uploadedResource}`);
152
+ }
153
+
154
+ const resourceDir =
155
+ action === 'create'
156
+ ? path.join(projectDir, PROJECT.RESOURCE_DIR, PROJECT.MAIN_DIR)
157
+ : path.join(projectDir, PROJECT.RELEASE_DIR, `${releaseId}`, PROJECT.RESOURCE_DIR, PROJECT.MAIN_DIR);
158
+
159
+ if (fs.existsSync(resourceDir)) {
160
+ fs.rmSync(resourceDir, { recursive: true });
161
+ }
162
+ fs.ensureDirSync(resourceDir);
163
+
164
+ await expandBundle(uploadedFile, resourceDir);
165
+ const files = ['index.html', 'index.htm'];
166
+ if (files.every((file) => fs.existsSync(path.join(resourceDir, file)) === false)) {
167
+ throw new Error(`Uploaded resource does not contain index.html or index.htm: ${uploadedResource}`);
168
+ }
169
+ };
170
+
138
171
  const getExtendedMetaFile = ({ app, projectId, releaseId }) => {
139
172
  const { dataDir } = app.env;
140
173
  const dirArr = [dataDir, PROJECT.DIR, projectId || '/'];
@@ -148,7 +181,9 @@ const getExtendedMetaFile = ({ app, projectId, releaseId }) => {
148
181
  module.exports = {
149
182
  getLogoFile,
150
183
  exportBlockletResources,
184
+ exportUploadedResources,
151
185
  getResourceList,
186
+ checkUploadExists,
152
187
  checkResourceExists,
153
188
  getExtendedMetaFile,
154
189
  };
@@ -1,4 +1,4 @@
1
- const EventEmitter = require('events');
1
+ const { EventEmitter } = require('events');
2
2
 
3
3
  const cloneDeep = require('lodash/cloneDeep');
4
4
  const pLimit = require('p-limit');
@@ -1,4 +1,4 @@
1
- const EventEmitter = require('events');
1
+ const { EventEmitter } = require('events');
2
2
  const dayjs = require('@abtnode/util/lib/dayjs');
3
3
  const pick = require('lodash/pick');
4
4
  const cloneDeep = require('lodash/cloneDeep');
@@ -10,7 +10,7 @@ const get = require('lodash/get');
10
10
  const cloneDeep = require('lodash/cloneDeep');
11
11
  const groupBy = require('lodash/groupBy');
12
12
  const isEqual = require('lodash/isEqual');
13
- const joinUrl = require('url-join');
13
+ const { joinURL } = require('ufo');
14
14
  const {
15
15
  replaceSlotToIp,
16
16
  findComponentById,
@@ -288,7 +288,7 @@ const ensureLatestInterfaceInfo = async (sites = []) => {
288
288
  if (rule.isProtected && rule.to.target === WELLKNOWN_SERVICE_PATH_PREFIX) {
289
289
  return rule;
290
290
  }
291
- if (rule.isProtected && rule.to.target === joinUrl(WELLKNOWN_SERVICE_PATH_PREFIX, USER_AVATAR_PATH_PREFIX)) {
291
+ if (rule.isProtected && rule.to.target === joinURL(WELLKNOWN_SERVICE_PATH_PREFIX, USER_AVATAR_PATH_PREFIX)) {
292
292
  return rule;
293
293
  }
294
294
 
@@ -359,7 +359,7 @@ const ensureWellknownRule = async (sites = []) => {
359
359
  const rule = grouped[groupPathPrefix][0];
360
360
 
361
361
  // Serve blocklet service always
362
- const servicePathPrefix = joinUrl(groupPathPrefix, WELLKNOWN_SERVICE_PATH_PREFIX);
362
+ const servicePathPrefix = joinURL(groupPathPrefix, WELLKNOWN_SERVICE_PATH_PREFIX);
363
363
  if (!site.rules.some((x) => x.from.pathPrefix === servicePathPrefix)) {
364
364
  site.rules.push({
365
365
  id: rule.id,
@@ -380,7 +380,7 @@ const ensureWellknownRule = async (sites = []) => {
380
380
  }
381
381
 
382
382
  // Cache user avatar from gateway
383
- const avatarPathPrefix = joinUrl(servicePathPrefix, USER_AVATAR_PATH_PREFIX);
383
+ const avatarPathPrefix = joinURL(servicePathPrefix, USER_AVATAR_PATH_PREFIX);
384
384
  if (!site.rules.some((x) => x.from.pathPrefix === avatarPathPrefix)) {
385
385
  site.rules.push({
386
386
  id: rule.id,
@@ -492,7 +492,7 @@ const ensureBlockletWellknownRules = (sites, blocklets) => {
492
492
 
493
493
  const tmpMountPoint = mountPoint || blocklet.mountPoint;
494
494
  if (tmpMountPoint) {
495
- pathPrefix = joinUrl(tmpMountPoint, pathPrefix);
495
+ pathPrefix = joinURL(tmpMountPoint, pathPrefix);
496
496
  }
497
497
 
498
498
  const port = findInterfacePortByName(blocklet, tmpInterface.name);
@@ -585,7 +585,7 @@ const expandComponentRules = (sites = [], blocklets) => {
585
585
  newRule.from.pathPrefix = baseRule.from.pathPrefix;
586
586
  newRule.to.pageGroup = baseRule.to.pageGroup;
587
587
  } else {
588
- newRule.from.pathPrefix = joinUrl(baseRule.from.pathPrefix, x.mountPoint);
588
+ newRule.from.pathPrefix = joinURL(baseRule.from.pathPrefix, x.mountPoint);
589
589
  }
590
590
 
591
591
  expandedRules.push(newRule);
@@ -633,7 +633,7 @@ const ensureBlockletCache = (sites = [], blocklets) => {
633
633
  const cacheable = get(findWebInterface(component), 'cacheable', []);
634
634
  cacheable.forEach((cachePrefix) => {
635
635
  const clone = cloneDeep(rule);
636
- clone.from.pathPrefix = joinUrl(rule.from.pathPrefix, cachePrefix);
636
+ clone.from.pathPrefix = joinURL(rule.from.pathPrefix, cachePrefix);
637
637
  clone.to.cacheGroup = isServiceFeDevelopment ? '' : 'blockletProxy';
638
638
  clone.to.targetPrefix = cachePrefix;
639
639
  clone.dynamic = true; // mark as dynamic to avoid redundant generated rules
@@ -674,7 +674,7 @@ const decompressCertificates = async (source, dest) => {
674
674
  return dest;
675
675
  };
676
676
 
677
- const joinCertDownUrl = (baseUrl, name) => joinUrl(baseUrl, '/certs', name);
677
+ const joinCertDownUrl = (baseUrl, name) => joinURL(baseUrl, '/certs', name);
678
678
 
679
679
  const getIpEchoCertDownloadUrl = (baseUrl) => joinCertDownUrl(baseUrl, 'ip-abtnet-io.tar.gz');
680
680
  const getDidDomainCertDownloadUrl = (baseUrl) => joinCertDownUrl(baseUrl, 'did-abtnet-io.tar.gz');
@@ -13,7 +13,7 @@ const toLower = require('lodash/toLower');
13
13
  const { EventEmitter } = require('events');
14
14
  const uuid = require('uuid');
15
15
  const isUrl = require('is-url');
16
- const joinUrl = require('url-join');
16
+ const { joinURL } = require('ufo');
17
17
  const cloneDeep = require('lodash/cloneDeep');
18
18
  const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
19
19
  const logger = require('@abtnode/logger')('@abtnode/core:router:manager');
@@ -704,7 +704,7 @@ class RouterManager extends EventEmitter {
704
704
  const blockletPrefix = normalizePathPrefix(rawRule.from.pathPrefix);
705
705
 
706
706
  // root component's mountPoint may not be '/'
707
- const rootComponentPrefix = joinUrl(blockletPrefix, blocklet.mountPoint || '/');
707
+ const rootComponentPrefix = joinURL(blockletPrefix, blocklet.mountPoint || '/');
708
708
  rawRule.from.pathPrefix = normalizePathPrefix(rootComponentPrefix);
709
709
 
710
710
  const isOccupiable = isGatewayBlocklet(blocklet.meta);
@@ -3,7 +3,7 @@
3
3
  const pick = require('lodash/pick');
4
4
  const get = require('lodash/get');
5
5
  const uniq = require('lodash/uniq');
6
- const joinUrl = require('url-join');
6
+ const { joinURL } = require('ufo');
7
7
  const { getDisplayName } = require('@blocklet/meta/lib/util');
8
8
  const { BLOCKLET_SITE_GROUP_SUFFIX, NODE_SERVICES } = require('@abtnode/constant');
9
9
  const logger = require('@abtnode/logger')('@abtnode/core:states:audit-log');
@@ -12,7 +12,7 @@ const BaseState = require('./base');
12
12
 
13
13
  const { parse } = require('../util/ua');
14
14
 
15
- const getServerInfo = (info) => `[${info.name}](${joinUrl(info.routing.adminPath, '/settings/about')})`;
15
+ const getServerInfo = (info) => `[${info.name}](${joinURL(info.routing.adminPath, '/settings/about')})`;
16
16
  /**
17
17
  * @description
18
18
  * @param {import('@abtnode/client').BlockletState} blocklet
@@ -21,7 +21,7 @@ const getServerInfo = (info) => `[${info.name}](${joinUrl(info.routing.adminPath
21
21
  * }} info
22
22
  * @returns {string}
23
23
  */
24
- const getBlockletInfo = (blocklet, info) => `[${getDisplayName(blocklet)}](${joinUrl(info.routing.adminPath, '/blocklets/', blocklet.meta.did, '/overview')})`; // prettier-ignore
24
+ const getBlockletInfo = (blocklet, info) => `[${getDisplayName(blocklet)}](${joinURL(info.routing.adminPath, '/blocklets/', blocklet.meta.did, '/overview')})`; // prettier-ignore
25
25
  const componentOrApplication = (componentDids) => (componentDids?.length ? 'component' : 'application');
26
26
  const getComponentNames = (blocklet, componentDids, withVersion) =>
27
27
  uniq(componentDids || [])
@@ -82,10 +82,10 @@ const expandUser = async (teamDid, userDid, passportId, info, node) => {
82
82
  const passport = user.passports.find((x) => x.id === passportId);
83
83
 
84
84
  if (teamDid === info.did) {
85
- return [`[${user.fullName}](${joinUrl(info.routing.adminPath, '/team/members')})`, passport ? passport.name : ''];
85
+ return [`[${user.fullName}](${joinURL(info.routing.adminPath, '/team/members')})`, passport ? passport.name : ''];
86
86
  }
87
87
 
88
- return [`[${user.fullName}](${joinUrl(info.routing.adminPath, '/blocklets/', teamDid, '/members')})`, passport ? passport.name : '']; // prettier-ignore
88
+ return [`[${user.fullName}](${joinURL(info.routing.adminPath, '/blocklets/', teamDid, '/members')})`, passport ? passport.name : '']; // prettier-ignore
89
89
  };
90
90
 
91
91
  /**
@@ -31,6 +31,7 @@ class Release extends BaseState {
31
31
  'blockletScreenshots',
32
32
  'publishedStoreIds',
33
33
  'blockletComponents',
34
+ 'uploadedResource',
34
35
  'note',
35
36
  'status',
36
37
  'files',
@@ -393,6 +393,7 @@ class User extends ExtendBase {
393
393
  'email',
394
394
  'avatar',
395
395
  'role',
396
+
396
397
  'locale',
397
398
  'extra',
398
399
  'lastLoginIp',
@@ -413,6 +414,8 @@ class User extends ExtendBase {
413
414
  if (updates.sourceAppPid) {
414
415
  delete updates.sourceAppPid;
415
416
  }
417
+ // 登录不再更新 locale
418
+ delete updates.locale;
416
419
  // update user, connectedAccount, passport
417
420
  updates.connectedAccounts = updateConnectedAccount(exist.connectedAccounts, user.connectedAccount);
418
421
  updated = await this.updateUser(exist.did, updates);
@@ -1,3 +1,4 @@
1
+ /* eslint-disable camelcase */
1
2
  /* eslint-disable no-await-in-loop */
2
3
 
3
4
  const fs = require('fs-extra');
@@ -27,7 +28,7 @@ const logger = require('@abtnode/logger')('@abtnode/core:util:blocklet');
27
28
  const pm2 = require('@abtnode/util/lib/async-pm2');
28
29
  const sleep = require('@abtnode/util/lib/sleep');
29
30
  const getPm2ProcessInfo = require('@abtnode/util/lib/get-pm2-process-info');
30
- const { formatEnv } = require('@abtnode/util/lib/security');
31
+ const { formatEnv, getSecurityNodeOptions } = require('@abtnode/util/lib/security');
31
32
  const ensureEndpointHealthy = require('@abtnode/util/lib/ensure-endpoint-healthy');
32
33
  const getFolderSize = require('@abtnode/util/lib/get-folder-size');
33
34
  const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
@@ -180,7 +181,9 @@ const getComponentDirs = (component, { dataDirs, ensure = false, ancestors = []
180
181
  };
181
182
 
182
183
  /**
183
- * @returns { cwd, script, args, environmentObj, interpreter, interpreterArgs }
184
+ * @param component {import('@abtnode/client').ComponentState & { environmentObj: {[key: string]: string } } }
185
+ * @returns {{cwd, script, args, environmentObj, interpreter, interpreterArgs}: { args: []}}
186
+ * @return {*}
184
187
  */
185
188
  const getComponentStartEngine = (component, { e2eMode = false } = {}) => {
186
189
  if (!hasStartEngine(component.meta)) {
@@ -208,8 +211,8 @@ const getComponentStartEngine = (component, { e2eMode = false } = {}) => {
208
211
  }
209
212
 
210
213
  let script = null;
211
- let interpreter = '';
212
- let interpreterArgs = '';
214
+ let interpreter;
215
+ let interpreterArgs = [];
213
216
  const environmentObj = {};
214
217
  let args = [];
215
218
 
@@ -226,11 +229,18 @@ const getComponentStartEngine = (component, { e2eMode = false } = {}) => {
226
229
 
227
230
  if (component.mode !== BLOCKLET_MODES.DEVELOPMENT) {
228
231
  const engine = getEngine(blockletEngineInfo.interpreter);
229
- interpreter = engine.interpreter === 'node' ? '' : engine.interpreter;
230
- interpreterArgs = engine.args || '';
232
+ interpreter = engine.interpreter === 'node' ? undefined : engine.interpreter;
233
+ interpreterArgs = interpreterArgs.concat(engine.args ? [engine.args] : []);
231
234
  }
232
235
 
233
- return { cwd, script, args, environmentObj, interpreter, interpreterArgs };
236
+ return {
237
+ cwd,
238
+ script,
239
+ args,
240
+ environmentObj,
241
+ interpreter,
242
+ interpreterArgs: interpreterArgs.join(' ').trim(),
243
+ };
234
244
  };
235
245
 
236
246
  const getBlockletConfigObj = (blocklet, { excludeSecure } = {}) => {
@@ -248,7 +258,6 @@ const getBlockletConfigObj = (blocklet, { excludeSecure } = {}) => {
248
258
 
249
259
  return obj;
250
260
  };
251
-
252
261
  /**
253
262
  * set 'configs', configObj', 'environmentObj' to blocklet TODO
254
263
  * @param {*} blocklet
@@ -531,6 +540,12 @@ const startBlockletProcess = async (
531
540
 
532
541
  await forEachBlocklet(
533
542
  blocklet,
543
+ /**
544
+ *
545
+ * @param {import('@abtnode/client').BlockletState} b
546
+ * @param {*} param1
547
+ * @returns
548
+ */
534
549
  async (b, { ancestors }) => {
535
550
  if (b.meta.group === BlockletGroup.gateway) {
536
551
  return;
@@ -572,10 +587,10 @@ const startBlockletProcess = async (
572
587
  await installExternalDependencies({ appDir: env?.BLOCKLET_APP_DIR });
573
588
 
574
589
  // run hook
575
- await preFlight(b, { env });
590
+ await preFlight(b, { env: { ...env } });
576
591
 
577
592
  // run hook
578
- await preStart(b, { env });
593
+ await preStart(b, { env: { ...env } });
579
594
 
580
595
  // kill process if port is occupied
581
596
  try {
@@ -588,6 +603,9 @@ const startBlockletProcess = async (
588
603
  // start process
589
604
  const maxMemoryRestart = get(nodeInfo, 'runtimeConfig.blockletMaxMemoryLimit', BLOCKLET_MAX_MEM_LIMIT_IN_MB);
590
605
 
606
+ /**
607
+ * @type {pm2.StartOptions}
608
+ */
591
609
  const options = {
592
610
  namespace: 'blocklets',
593
611
  name: processId,
@@ -606,6 +624,7 @@ const startBlockletProcess = async (
606
624
  ...env,
607
625
  NODE_ENV: 'production',
608
626
  BLOCKLET_START_AT: now,
627
+ NODE_OPTIONS: getSecurityNodeOptions(b, nodeInfo.enableFileSystemIsolation),
609
628
  },
610
629
  script,
611
630
  args,
@@ -1,4 +1,4 @@
1
- const joinUrl = require('url-join');
1
+ const { joinURL } = require('ufo');
2
2
  const axios = require('@abtnode/util/lib/axios');
3
3
  const { DEFAULT_IP_DOMAIN, WELLKNOWN_SERVER_ADMIN_PATH } = require('@abtnode/constant');
4
4
  const logger = require('@abtnode/logger')('@abtnode/core:util:get-accessible-external-node-ip');
@@ -15,7 +15,7 @@ const timeout = process.env.NODE_ENV === 'test' ? 500 : 5000;
15
15
  const checkConnected = async ({ ip, info }) => {
16
16
  const { adminPath = WELLKNOWN_SERVER_ADMIN_PATH } = info.routing || {};
17
17
  const origin = `https://${getNodeDomain(ip)}`;
18
- const endpoint = joinUrl(origin, adminPath);
18
+ const endpoint = joinURL(origin, adminPath);
19
19
  await axios.get(endpoint, { timeout });
20
20
  };
21
21
 
package/lib/util/index.js CHANGED
@@ -8,7 +8,7 @@ const camelCase = require('lodash/camelCase');
8
8
  const get = require('lodash/get');
9
9
  const pickBy = require('lodash/pickBy');
10
10
  const { isFromPublicKey } = require('@arcblock/did');
11
- const joinUrl = require('url-join');
11
+ const { joinURL } = require('ufo');
12
12
  const { Certificate } = require('@fidm/x509');
13
13
  const getPortLib = require('get-port');
14
14
  const v8 = require('v8');
@@ -362,7 +362,7 @@ const getStateCrons = (states) => [
362
362
 
363
363
  const getDelegateState = async (chainHost, address) => {
364
364
  const result = await axios.post(
365
- joinUrl(chainHost, '/gql/'),
365
+ joinURL(chainHost, '/gql/'),
366
366
  JSON.stringify({
367
367
  query: `{
368
368
  getDelegateState(address: "${address}") {
@@ -388,7 +388,7 @@ const getDelegateState = async (chainHost, address) => {
388
388
  };
389
389
 
390
390
  const getNFTState = async (chainHost, nftId) => {
391
- const url = joinUrl(new URL(chainHost).origin, '/api/gql/');
391
+ const url = joinURL(new URL(chainHost).origin, '/api/gql/');
392
392
 
393
393
  const result = await axios.post(
394
394
  url,
@@ -1,6 +1,6 @@
1
1
  const path = require('path');
2
2
  const fs = require('fs-extra');
3
- const joinUrl = require('url-join');
3
+ const { joinURL } = require('ufo');
4
4
  const dayjs = require('@abtnode/util/lib/dayjs');
5
5
  const pick = require('lodash/pick');
6
6
  const uniq = require('lodash/uniq');
@@ -206,7 +206,7 @@ const setupAppOwner = async (node, sessionId) => {
206
206
  throw new Error(`Owner user not found from launcher: ${launcherUrl}`);
207
207
  }
208
208
  appOwnerProfile = pick(user, ['fullName', 'email', 'avatar']);
209
- const avatarBase64 = await getAvatarByUrl(joinUrl(launcherUrl, user.avatar));
209
+ const avatarBase64 = await getAvatarByUrl(joinURL(launcherUrl, user.avatar));
210
210
  appOwnerProfile.avatar = await extractUserAvatar(avatarBase64, { dataDir });
211
211
  logger.info('Create owner from launcher for blocklet', { appDid, ownerDid, ownerPk, sessionId, appOwnerProfile });
212
212
  } else {
@@ -242,7 +242,7 @@ const setupAppOwner = async (node, sessionId) => {
242
242
  endpoint: appUrl,
243
243
  }),
244
244
  endpoint: getPassportStatusEndpoint({
245
- baseUrl: joinUrl(appUrl, WELLKNOWN_SERVICE_PATH_PREFIX),
245
+ baseUrl: joinURL(appUrl, WELLKNOWN_SERVICE_PATH_PREFIX),
246
246
  userDid: ownerDid,
247
247
  teamDid: appDid,
248
248
  }),
@@ -3,7 +3,7 @@ const { default: axios } = require('axios');
3
3
  const isUrl = require('is-url');
4
4
  const isArray = require('lodash/isArray');
5
5
  const isEmpty = require('lodash/isEmpty');
6
- const joinUrl = require('url-join');
6
+ const { joinURL, withQuery } = require('ufo');
7
7
 
8
8
  /**
9
9
  * @description
@@ -35,7 +35,9 @@ function getBackupFilesUrlFromEndpoint(endpoint) {
35
35
  const spaceDid = strArray.at(-4);
36
36
  const appDid = strArray.at(-2);
37
37
 
38
- return joinUrl(prefix, 'space', spaceDid, 'apps', appDid, 'explorer', `?key=/apps/${appDid}/.did-objects/${appDid}/`);
38
+ return withQuery(joinURL(prefix, 'space', spaceDid, 'apps', appDid, 'explorer'), {
39
+ key: `/apps/${appDid}/.did-objects/${appDid}/`,
40
+ });
39
41
  }
40
42
 
41
43
  function getDIDSpacesUrlFromEndpoint(endpoint) {
package/lib/util/store.js CHANGED
@@ -1,4 +1,4 @@
1
- const joinUrl = require('url-join');
1
+ const { joinURL, withQuery } = require('ufo');
2
2
  const pick = require('lodash/pick');
3
3
  const isBase64 = require('is-base64');
4
4
 
@@ -48,7 +48,9 @@ const fixAndVerifyMetaFromStore = (meta) => {
48
48
 
49
49
  const getStoreMeta = async (registry) => {
50
50
  try {
51
- const url = joinUrl(registry, BLOCKLET_STORE_META_PATH, `?__t__=${Date.now()}`);
51
+ const url = withQuery(joinURL(registry, BLOCKLET_STORE_META_PATH), {
52
+ __t__: Date.now(),
53
+ });
52
54
  const { data } = await request.get(url);
53
55
 
54
56
  if (!data) {
@@ -69,7 +71,7 @@ const getStoreMeta = async (registry) => {
69
71
  } else if (isBase64(logoUrl, { allowMime: true })) {
70
72
  result.logoUrl = logoUrl;
71
73
  } else {
72
- result.logoUrl = joinUrl(registry, logoUrl);
74
+ result.logoUrl = joinURL(registry, logoUrl);
73
75
  }
74
76
  }
75
77
 
@@ -124,14 +126,14 @@ const resolveTarballURL = ({ did, tarball = '', storeUrl = '' }) => {
124
126
  return '';
125
127
  }
126
128
 
127
- return joinUrl(storeUrl, 'api', 'blocklets', did, tarball);
129
+ return joinURL(storeUrl, 'api', 'blocklets', did, tarball);
128
130
  };
129
131
 
130
132
  const getBlockletMetaUrl = ({ did, storeUrl }) =>
131
- joinUrl(storeUrl, BLOCKLET_STORE_API_PREFIX, `/blocklets/${did}/blocklet.json`);
133
+ joinURL(storeUrl, BLOCKLET_STORE_API_PREFIX, `/blocklets/${did}/blocklet.json`);
132
134
 
133
135
  const getBlockletMeta = async ({ did, storeUrl }) => {
134
- const url = joinUrl(storeUrl, BLOCKLET_STORE_API_PREFIX, `/blocklets/${did}/blocklet.json?__t__=${Date.now()}`);
136
+ const url = joinURL(storeUrl, BLOCKLET_STORE_API_PREFIX, `/blocklets/${did}/blocklet.json?__t__=${Date.now()}`);
135
137
 
136
138
  const { data } = await request.get(url);
137
139
  try {
@@ -39,6 +39,7 @@ const nodeInfoSchema = Joi.object({
39
39
  }),
40
40
  autoUpgrade: Joi.boolean(),
41
41
  enableWelcomePage: Joi.boolean(),
42
+ enableFileSystemIsolation: Joi.boolean(),
42
43
  diskAlertThreshold: Joi.number()
43
44
  .label('disk alarm threshold')
44
45
  .max(99)
@@ -5,6 +5,7 @@ const note = Joi.string().min(1).max(PROJECT.MAX_NOTE_LENGTH);
5
5
  const blockletIntroduction = Joi.string().max(PROJECT.MAX_INTRO_LENGTH).allow('').allow(null);
6
6
  const blockletScreenshots = Joi.array().items(Joi.string().min(1).max(200));
7
7
  const blockletLogo = Joi.string().max(200).allow(null).allow('');
8
+ const uploadedResource = Joi.string().max(255).allow(null).allow('');
8
9
 
9
10
  const createReleaseSchema = (status) =>
10
11
  Joi.object({
@@ -24,6 +25,7 @@ const createReleaseSchema = (status) =>
24
25
  required: Joi.boolean().allow(null),
25
26
  })
26
27
  ),
28
+ uploadedResource,
27
29
  });
28
30
 
29
31
  module.exports = {
@@ -31,5 +33,6 @@ module.exports = {
31
33
  blockletIntroduction,
32
34
  blockletScreenshots,
33
35
  blockletLogo,
36
+ uploadedResource,
34
37
  createReleaseSchema,
35
38
  };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.16.28-next-5a717317",
6
+ "version": "1.16.28",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,19 +19,19 @@
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
20
  "license": "Apache-2.0",
21
21
  "dependencies": {
22
- "@abtnode/analytics": "1.16.28-next-5a717317",
23
- "@abtnode/auth": "1.16.28-next-5a717317",
24
- "@abtnode/certificate-manager": "1.16.28-next-5a717317",
25
- "@abtnode/constant": "1.16.28-next-5a717317",
26
- "@abtnode/cron": "1.16.28-next-5a717317",
27
- "@abtnode/logger": "1.16.28-next-5a717317",
28
- "@abtnode/models": "1.16.28-next-5a717317",
29
- "@abtnode/queue": "1.16.28-next-5a717317",
30
- "@abtnode/rbac": "1.16.28-next-5a717317",
31
- "@abtnode/router-provider": "1.16.28-next-5a717317",
32
- "@abtnode/static-server": "1.16.28-next-5a717317",
33
- "@abtnode/timemachine": "1.16.28-next-5a717317",
34
- "@abtnode/util": "1.16.28-next-5a717317",
22
+ "@abtnode/analytics": "1.16.28",
23
+ "@abtnode/auth": "1.16.28",
24
+ "@abtnode/certificate-manager": "1.16.28",
25
+ "@abtnode/constant": "1.16.28",
26
+ "@abtnode/cron": "1.16.28",
27
+ "@abtnode/logger": "1.16.28",
28
+ "@abtnode/models": "1.16.28",
29
+ "@abtnode/queue": "1.16.28",
30
+ "@abtnode/rbac": "1.16.28",
31
+ "@abtnode/router-provider": "1.16.28",
32
+ "@abtnode/static-server": "1.16.28",
33
+ "@abtnode/timemachine": "1.16.28",
34
+ "@abtnode/util": "1.16.28",
35
35
  "@arcblock/did": "1.18.123",
36
36
  "@arcblock/did-auth": "1.18.123",
37
37
  "@arcblock/did-ext": "^1.18.123",
@@ -42,20 +42,20 @@
42
42
  "@arcblock/pm2-events": "^0.0.5",
43
43
  "@arcblock/validator": "^1.18.123",
44
44
  "@arcblock/vc": "1.18.123",
45
- "@blocklet/constant": "1.16.28-next-5a717317",
46
- "@blocklet/env": "1.16.28-next-5a717317",
47
- "@blocklet/meta": "1.16.28-next-5a717317",
48
- "@blocklet/resolver": "1.16.28-next-5a717317",
49
- "@blocklet/sdk": "1.16.28-next-5a717317",
50
- "@blocklet/store": "1.16.28-next-5a717317",
51
- "@did-space/client": "^0.4.20",
45
+ "@blocklet/constant": "1.16.28",
46
+ "@blocklet/env": "1.16.28",
47
+ "@blocklet/meta": "1.16.28",
48
+ "@blocklet/resolver": "1.16.28",
49
+ "@blocklet/sdk": "1.16.28",
50
+ "@blocklet/store": "1.16.28",
51
+ "@did-space/client": "^0.5.1",
52
52
  "@fidm/x509": "^1.2.1",
53
53
  "@ocap/mcrypto": "1.18.123",
54
54
  "@ocap/util": "1.18.123",
55
55
  "@ocap/wallet": "1.18.123",
56
56
  "@slack/webhook": "^5.0.4",
57
57
  "archiver": "^7.0.1",
58
- "axios": "^0.27.2",
58
+ "axios": "^1.7.2",
59
59
  "axon": "^2.0.3",
60
60
  "chalk": "^4.1.2",
61
61
  "cross-spawn": "^7.0.3",
@@ -92,7 +92,7 @@
92
92
  "tar": "^6.1.11",
93
93
  "transliteration": "^2.3.5",
94
94
  "ua-parser-js": "^1.0.2",
95
- "url-join": "^4.0.1",
95
+ "ufo": "^1.5.3",
96
96
  "uuid": "^9.0.1",
97
97
  "valid-url": "^1.0.9",
98
98
  "xbytes": "^1.8.0"
@@ -103,5 +103,5 @@
103
103
  "jest": "^29.7.0",
104
104
  "unzipper": "^0.10.11"
105
105
  },
106
- "gitHead": "3624967f9549de3a25a87ed6b20f82519ddb4757"
106
+ "gitHead": "54db076a7e520bbc260f8cbf0af31dd50b86aef1"
107
107
  }