@abtnode/core 1.8.60 → 1.8.62
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 +7 -1
- package/lib/blocklet/manager/disk.js +26 -87
- package/lib/event.js +7 -4
- package/lib/router/helper.js +9 -3
- package/lib/router/index.js +4 -0
- package/lib/router/manager.js +40 -22
- package/lib/util/blocklet.js +132 -4
- package/lib/util/get-domain-for-blocklet.js +1 -3
- package/lib/validators/router.js +1 -1
- package/package.json +25 -25
package/lib/api/team.js
CHANGED
|
@@ -595,7 +595,13 @@ class TeamAPI extends EventEmitter {
|
|
|
595
595
|
async getPassportIssuances({ teamDid, ownerDid }) {
|
|
596
596
|
const state = await this.getSessionState(teamDid);
|
|
597
597
|
|
|
598
|
-
const
|
|
598
|
+
const query = { type: 'passport-issuance' };
|
|
599
|
+
|
|
600
|
+
if (ownerDid) {
|
|
601
|
+
query.ownerDid = ownerDid;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
const list = await state.find(query);
|
|
599
605
|
|
|
600
606
|
return list.map((d) => ({
|
|
601
607
|
// eslint-disable-next-line no-underscore-dangle
|
|
@@ -12,14 +12,12 @@ const capitalize = require('lodash/capitalize');
|
|
|
12
12
|
const { Throttle } = require('stream-throttle');
|
|
13
13
|
const LRU = require('lru-cache');
|
|
14
14
|
const joi = require('joi');
|
|
15
|
-
const isUrl = require('is-url');
|
|
16
15
|
const { isNFTExpired, getNftExpirationDate } = require('@abtnode/util/lib/nft');
|
|
17
16
|
const didDocument = require('@abtnode/util/lib/did-document');
|
|
18
17
|
const { sign } = require('@arcblock/jwt');
|
|
19
18
|
const { isValid: isValidDid } = require('@arcblock/did');
|
|
20
19
|
const { verifyPresentation } = require('@arcblock/vc');
|
|
21
|
-
const { toBase58
|
|
22
|
-
const { fromSecretKey } = require('@ocap/wallet');
|
|
20
|
+
const { toBase58 } = require('@ocap/util');
|
|
23
21
|
const { toSvg: createDidLogo } =
|
|
24
22
|
process.env.NODE_ENV !== 'test' ? require('@arcblock/did-motif') : require('@arcblock/did-motif/dist/did-motif.cjs');
|
|
25
23
|
const getBlockletInfo = require('@blocklet/meta/lib/info');
|
|
@@ -55,13 +53,7 @@ const validateBlockletEntry = require('@blocklet/meta/lib/entry');
|
|
|
55
53
|
const toBlockletDid = require('@blocklet/meta/lib/did');
|
|
56
54
|
const { validateMeta } = require('@blocklet/meta/lib/validate');
|
|
57
55
|
const { update: updateMetaFile } = require('@blocklet/meta/lib/file');
|
|
58
|
-
const {
|
|
59
|
-
titleSchema,
|
|
60
|
-
descriptionSchema,
|
|
61
|
-
mountPointSchema,
|
|
62
|
-
logoSchema,
|
|
63
|
-
environmentNameSchema,
|
|
64
|
-
} = require('@blocklet/meta/lib/schema');
|
|
56
|
+
const { titleSchema, mountPointSchema, environmentNameSchema } = require('@blocklet/meta/lib/schema');
|
|
65
57
|
const hasReservedKey = require('@blocklet/meta/lib/has-reserved-key');
|
|
66
58
|
|
|
67
59
|
const { toExternalBlocklet } = toBlockletDid;
|
|
@@ -86,7 +78,6 @@ const {
|
|
|
86
78
|
BLOCKLET_META_FILE,
|
|
87
79
|
BLOCKLET_CONFIGURABLE_KEY,
|
|
88
80
|
} = require('@blocklet/constant');
|
|
89
|
-
const { isEmpty } = require('lodash');
|
|
90
81
|
const util = require('../../util');
|
|
91
82
|
const {
|
|
92
83
|
refresh: refreshAccessibleExternalNodeIp,
|
|
@@ -125,6 +116,9 @@ const {
|
|
|
125
116
|
ensureEnvDefault,
|
|
126
117
|
getConfigFromPreferences,
|
|
127
118
|
consumeServerlessNFT,
|
|
119
|
+
validateAppConfig,
|
|
120
|
+
checkDuplicateAppSk,
|
|
121
|
+
checkDuplicateMountPoint,
|
|
128
122
|
} = require('../../util/blocklet');
|
|
129
123
|
const StoreUtil = require('../../util/store');
|
|
130
124
|
const states = require('../../states');
|
|
@@ -978,77 +972,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
978
972
|
throw new Error(`Cannot set ${x.key} to child blocklet`);
|
|
979
973
|
}
|
|
980
974
|
|
|
981
|
-
|
|
982
|
-
try {
|
|
983
|
-
fromSecretKey(x.value);
|
|
984
|
-
} catch {
|
|
985
|
-
try {
|
|
986
|
-
fromSecretKey(x.value, 'eth');
|
|
987
|
-
} catch {
|
|
988
|
-
throw new Error('Invalid custom blocklet secret key');
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
// Ensure sk is not used by other blocklets, otherwise we may encounter appDid collision
|
|
993
|
-
const blocklets = await states.blocklet.getBlocklets({});
|
|
994
|
-
const others = blocklets.filter((b) => b.meta.did !== did);
|
|
995
|
-
if (
|
|
996
|
-
others.some(
|
|
997
|
-
(b) => b.environments.find((e) => e.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK).value === x.value
|
|
998
|
-
)
|
|
999
|
-
) {
|
|
1000
|
-
throw new Error('Invalid custom blocklet secret key: already used by another blocklet');
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
if (x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_NAME) {
|
|
1005
|
-
x.value = await titleSchema.validateAsync(x.value);
|
|
1006
|
-
}
|
|
1007
|
-
|
|
1008
|
-
if (x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_DESCRIPTION) {
|
|
1009
|
-
x.value = await descriptionSchema.validateAsync(x.value);
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
if (
|
|
1013
|
-
[
|
|
1014
|
-
BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_LOGO,
|
|
1015
|
-
BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_LOGO_RECT,
|
|
1016
|
-
BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_LOGO_SQUARE,
|
|
1017
|
-
BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_LOGO_FAVICON,
|
|
1018
|
-
].includes(x.key)
|
|
1019
|
-
) {
|
|
1020
|
-
x.value = await logoSchema.validateAsync(x.value);
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
if (x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_WALLET_TYPE) {
|
|
1024
|
-
if (['default', 'eth'].includes(x.value) === false) {
|
|
1025
|
-
throw new Error('Invalid blocklet wallet type, only "default" and "eth" are supported');
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
if (x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_DELETABLE) {
|
|
1030
|
-
if (['yes', 'no'].includes(x.value) === false) {
|
|
1031
|
-
throw new Error('BLOCKLET_DELETABLE must be either "yes" or "no"');
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
if (x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_PASSPORT_COLOR) {
|
|
1036
|
-
if (x.value && x.value !== 'auto') {
|
|
1037
|
-
if (x.value.length !== 7 || !isHex(x.value.slice(-6))) {
|
|
1038
|
-
throw new Error('BLOCKLET_PASSPORT_COLOR must be a hex encoded color, eg. #ffeeaa');
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
if (x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SPACE_ENDPOINT) {
|
|
1044
|
-
if (isEmpty(x.value)) {
|
|
1045
|
-
throw new Error(`${x.key} can not be empty`);
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
|
-
if (!isUrl(x.value)) {
|
|
1049
|
-
throw new Error(`${x.key}(${x.value}) is not a valid http address`);
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
975
|
+
await validateAppConfig(x, rootDid, states);
|
|
1052
976
|
} else if (!BLOCKLET_CONFIGURABLE_KEY[x.key] && !isPreferenceKey(x)) {
|
|
1053
977
|
if (!(blocklet.meta.environments || []).some((y) => y.name === x.key)) {
|
|
1054
978
|
// forbid unknown format key
|
|
@@ -1221,20 +1145,26 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1221
1145
|
}
|
|
1222
1146
|
|
|
1223
1147
|
const rootDid = blocklet.meta.did;
|
|
1148
|
+
const isRootComponent = !did;
|
|
1224
1149
|
|
|
1225
|
-
const
|
|
1226
|
-
const component = children.find((x) => x.meta.did === did);
|
|
1227
|
-
|
|
1150
|
+
const component = isRootComponent ? blocklet : blocklet.children.find((x) => x.meta.did === did);
|
|
1228
1151
|
if (!component) {
|
|
1229
1152
|
throw new Error('component does not exist');
|
|
1230
1153
|
}
|
|
1231
1154
|
|
|
1232
|
-
if (!component.dynamic) {
|
|
1155
|
+
if (!isRootComponent && !component.dynamic) {
|
|
1233
1156
|
throw new Error('cannot update mountPoint of non-dynamic component');
|
|
1234
1157
|
}
|
|
1235
1158
|
|
|
1159
|
+
if (isRootComponent && component.group === BlockletGroup.gateway) {
|
|
1160
|
+
throw new Error('cannot update mountPoint of gateway blocklet');
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
checkDuplicateMountPoint(blocklet, mountPoint);
|
|
1164
|
+
|
|
1236
1165
|
component.mountPoint = mountPoint;
|
|
1237
|
-
|
|
1166
|
+
|
|
1167
|
+
await states.blocklet.updateBlocklet(rootDid, { mountPoint: blocklet.mountPoint, children: blocklet.children });
|
|
1238
1168
|
|
|
1239
1169
|
this.emit(BlockletEvents.upgraded, { blocklet, context: { ...context, createAuditLog: false } }); // trigger router refresh
|
|
1240
1170
|
|
|
@@ -2362,6 +2292,9 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2362
2292
|
});
|
|
2363
2293
|
await this._setConfigsFromMeta(did);
|
|
2364
2294
|
|
|
2295
|
+
// check duplicate appSk
|
|
2296
|
+
await checkDuplicateAppSk({ did, states });
|
|
2297
|
+
|
|
2365
2298
|
// fake install bundle
|
|
2366
2299
|
const bundleDir = getBundleDir(this.installDir, meta);
|
|
2367
2300
|
fs.mkdirSync(bundleDir, { recursive: true });
|
|
@@ -2479,6 +2412,9 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2479
2412
|
await this._setConfigsFromMeta(meta.did);
|
|
2480
2413
|
await validateBlocklet(blocklet);
|
|
2481
2414
|
|
|
2415
|
+
// check duplicate appSk
|
|
2416
|
+
await checkDuplicateAppSk({ did: meta.did, states });
|
|
2417
|
+
|
|
2482
2418
|
// download
|
|
2483
2419
|
await this._downloadBlocklet(
|
|
2484
2420
|
blocklet,
|
|
@@ -2743,6 +2679,9 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2743
2679
|
|
|
2744
2680
|
await this._setConfigsFromMeta(did);
|
|
2745
2681
|
|
|
2682
|
+
// check duplicate appSk
|
|
2683
|
+
await checkDuplicateAppSk({ did, states });
|
|
2684
|
+
|
|
2746
2685
|
logger.info('blocklet added to database', { did: meta.did });
|
|
2747
2686
|
|
|
2748
2687
|
const blocklet1 = await states.blocklet.setBlockletStatus(did, BlockletStatus.waiting);
|
package/lib/event.js
CHANGED
|
@@ -11,6 +11,7 @@ const eventHub =
|
|
|
11
11
|
process.env.NODE_ENV === 'test' ? require('@arcblock/event-hub/single') : require('@arcblock/event-hub');
|
|
12
12
|
|
|
13
13
|
const states = require('./states');
|
|
14
|
+
const { isBeforeInstalled } = require('./util');
|
|
14
15
|
|
|
15
16
|
const routingSnapshotPrefix = (blocklet) => (blocklet.mode === BLOCKLET_MODES.DEVELOPMENT ? '[DEV] ' : '');
|
|
16
17
|
|
|
@@ -237,10 +238,12 @@ module.exports = ({
|
|
|
237
238
|
logger.info('take snapshot after updated blocklet app', { event: eventName, did: blocklet.meta.did, hash });
|
|
238
239
|
}
|
|
239
240
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
241
|
+
if (blocklet.status && !isBeforeInstalled(blocklet.status)) {
|
|
242
|
+
try {
|
|
243
|
+
await blockletManager.runtimeMonitor.monit(blocklet.meta.did);
|
|
244
|
+
} catch (error) {
|
|
245
|
+
logger.error('monit runtime info failed', { eventName, error });
|
|
246
|
+
}
|
|
244
247
|
}
|
|
245
248
|
|
|
246
249
|
if (payload.blocklet && !payload.meta) {
|
package/lib/router/helper.js
CHANGED
|
@@ -258,13 +258,19 @@ const ensureWellknownRule = async (sites) => {
|
|
|
258
258
|
if (blockletRules.length) {
|
|
259
259
|
// get pathPrefix for blocklet-service
|
|
260
260
|
const rootBlockletRule = blockletRules.find((x) => x.to.did === x.to.componentId);
|
|
261
|
-
|
|
261
|
+
|
|
262
|
+
const servicePathPrefix = joinUrl(
|
|
263
|
+
// rootBlockletRule?.from?.pathPrefix is for backwards compatibility
|
|
264
|
+
// rootBlockletRule?.from?.groupPathPrefix 在旧的场景中可能不为 '/' (blocklet 只能挂载在 relative prefix 下)
|
|
265
|
+
rootBlockletRule?.from?.groupPathPrefix || rootBlockletRule?.from?.pathPrefix || '/',
|
|
266
|
+
WELLKNOWN_SERVICE_PATH_PREFIX
|
|
267
|
+
);
|
|
262
268
|
|
|
263
269
|
// requests for /.well-known/service will stay in blocklet-service and never proxy back to blocklet
|
|
264
270
|
// so any rule is ok to be cloned
|
|
265
|
-
if (!site.rules.some((x) => x.from.pathPrefix ===
|
|
271
|
+
if (!site.rules.some((x) => x.from.pathPrefix === servicePathPrefix)) {
|
|
266
272
|
const rule = cloneDeep(rootBlockletRule || blockletRules[0]);
|
|
267
|
-
rule.from.pathPrefix =
|
|
273
|
+
rule.from.pathPrefix = servicePathPrefix;
|
|
268
274
|
rule.isProtected = true;
|
|
269
275
|
site.rules.push(rule);
|
|
270
276
|
}
|
package/lib/router/index.js
CHANGED
package/lib/router/manager.js
CHANGED
|
@@ -11,6 +11,7 @@ const get = require('lodash/get');
|
|
|
11
11
|
const { EventEmitter } = require('events');
|
|
12
12
|
const uuid = require('uuid');
|
|
13
13
|
const isUrl = require('is-url');
|
|
14
|
+
const joinUrl = require('url-join');
|
|
14
15
|
const cloneDeep = require('lodash/cloneDeep');
|
|
15
16
|
const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
|
|
16
17
|
const logger = require('@abtnode/logger')('@abtnode/core:router:manager');
|
|
@@ -570,7 +571,8 @@ class RouterManager extends EventEmitter {
|
|
|
570
571
|
rule.groupId = rule.id;
|
|
571
572
|
rule.from.pathPrefix = normalizePathPrefix(rule.from.pathPrefix);
|
|
572
573
|
if (rule.to.type === ROUTING_RULE_TYPES.BLOCKLET) {
|
|
573
|
-
|
|
574
|
+
// pathPrefix of root blocklet maybe changed to another prefix than '/', so use old groupPathPrefix first
|
|
575
|
+
rule.from.groupPathPrefix = rule.from.groupPathPrefix || rule.from.pathPrefix;
|
|
574
576
|
rule.to.componentId = rule.to.did;
|
|
575
577
|
}
|
|
576
578
|
if (rule.to.url) {
|
|
@@ -580,18 +582,32 @@ class RouterManager extends EventEmitter {
|
|
|
580
582
|
|
|
581
583
|
/**
|
|
582
584
|
* get all rules to be add or update to site from root rule
|
|
583
|
-
* @param {*}
|
|
585
|
+
* @param {*} rootRule
|
|
584
586
|
*/
|
|
585
|
-
async getRulesForMutation(
|
|
586
|
-
if (
|
|
587
|
-
return [
|
|
587
|
+
async getRulesForMutation(rootRule) {
|
|
588
|
+
if (rootRule.to.type !== ROUTING_RULE_TYPES.BLOCKLET) {
|
|
589
|
+
return [rootRule];
|
|
588
590
|
}
|
|
589
591
|
|
|
590
592
|
const rules = [];
|
|
591
|
-
let occupied = false;
|
|
592
593
|
|
|
593
594
|
// get child rules
|
|
594
|
-
const blocklet = await states.blocklet.getBlocklet(
|
|
595
|
+
const blocklet = await states.blocklet.getBlocklet(rootRule.to.did);
|
|
596
|
+
|
|
597
|
+
// blocklet may be mounted in relative prefix (for old usage), so blockletPrefix may not be '/'
|
|
598
|
+
// blocklet prefix is the origin pathPrefix in rootRule
|
|
599
|
+
const blockletPrefix = normalizePathPrefix(rootRule.from.pathPrefix);
|
|
600
|
+
|
|
601
|
+
// root component's mountPoint may not be '/'
|
|
602
|
+
const rootComponentPrefix = joinUrl(blockletPrefix, blocklet.mountPoint || '/');
|
|
603
|
+
rootRule.from.pathPrefix = normalizePathPrefix(rootComponentPrefix);
|
|
604
|
+
|
|
605
|
+
const isOccupiable = blocklet.meta.group === BlockletGroup.gateway;
|
|
606
|
+
|
|
607
|
+
if (!isOccupiable) {
|
|
608
|
+
rules.push(rootRule);
|
|
609
|
+
}
|
|
610
|
+
|
|
595
611
|
forEachChildSync(blocklet, (component, { id, ancestors }) => {
|
|
596
612
|
if (component.meta.group === BlockletGroup.gateway) {
|
|
597
613
|
return;
|
|
@@ -611,38 +627,40 @@ class RouterManager extends EventEmitter {
|
|
|
611
627
|
return;
|
|
612
628
|
}
|
|
613
629
|
|
|
614
|
-
const pathPrefix = path.join(
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
630
|
+
const pathPrefix = path.join(
|
|
631
|
+
blockletPrefix,
|
|
632
|
+
// level 1 component should be independent with root component
|
|
633
|
+
...ancestors.slice(1).map((x) => x.mountPoint || ''),
|
|
634
|
+
mountPoint
|
|
635
|
+
);
|
|
636
|
+
|
|
637
|
+
const occupied = normalizePathPrefix(pathPrefix) === normalizePathPrefix(rootRule.from.pathPrefix);
|
|
638
|
+
|
|
639
|
+
if (occupied && !isOccupiable) {
|
|
640
|
+
return;
|
|
618
641
|
}
|
|
619
642
|
|
|
620
643
|
// if is root path, child rule become root rule
|
|
621
644
|
const childRule = {
|
|
622
|
-
id:
|
|
623
|
-
groupId:
|
|
645
|
+
id: occupied ? rootRule.id : uuid.v4(),
|
|
646
|
+
groupId: rootRule.id,
|
|
624
647
|
from: {
|
|
625
648
|
pathPrefix: normalizePathPrefix(pathPrefix),
|
|
626
|
-
groupPathPrefix:
|
|
649
|
+
groupPathPrefix: blockletPrefix,
|
|
627
650
|
},
|
|
628
651
|
to: {
|
|
629
652
|
type: ROUTING_RULE_TYPES.BLOCKLET,
|
|
630
653
|
port: findInterfacePortByName(component, childWebInterface.name),
|
|
631
|
-
did:
|
|
632
|
-
interfaceName:
|
|
654
|
+
did: rootRule.to.did, // root component did
|
|
655
|
+
interfaceName: rootRule.to.interfaceName, // root component interface
|
|
633
656
|
componentId: id,
|
|
634
657
|
},
|
|
635
|
-
isProtected:
|
|
658
|
+
isProtected: occupied ? rootRule.isProtected : true,
|
|
636
659
|
};
|
|
637
660
|
|
|
638
661
|
rules.push(childRule);
|
|
639
662
|
});
|
|
640
663
|
|
|
641
|
-
// get root rule
|
|
642
|
-
if (!occupied && blocklet.meta.group !== BlockletGroup.gateway) {
|
|
643
|
-
rules.push(rule);
|
|
644
|
-
}
|
|
645
|
-
|
|
646
664
|
return rules;
|
|
647
665
|
}
|
|
648
666
|
}
|
package/lib/util/blocklet.js
CHANGED
|
@@ -7,16 +7,18 @@ const shelljs = require('shelljs');
|
|
|
7
7
|
const os = require('os');
|
|
8
8
|
const tar = require('tar');
|
|
9
9
|
const get = require('lodash/get');
|
|
10
|
+
const isEmpty = require('lodash/isEmpty');
|
|
10
11
|
const streamToPromise = require('stream-to-promise');
|
|
11
12
|
const { Throttle } = require('stream-throttle');
|
|
12
13
|
const ssri = require('ssri');
|
|
13
14
|
const diff = require('deep-diff');
|
|
14
15
|
const createArchive = require('archiver');
|
|
16
|
+
const isUrl = require('is-url');
|
|
15
17
|
const axios = require('@abtnode/util/lib/axios');
|
|
16
18
|
const { stableStringify } = require('@arcblock/vc');
|
|
17
19
|
|
|
18
20
|
const { fromSecretKey, WalletType } = require('@ocap/wallet');
|
|
19
|
-
const { toHex, toBase58 } = require('@ocap/util');
|
|
21
|
+
const { toHex, toBase58, isHex } = require('@ocap/util');
|
|
20
22
|
const { types } = require('@ocap/mcrypto');
|
|
21
23
|
const { isValid: isValidDid } = require('@arcblock/did');
|
|
22
24
|
const logger = require('@abtnode/logger')('@abtnode/core:util:blocklet');
|
|
@@ -67,7 +69,7 @@ const {
|
|
|
67
69
|
isEnvShareable,
|
|
68
70
|
} = require('@blocklet/meta/lib/util');
|
|
69
71
|
const toBlockletDid = require('@blocklet/meta/lib/did');
|
|
70
|
-
const { titleSchema, descriptionSchema } = require('@blocklet/meta/lib/schema');
|
|
72
|
+
const { titleSchema, descriptionSchema, logoSchema } = require('@blocklet/meta/lib/schema');
|
|
71
73
|
const {
|
|
72
74
|
getSourceUrlsFromConfig,
|
|
73
75
|
getBlockletMetaFromUrls,
|
|
@@ -1017,8 +1019,8 @@ const pruneBlockletBundle = async ({ blocklets, installDir, blockletSettings })
|
|
|
1017
1019
|
const bundleName = arr[length - 2];
|
|
1018
1020
|
const scopeName = length > 2 ? arr[length - 3] : '';
|
|
1019
1021
|
const bundleDir = path.join(installDir, scopeName, bundleName);
|
|
1020
|
-
const
|
|
1021
|
-
if (
|
|
1022
|
+
const isDirEmpty = (await fs.promises.readdir(bundleDir)).length === 0;
|
|
1023
|
+
if (isDirEmpty) {
|
|
1022
1024
|
logger.info('Remove bundle folder', { bundleDir });
|
|
1023
1025
|
await fs.remove(bundleDir);
|
|
1024
1026
|
}
|
|
@@ -1513,6 +1515,129 @@ const createDataArchive = (dataDir, fileName) => {
|
|
|
1513
1515
|
});
|
|
1514
1516
|
};
|
|
1515
1517
|
|
|
1518
|
+
/**
|
|
1519
|
+
* this function has side effect on config.value
|
|
1520
|
+
* @param {{ key: string, value?: string }} config
|
|
1521
|
+
*/
|
|
1522
|
+
const validateAppConfig = async (config, blockletDid, states) => {
|
|
1523
|
+
const x = config;
|
|
1524
|
+
|
|
1525
|
+
if (x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK) {
|
|
1526
|
+
if (x.value) {
|
|
1527
|
+
try {
|
|
1528
|
+
fromSecretKey(x.value);
|
|
1529
|
+
} catch {
|
|
1530
|
+
try {
|
|
1531
|
+
fromSecretKey(x.value, 'eth');
|
|
1532
|
+
} catch {
|
|
1533
|
+
throw new Error('Invalid custom blocklet secret key');
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
// Ensure sk is not used by other blocklets, otherwise we may encounter appDid collision
|
|
1538
|
+
const blocklets = await states.blocklet.getBlocklets({});
|
|
1539
|
+
const others = blocklets.filter((b) => b.meta.did !== blockletDid);
|
|
1540
|
+
if (
|
|
1541
|
+
others.some(
|
|
1542
|
+
(b) => b.environments.find((e) => e.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK).value === x.value
|
|
1543
|
+
)
|
|
1544
|
+
) {
|
|
1545
|
+
throw new Error('Invalid custom blocklet secret key: already used by another blocklet');
|
|
1546
|
+
}
|
|
1547
|
+
} else {
|
|
1548
|
+
delete x.value;
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
if (x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_NAME) {
|
|
1553
|
+
x.value = await titleSchema.validateAsync(x.value);
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
if (x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_DESCRIPTION) {
|
|
1557
|
+
x.value = await descriptionSchema.validateAsync(x.value);
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
if (
|
|
1561
|
+
[
|
|
1562
|
+
BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_LOGO,
|
|
1563
|
+
BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_LOGO_RECT,
|
|
1564
|
+
BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_LOGO_SQUARE,
|
|
1565
|
+
BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_LOGO_FAVICON,
|
|
1566
|
+
].includes(x.key)
|
|
1567
|
+
) {
|
|
1568
|
+
x.value = await logoSchema.validateAsync(x.value);
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
if (x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_WALLET_TYPE) {
|
|
1572
|
+
if (['default', 'eth'].includes(x.value) === false) {
|
|
1573
|
+
throw new Error('Invalid blocklet wallet type, only "default" and "eth" are supported');
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
if (x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_DELETABLE) {
|
|
1578
|
+
if (['yes', 'no'].includes(x.value) === false) {
|
|
1579
|
+
throw new Error('BLOCKLET_DELETABLE must be either "yes" or "no"');
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
if (x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_PASSPORT_COLOR) {
|
|
1584
|
+
if (x.value && x.value !== 'auto') {
|
|
1585
|
+
if (x.value.length !== 7 || !isHex(x.value.slice(-6))) {
|
|
1586
|
+
throw new Error('BLOCKLET_PASSPORT_COLOR must be a hex encoded color, eg. #ffeeaa');
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
if (x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SPACE_ENDPOINT) {
|
|
1592
|
+
if (isEmpty(x.value)) {
|
|
1593
|
+
throw new Error(`${x.key} can not be empty`);
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
if (!isUrl(x.value)) {
|
|
1597
|
+
throw new Error(`${x.key}(${x.value}) is not a valid http address`);
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
};
|
|
1601
|
+
|
|
1602
|
+
const checkDuplicateAppSk = async ({ did, states }) => {
|
|
1603
|
+
const nodeInfo = await states.node.read();
|
|
1604
|
+
const blocklets = await states.blocklet.getBlocklets({});
|
|
1605
|
+
const blocklet = await states.blocklet.getBlocklet(did);
|
|
1606
|
+
const configs = await states.blockletExtras.getConfigs([did]);
|
|
1607
|
+
|
|
1608
|
+
const { wallet } = getBlockletInfo(
|
|
1609
|
+
{
|
|
1610
|
+
meta: blocklet.meta,
|
|
1611
|
+
environments: (configs || []).filter((x) => x.value),
|
|
1612
|
+
},
|
|
1613
|
+
nodeInfo.sk
|
|
1614
|
+
);
|
|
1615
|
+
|
|
1616
|
+
const others = blocklets.filter((b) => b.meta.did !== did);
|
|
1617
|
+
|
|
1618
|
+
const exist = others.find((b) => {
|
|
1619
|
+
const item = (b.environments || []).find((e) => e.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK);
|
|
1620
|
+
return item?.value === toHex(wallet.secretKey);
|
|
1621
|
+
});
|
|
1622
|
+
|
|
1623
|
+
if (exist) {
|
|
1624
|
+
throw new Error(`blocklet secret key already used by ${exist.meta.title || exist.meta.name}`);
|
|
1625
|
+
}
|
|
1626
|
+
};
|
|
1627
|
+
|
|
1628
|
+
const checkDuplicateMountPoint = (blocklet, mountPoint) => {
|
|
1629
|
+
const err = new Error(`cannot add duplicate mount point, ${mountPoint || '/'} already exist`);
|
|
1630
|
+
if (normalizePathPrefix(blocklet.mountPoint) === normalizePathPrefix(mountPoint)) {
|
|
1631
|
+
throw err;
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
for (const component of blocklet.children || []) {
|
|
1635
|
+
if (normalizePathPrefix(component.mountPoint) === normalizePathPrefix(mountPoint)) {
|
|
1636
|
+
throw err;
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
};
|
|
1640
|
+
|
|
1516
1641
|
module.exports = {
|
|
1517
1642
|
consumeServerlessNFT,
|
|
1518
1643
|
forEachBlocklet,
|
|
@@ -1556,4 +1681,7 @@ module.exports = {
|
|
|
1556
1681
|
ensureEnvDefault,
|
|
1557
1682
|
getConfigFromPreferences,
|
|
1558
1683
|
createDataArchive,
|
|
1684
|
+
validateAppConfig,
|
|
1685
|
+
checkDuplicateAppSk,
|
|
1686
|
+
checkDuplicateMountPoint,
|
|
1559
1687
|
};
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
const slugify = require('slugify');
|
|
2
1
|
const { DEFAULT_IP_DOMAIN_SUFFIX } = require('@abtnode/constant');
|
|
3
2
|
const { encode: encodeBase32 } = require('@abtnode/util/lib/base32');
|
|
3
|
+
const formatName = require('@abtnode/util/lib/format-name');
|
|
4
4
|
|
|
5
5
|
const SLOT_FOR_IP_DNS_SITE = '888-888-888-888';
|
|
6
6
|
|
|
7
|
-
const formatName = (name) => slugify(name.replace(/^[@./-]/, '').replace(/[@./_]/g, '-'));
|
|
8
|
-
|
|
9
7
|
const hiddenInterfaceNames = ['public', 'publicUrl'];
|
|
10
8
|
|
|
11
9
|
const getIpDnsDomainForBlocklet = (blocklet, blockletInterface) => {
|
package/lib/validators/router.js
CHANGED
|
@@ -47,7 +47,7 @@ const ruleSchema = {
|
|
|
47
47
|
port: Joi.number().label('port').port().when('type', { is: ROUTING_RULE_TYPES.BLOCKLET, then: Joi.required() }),
|
|
48
48
|
url: Joi.string().label('url').when('type', { is: ROUTING_RULE_TYPES.REDIRECT, then: Joi.required() }),
|
|
49
49
|
redirectCode: Joi.alternatives()
|
|
50
|
-
.try(301, 302)
|
|
50
|
+
.try(301, 302, 307, 308)
|
|
51
51
|
.label('redirect code')
|
|
52
52
|
.when('type', { is: ROUTING_RULE_TYPES.REDIRECT, then: Joi.required() }),
|
|
53
53
|
interfaceName: Joi.string() // root interface
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.8.
|
|
6
|
+
"version": "1.8.62",
|
|
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.
|
|
23
|
-
"@abtnode/certificate-manager": "1.8.
|
|
24
|
-
"@abtnode/constant": "1.8.
|
|
25
|
-
"@abtnode/cron": "1.8.
|
|
26
|
-
"@abtnode/db": "1.8.
|
|
27
|
-
"@abtnode/logger": "1.8.
|
|
28
|
-
"@abtnode/queue": "1.8.
|
|
29
|
-
"@abtnode/rbac": "1.8.
|
|
30
|
-
"@abtnode/router-provider": "1.8.
|
|
31
|
-
"@abtnode/static-server": "1.8.
|
|
32
|
-
"@abtnode/timemachine": "1.8.
|
|
33
|
-
"@abtnode/util": "1.8.
|
|
34
|
-
"@arcblock/did": "1.18.
|
|
22
|
+
"@abtnode/auth": "1.8.62",
|
|
23
|
+
"@abtnode/certificate-manager": "1.8.62",
|
|
24
|
+
"@abtnode/constant": "1.8.62",
|
|
25
|
+
"@abtnode/cron": "1.8.62",
|
|
26
|
+
"@abtnode/db": "1.8.62",
|
|
27
|
+
"@abtnode/logger": "1.8.62",
|
|
28
|
+
"@abtnode/queue": "1.8.62",
|
|
29
|
+
"@abtnode/rbac": "1.8.62",
|
|
30
|
+
"@abtnode/router-provider": "1.8.62",
|
|
31
|
+
"@abtnode/static-server": "1.8.62",
|
|
32
|
+
"@abtnode/timemachine": "1.8.62",
|
|
33
|
+
"@abtnode/util": "1.8.62",
|
|
34
|
+
"@arcblock/did": "1.18.36",
|
|
35
35
|
"@arcblock/did-motif": "^1.1.10",
|
|
36
|
-
"@arcblock/did-util": "1.18.
|
|
37
|
-
"@arcblock/event-hub": "1.18.
|
|
38
|
-
"@arcblock/jwt": "^1.18.
|
|
36
|
+
"@arcblock/did-util": "1.18.36",
|
|
37
|
+
"@arcblock/event-hub": "1.18.36",
|
|
38
|
+
"@arcblock/jwt": "^1.18.36",
|
|
39
39
|
"@arcblock/pm2-events": "^0.0.5",
|
|
40
|
-
"@arcblock/vc": "1.18.
|
|
41
|
-
"@blocklet/constant": "1.8.
|
|
42
|
-
"@blocklet/meta": "1.8.
|
|
43
|
-
"@blocklet/sdk": "1.8.
|
|
40
|
+
"@arcblock/vc": "1.18.36",
|
|
41
|
+
"@blocklet/constant": "1.8.62",
|
|
42
|
+
"@blocklet/meta": "1.8.62",
|
|
43
|
+
"@blocklet/sdk": "1.8.62",
|
|
44
44
|
"@fidm/x509": "^1.2.1",
|
|
45
|
-
"@ocap/mcrypto": "1.18.
|
|
46
|
-
"@ocap/util": "1.18.
|
|
47
|
-
"@ocap/wallet": "1.18.
|
|
45
|
+
"@ocap/mcrypto": "1.18.36",
|
|
46
|
+
"@ocap/util": "1.18.36",
|
|
47
|
+
"@ocap/wallet": "1.18.36",
|
|
48
48
|
"@slack/webhook": "^5.0.4",
|
|
49
49
|
"archiver": "^5.3.1",
|
|
50
50
|
"axios": "^0.27.2",
|
|
@@ -85,5 +85,5 @@
|
|
|
85
85
|
"express": "^4.18.2",
|
|
86
86
|
"jest": "^27.5.1"
|
|
87
87
|
},
|
|
88
|
-
"gitHead": "
|
|
88
|
+
"gitHead": "ea6ab48149eb9e1157ef37ab1505fa36a9ce598f"
|
|
89
89
|
}
|