@abtnode/core 1.8.69-beta-54faead3 → 1.8.69-beta-76f8a46f
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 +2 -2
- package/lib/blocklet/downloader/resolve-download.js +31 -0
- package/lib/blocklet/manager/base.js +8 -16
- package/lib/blocklet/manager/disk.js +367 -1659
- package/lib/blocklet/manager/helper/{install-from-backup.js → install-application-from-backup.js} +26 -19
- package/lib/blocklet/manager/helper/install-application-from-dev.js +94 -0
- package/lib/blocklet/manager/helper/install-application-from-general.js +188 -0
- package/lib/blocklet/manager/helper/install-component-from-dev.js +80 -0
- package/lib/blocklet/manager/helper/install-component-from-upload.js +181 -0
- package/lib/blocklet/manager/helper/install-component-from-url.js +173 -0
- package/lib/blocklet/manager/helper/migrate-application-to-struct-v2.js +377 -0
- package/lib/blocklet/manager/helper/upgrade-components.js +152 -0
- package/lib/blocklet/storage/backup/spaces.js +20 -12
- package/lib/index.js +7 -7
- package/lib/router/helper.js +5 -7
- package/lib/states/blocklet-extras.js +44 -0
- package/lib/states/blocklet.js +56 -4
- package/lib/states/node.js +1 -0
- package/lib/states/site.js +15 -6
- package/lib/team/manager.js +5 -0
- package/lib/util/blocklet.js +177 -132
- package/lib/util/get-domain-for-blocklet.js +5 -14
- package/lib/util/get-meta-from-url.js +33 -0
- package/lib/util/index.js +0 -5
- package/lib/util/store.js +44 -6
- package/lib/webhook/sender/wallet/index.js +3 -0
- package/package.json +26 -26
package/lib/util/blocklet.js
CHANGED
|
@@ -14,11 +14,12 @@ const ssri = require('ssri');
|
|
|
14
14
|
const diff = require('deep-diff');
|
|
15
15
|
const createArchive = require('archiver');
|
|
16
16
|
const isUrl = require('is-url');
|
|
17
|
+
const semver = require('semver');
|
|
17
18
|
const axios = require('@abtnode/util/lib/axios');
|
|
18
19
|
const { stableStringify } = require('@arcblock/vc');
|
|
19
20
|
|
|
20
21
|
const { fromSecretKey, WalletType } = require('@ocap/wallet');
|
|
21
|
-
const { toHex, toBase58, isHex } = require('@ocap/util');
|
|
22
|
+
const { toHex, toBase58, isHex, toDid } = require('@ocap/util');
|
|
22
23
|
const { types } = require('@ocap/mcrypto');
|
|
23
24
|
const { isValid: isValidDid } = require('@arcblock/did');
|
|
24
25
|
const logger = require('@abtnode/logger')('@abtnode/core:util:blocklet');
|
|
@@ -30,12 +31,12 @@ const CustomError = require('@abtnode/util/lib/custom-error');
|
|
|
30
31
|
const getFolderSize = require('@abtnode/util/lib/get-folder-size');
|
|
31
32
|
const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
|
|
32
33
|
const hashFiles = require('@abtnode/util/lib/hash-files');
|
|
33
|
-
const isPathPrefixEqual = require('@abtnode/util/lib/is-path-prefix-equal');
|
|
34
34
|
const {
|
|
35
35
|
BLOCKLET_MAX_MEM_LIMIT_IN_MB,
|
|
36
36
|
BLOCKLET_STORE,
|
|
37
37
|
BLOCKLET_INSTALL_TYPE,
|
|
38
38
|
BLOCKLET_STORE_DEV,
|
|
39
|
+
APP_STRUCT_VERSION,
|
|
39
40
|
} = require('@abtnode/constant');
|
|
40
41
|
const formatBackSlash = require('@abtnode/util/lib/format-back-slash');
|
|
41
42
|
|
|
@@ -52,12 +53,10 @@ const {
|
|
|
52
53
|
BLOCKLET_DEFAULT_PORT_NAME,
|
|
53
54
|
BLOCKLET_INTERFACE_TYPE_WEB,
|
|
54
55
|
BLOCKLET_CONFIGURABLE_KEY,
|
|
55
|
-
BLOCKLET_DYNAMIC_PATH_PREFIX,
|
|
56
56
|
fromBlockletStatus,
|
|
57
57
|
BLOCKLET_PREFERENCE_FILE,
|
|
58
58
|
BLOCKLET_PREFERENCE_PREFIX,
|
|
59
59
|
} = require('@blocklet/constant');
|
|
60
|
-
const verifyMultiSig = require('@blocklet/meta/lib/verify-multi-sig');
|
|
61
60
|
const validateBlockletEntry = require('@blocklet/meta/lib/entry');
|
|
62
61
|
const getBlockletEngine = require('@blocklet/meta/lib/engine');
|
|
63
62
|
const getBlockletInfo = require('@blocklet/meta/lib/info');
|
|
@@ -91,13 +90,16 @@ const {
|
|
|
91
90
|
isBeforeInstalled,
|
|
92
91
|
expandBundle,
|
|
93
92
|
findInterfacePortByName,
|
|
94
|
-
validateBlockletMeta,
|
|
95
93
|
prettyURL,
|
|
96
94
|
getNFTState,
|
|
97
95
|
templateReplace,
|
|
98
96
|
} = require('./index');
|
|
99
97
|
|
|
100
|
-
const getComponentConfig = (meta) =>
|
|
98
|
+
const getComponentConfig = (meta) => {
|
|
99
|
+
const components = meta.components || meta.children || [];
|
|
100
|
+
const staticComponents = (meta.staticComponents || []).map((x) => ({ ...x, static: true }));
|
|
101
|
+
return [...components, ...staticComponents];
|
|
102
|
+
};
|
|
101
103
|
|
|
102
104
|
/**
|
|
103
105
|
* get blocklet engine info, default is node
|
|
@@ -284,6 +286,10 @@ const getAppSystemEnvironments = (blocklet, nodeInfo) => {
|
|
|
284
286
|
const appName = title || name || result.name;
|
|
285
287
|
const appDescription = description || result.description;
|
|
286
288
|
|
|
289
|
+
const isMigrated = Array.isArray(blocklet.migratedFrom) && blocklet.migratedFrom.length > 0;
|
|
290
|
+
const appPid = blocklet.appPid || appId;
|
|
291
|
+
const appPsk = isMigrated ? blocklet.migratedFrom[0].appSk : appSk;
|
|
292
|
+
|
|
287
293
|
/* 获取 did domain 方式:
|
|
288
294
|
* 1. 先从 site 里读
|
|
289
295
|
* 2. 如果没有,再拼接
|
|
@@ -299,16 +305,9 @@ const getAppSystemEnvironments = (blocklet, nodeInfo) => {
|
|
|
299
305
|
if (didDomainAlias) {
|
|
300
306
|
appUrl = prettyURL(didDomainAlias.value, true);
|
|
301
307
|
} else {
|
|
302
|
-
appUrl = `https://${getDidDomainForBlocklet({
|
|
303
|
-
blockletAppDid: appId,
|
|
304
|
-
didDomain: nodeInfo.didDomain,
|
|
305
|
-
})}`;
|
|
308
|
+
appUrl = `https://${getDidDomainForBlocklet({ appPid, didDomain: nodeInfo.didDomain })}`;
|
|
306
309
|
}
|
|
307
310
|
|
|
308
|
-
const isMigrated = Array.isArray(blocklet.migratedFrom) && blocklet.migratedFrom.length > 0;
|
|
309
|
-
const appPid = blocklet.appPid || appId;
|
|
310
|
-
const appPsk = isMigrated ? blocklet.migratedFrom[0].appSk : appSk;
|
|
311
|
-
|
|
312
311
|
return {
|
|
313
312
|
BLOCKLET_DID: did,
|
|
314
313
|
BLOCKLET_APP_SK: appSk,
|
|
@@ -743,75 +742,78 @@ const reloadProcess = (processId) =>
|
|
|
743
742
|
});
|
|
744
743
|
});
|
|
745
744
|
|
|
746
|
-
const parseChildren = (children, parentMeta = {}, { dynamic } = {}) => {
|
|
747
|
-
const configs = Array.isArray(parentMeta) ? parentMeta : getComponentConfig(parentMeta) || [];
|
|
748
|
-
|
|
749
|
-
children.forEach((x) => {
|
|
750
|
-
if (dynamic !== undefined) {
|
|
751
|
-
x.dynamic = !!dynamic;
|
|
752
|
-
}
|
|
753
|
-
});
|
|
754
|
-
|
|
755
|
-
mergeMeta(configs, children);
|
|
756
|
-
return children;
|
|
757
|
-
};
|
|
758
|
-
|
|
759
745
|
/**
|
|
760
|
-
* this function has side effect
|
|
746
|
+
* this function has side effect to component (will set component.children: Array<staticComponent> )
|
|
747
|
+
* this function has side effect to dynamicComponents (will push dynamicComponent in dynamicComponents)
|
|
748
|
+
*
|
|
749
|
+
* @param {Component} component
|
|
750
|
+
* @param {{
|
|
751
|
+
* ancestors: Array<{meta}>
|
|
752
|
+
* dynamicComponents: Array<{Component}>
|
|
753
|
+
* }} context
|
|
754
|
+
* @returns {{
|
|
755
|
+
* dynamicComponents: Array<Component>,
|
|
756
|
+
* staticComponents: Array<Component>,
|
|
757
|
+
* }}
|
|
761
758
|
*/
|
|
762
|
-
const
|
|
763
|
-
const {
|
|
759
|
+
const parseComponents = async (component, context = {}) => {
|
|
760
|
+
const { ancestors = [], dynamicComponents = [] } = context;
|
|
764
761
|
if (ancestors.length > 40) {
|
|
765
762
|
throw new Error('The depth of component should not exceed 40');
|
|
766
763
|
}
|
|
767
764
|
|
|
768
|
-
const configs =
|
|
765
|
+
const configs = getComponentConfig(component.meta) || [];
|
|
769
766
|
|
|
770
767
|
if (!configs || !configs.length) {
|
|
771
|
-
return
|
|
768
|
+
return {
|
|
769
|
+
staticComponents: [],
|
|
770
|
+
dynamicComponents,
|
|
771
|
+
};
|
|
772
772
|
}
|
|
773
773
|
|
|
774
|
-
const
|
|
774
|
+
const staticComponents = [];
|
|
775
775
|
|
|
776
|
+
// FIXME @linchen 改成并行获取
|
|
776
777
|
for (const config of configs) {
|
|
777
|
-
const
|
|
778
|
-
|
|
779
|
-
|
|
778
|
+
const isStatic = !!config.static;
|
|
779
|
+
|
|
780
|
+
if (isStatic) {
|
|
781
|
+
if (!config.name) {
|
|
782
|
+
throw new Error(`Name does not found in child ${config.name}`);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
if (!config.mountPoint) {
|
|
786
|
+
throw new Error(`MountPoint does not found in child ${config.name}`);
|
|
787
|
+
}
|
|
780
788
|
}
|
|
781
789
|
|
|
782
790
|
const urls = getSourceUrlsFromConfig(config);
|
|
783
791
|
|
|
784
|
-
let
|
|
792
|
+
let rawMeta;
|
|
785
793
|
try {
|
|
786
|
-
|
|
787
|
-
} catch {
|
|
788
|
-
throw new Error(
|
|
794
|
+
rawMeta = await getBlockletMetaFromUrls(urls, { logger });
|
|
795
|
+
} catch (error) {
|
|
796
|
+
throw new Error(
|
|
797
|
+
`Failed get component meta. Component: ${rawMeta.title || rawMeta.did}, reason: ${error.message}`
|
|
798
|
+
);
|
|
789
799
|
}
|
|
790
800
|
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
if (!isComponentBlocklet(m)) {
|
|
794
|
-
throw new Error(`The blocklet cannot be a component: ${m.title}`);
|
|
801
|
+
if (rawMeta.group === BlockletGroup.gateway) {
|
|
802
|
+
throw new Error(`Cannot add gateway component ${rawMeta.title || rawMeta.did}`);
|
|
795
803
|
}
|
|
796
804
|
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
805
|
+
validateBlockletMeta(rawMeta, { ensureDist: true });
|
|
806
|
+
|
|
807
|
+
if (!isComponentBlocklet(rawMeta)) {
|
|
808
|
+
throw new Error(`The blocklet cannot be a component: ${rawMeta.title}`);
|
|
800
809
|
}
|
|
801
810
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
const fullMountPoint = path.join(
|
|
806
|
-
ancestors.map((x) => x.mountPoint || ''),
|
|
807
|
-
mountPoint
|
|
808
|
-
);
|
|
809
|
-
if (normalizePathPrefix(rule) !== normalizePathPrefix(fullMountPoint)) {
|
|
810
|
-
throw new Error(`Prefix does not match in child ${config.name}. expected: ${rule}, resolved: ${mountPoint}`);
|
|
811
|
-
}
|
|
811
|
+
const webInterface = findWebInterface(rawMeta);
|
|
812
|
+
if (!webInterface) {
|
|
813
|
+
throw new Error(`Web interface does not found in component ${rawMeta.title || rawMeta.name}`);
|
|
812
814
|
}
|
|
813
815
|
|
|
814
|
-
const meta = ensureMeta(
|
|
816
|
+
const meta = ensureMeta(rawMeta, { name: isStatic ? config.name : null });
|
|
815
817
|
|
|
816
818
|
// check circular dependencies
|
|
817
819
|
if (ancestors.map((x) => x.meta?.bundleDid).indexOf(meta.bundleDid) > -1) {
|
|
@@ -822,28 +824,44 @@ const parseChildrenFromMeta = async (src, context = {}) => {
|
|
|
822
824
|
meta.title = config.title;
|
|
823
825
|
meta.title = await titleSchema.validateAsync(config.title);
|
|
824
826
|
}
|
|
827
|
+
|
|
825
828
|
if (config.description) {
|
|
826
829
|
meta.description = await descriptionSchema.validateAsync(config.description);
|
|
827
830
|
}
|
|
828
831
|
|
|
832
|
+
const mountPoint = isStatic ? config.mountPoint : config.mountPoint || `/${meta.did}`;
|
|
833
|
+
|
|
829
834
|
const child = {
|
|
830
835
|
mountPoint,
|
|
831
836
|
meta,
|
|
832
|
-
bundleSource: config.source
|
|
837
|
+
bundleSource: config.source,
|
|
838
|
+
dependencies: [],
|
|
833
839
|
};
|
|
834
840
|
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
841
|
+
if (isStatic) {
|
|
842
|
+
staticComponents.push(child);
|
|
843
|
+
} else {
|
|
844
|
+
dynamicComponents.push(child);
|
|
845
|
+
component.dependencies = component.dependencies || [];
|
|
846
|
+
component.dependencies.push({
|
|
847
|
+
did: child.meta.bundleDid,
|
|
848
|
+
required: !!config.required,
|
|
849
|
+
version: config.source.version || 'latest',
|
|
850
|
+
});
|
|
851
|
+
}
|
|
840
852
|
|
|
841
|
-
|
|
853
|
+
await parseComponents(child, {
|
|
854
|
+
ancestors: [...ancestors, { meta }],
|
|
855
|
+
dynamicComponents,
|
|
856
|
+
});
|
|
842
857
|
}
|
|
843
858
|
|
|
844
|
-
|
|
859
|
+
component.children = staticComponents;
|
|
845
860
|
|
|
846
|
-
return
|
|
861
|
+
return {
|
|
862
|
+
staticComponents,
|
|
863
|
+
dynamicComponents,
|
|
864
|
+
};
|
|
847
865
|
};
|
|
848
866
|
|
|
849
867
|
const validateBlocklet = (blocklet) =>
|
|
@@ -1166,43 +1184,6 @@ const mergeMeta = (source, childrenMeta = []) => {
|
|
|
1166
1184
|
});
|
|
1167
1185
|
};
|
|
1168
1186
|
|
|
1169
|
-
const fixAndVerifyMetaFromStore = (meta) => {
|
|
1170
|
-
// strip unknown properties
|
|
1171
|
-
// rename property alias
|
|
1172
|
-
|
|
1173
|
-
const sanitized = validateMeta(meta, { ensureDist: false, schemaOptions: { noDefaults: true, stripUnknown: true } });
|
|
1174
|
-
|
|
1175
|
-
// rollback meta renamed by validateMeta
|
|
1176
|
-
|
|
1177
|
-
if (meta.children) {
|
|
1178
|
-
sanitized.children = meta.children;
|
|
1179
|
-
delete sanitized.components;
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1182
|
-
if (meta.navigation) {
|
|
1183
|
-
meta.navigation.forEach((nav, index) => {
|
|
1184
|
-
if (nav.child) {
|
|
1185
|
-
sanitized.navigation[index].child = nav.child;
|
|
1186
|
-
delete sanitized.navigation[index].component;
|
|
1187
|
-
}
|
|
1188
|
-
});
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
|
-
// verify
|
|
1192
|
-
|
|
1193
|
-
try {
|
|
1194
|
-
const result = verifyMultiSig(sanitized);
|
|
1195
|
-
if (!result) {
|
|
1196
|
-
throw new Error('invalid signature from developer or store');
|
|
1197
|
-
}
|
|
1198
|
-
} catch (err) {
|
|
1199
|
-
throw new Error(`Invalid blocklet meta: ${err.message}`);
|
|
1200
|
-
}
|
|
1201
|
-
|
|
1202
|
-
// this step comes last because it has side effects: the meta is changed
|
|
1203
|
-
return fixAndValidateService(meta);
|
|
1204
|
-
};
|
|
1205
|
-
|
|
1206
1187
|
const getUpdateMetaList = (oldBlocklet = {}, newBlocklet = {}) => {
|
|
1207
1188
|
const oldMap = {};
|
|
1208
1189
|
forEachChildSync(oldBlocklet, (b, { id }) => {
|
|
@@ -1238,7 +1219,7 @@ const getTypeFromInstallParams = (params) => {
|
|
|
1238
1219
|
}
|
|
1239
1220
|
|
|
1240
1221
|
if (params.file) {
|
|
1241
|
-
|
|
1222
|
+
throw new Error('install from upload is not supported');
|
|
1242
1223
|
}
|
|
1243
1224
|
|
|
1244
1225
|
if (params.did) {
|
|
@@ -1263,13 +1244,6 @@ const checkDuplicateComponents = (components = []) => {
|
|
|
1263
1244
|
.join(', ')}`
|
|
1264
1245
|
);
|
|
1265
1246
|
}
|
|
1266
|
-
|
|
1267
|
-
const duplicateMountPoints = components.filter(
|
|
1268
|
-
(item, index) => components.findIndex((x) => isPathPrefixEqual(x.mountPoint, item.mountPoint)) !== index
|
|
1269
|
-
);
|
|
1270
|
-
if (duplicateMountPoints.length) {
|
|
1271
|
-
throw new Error(`mount point must be unique: ${duplicateMountPoints.map((x) => x.mountPoint).join(', ')}`);
|
|
1272
|
-
}
|
|
1273
1247
|
};
|
|
1274
1248
|
|
|
1275
1249
|
const getDiffFiles = async (inputFiles, sourceDir) => {
|
|
@@ -1327,32 +1301,30 @@ const needBlockletDownload = (blocklet, oldBlocklet) => {
|
|
|
1327
1301
|
return get(oldBlocklet, 'meta.dist.integrity') !== get(blocklet, 'meta.dist.integrity');
|
|
1328
1302
|
};
|
|
1329
1303
|
|
|
1330
|
-
const
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
const newName = match ? meta.name.replace(reg, `-${Number(match[1]) + 1}`) : `${meta.name}-1`;
|
|
1335
|
-
const newDid = toBlockletDid(newName);
|
|
1336
|
-
const newMeta = { name: newName, did: newDid };
|
|
1337
|
-
|
|
1338
|
-
if (!(siblings || []).some((x) => x.did === newMeta.did)) {
|
|
1339
|
-
return newMeta;
|
|
1304
|
+
const isDidMatchName = (did, name) => {
|
|
1305
|
+
if (isValidDid(did) && name === did) {
|
|
1306
|
+
return true;
|
|
1340
1307
|
}
|
|
1341
1308
|
|
|
1342
|
-
return
|
|
1309
|
+
return toBlockletDid(name) === did;
|
|
1343
1310
|
};
|
|
1344
1311
|
|
|
1312
|
+
/**
|
|
1313
|
+
* set bundleDid and bundleMeta in meta
|
|
1314
|
+
* update name and did to server index
|
|
1315
|
+
* in app structure 2.0, application's bundleDid, bundleName, meta, did will be same
|
|
1316
|
+
*/
|
|
1345
1317
|
const ensureMeta = (meta, { name, did } = {}) => {
|
|
1346
|
-
if (name && did &&
|
|
1347
|
-
throw new Error(
|
|
1318
|
+
if (name && did && !isDidMatchName(did, name)) {
|
|
1319
|
+
throw new Error(`name does not match with did: ${name}, ${did}`);
|
|
1348
1320
|
}
|
|
1349
1321
|
|
|
1350
1322
|
const newMeta = {
|
|
1351
1323
|
...meta,
|
|
1352
1324
|
};
|
|
1353
1325
|
|
|
1354
|
-
if (!newMeta.did || !newMeta.name ||
|
|
1355
|
-
throw new Error(
|
|
1326
|
+
if (!newMeta.did || !newMeta.name || !isDidMatchName(newMeta.did, newMeta.name)) {
|
|
1327
|
+
throw new Error(`name does not match with did in meta: ${newMeta.name}, ${newMeta.did}`);
|
|
1356
1328
|
}
|
|
1357
1329
|
|
|
1358
1330
|
if (!meta.bundleDid) {
|
|
@@ -1740,7 +1712,10 @@ const checkDuplicateAppSk = async ({ sk, did, states }) => {
|
|
|
1740
1712
|
|
|
1741
1713
|
const checkDuplicateMountPoint = (blocklet, mountPoint) => {
|
|
1742
1714
|
const err = new Error(`cannot add duplicate mount point, ${mountPoint || '/'} already exist`);
|
|
1743
|
-
if (
|
|
1715
|
+
if (
|
|
1716
|
+
blocklet.meta?.group !== BlockletGroup.gateway &&
|
|
1717
|
+
normalizePathPrefix(blocklet.mountPoint) === normalizePathPrefix(mountPoint)
|
|
1718
|
+
) {
|
|
1744
1719
|
throw err;
|
|
1745
1720
|
}
|
|
1746
1721
|
|
|
@@ -1777,12 +1752,79 @@ const validateInServerless = ({ blockletMeta }) => {
|
|
|
1777
1752
|
}
|
|
1778
1753
|
};
|
|
1779
1754
|
|
|
1755
|
+
const checkStructVersion = (blocklet) => {
|
|
1756
|
+
if (blocklet.structVersion !== APP_STRUCT_VERSION) {
|
|
1757
|
+
throw new Error('You should migrate the application first');
|
|
1758
|
+
}
|
|
1759
|
+
};
|
|
1760
|
+
|
|
1761
|
+
const isVersionCompatible = (actualVersion, expectedRange) =>
|
|
1762
|
+
!expectedRange || expectedRange === 'latest' || semver.satisfies(actualVersion, expectedRange);
|
|
1763
|
+
|
|
1764
|
+
const checkVersionCompatibility = (components) => {
|
|
1765
|
+
for (const component of components) {
|
|
1766
|
+
// eslint-disable-next-line no-loop-func
|
|
1767
|
+
forEachBlockletSync(component, (x) => {
|
|
1768
|
+
const dependencies = x.dependencies || [];
|
|
1769
|
+
dependencies.forEach((dep) => {
|
|
1770
|
+
const { did, version: expectedRange } = dep;
|
|
1771
|
+
const exist = components.find((y) => y.meta.did === did);
|
|
1772
|
+
if (exist && !isVersionCompatible(exist.meta.version, expectedRange)) {
|
|
1773
|
+
throw new Error(
|
|
1774
|
+
`Check version compatible failed: ${component.meta.title || component.meta.did} expects ${
|
|
1775
|
+
exist.meta.title || exist.meta.did
|
|
1776
|
+
}'s version to be ${expectedRange}, but actual is ${exist.meta.version}`
|
|
1777
|
+
);
|
|
1778
|
+
}
|
|
1779
|
+
});
|
|
1780
|
+
});
|
|
1781
|
+
}
|
|
1782
|
+
};
|
|
1783
|
+
|
|
1784
|
+
const filterDuplicateComponents = (components = [], currents = []) => {
|
|
1785
|
+
const arr = [];
|
|
1786
|
+
|
|
1787
|
+
components.forEach((component) => {
|
|
1788
|
+
if (currents.some((x) => x.meta.did === component.meta.did)) {
|
|
1789
|
+
return;
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
const index = arr.findIndex((x) => x.meta.did === component.meta.did);
|
|
1793
|
+
if (index > -1) {
|
|
1794
|
+
const exist = arr[index];
|
|
1795
|
+
|
|
1796
|
+
// 选择最小版本:
|
|
1797
|
+
// 如果 com1 和 com2 都依赖某个 component, com1 声明依赖版本 1.0.0, 实际获取版本 1.0.0; com2 声明依赖版本 latest(不限), 实际获取版本 2.0.0 -> 则应该取 1.0.0
|
|
1798
|
+
if (semver.lt(component.meta.version, exist.meta.version)) {
|
|
1799
|
+
arr.splice(index, 1, component);
|
|
1800
|
+
}
|
|
1801
|
+
} else {
|
|
1802
|
+
arr.push(component);
|
|
1803
|
+
}
|
|
1804
|
+
});
|
|
1805
|
+
|
|
1806
|
+
return arr;
|
|
1807
|
+
};
|
|
1808
|
+
|
|
1809
|
+
const validateBlockletMeta = (meta, opts = {}) => {
|
|
1810
|
+
fixAndValidateService(meta);
|
|
1811
|
+
return validateMeta(meta, { ensureName: true, skipValidateDidName: true, schemaOptions: { ...opts } });
|
|
1812
|
+
};
|
|
1813
|
+
|
|
1814
|
+
const getBlockletKnownAs = (blocklet) => {
|
|
1815
|
+
const alsoKnownAs = [blocklet.appDid];
|
|
1816
|
+
if (Array.isArray(blocklet.migratedFrom)) {
|
|
1817
|
+
blocklet.migratedFrom.filter((x) => x.appDid !== blocklet.appPid).forEach((x) => alsoKnownAs.push(x.appDid));
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
return alsoKnownAs.filter(Boolean).map(toDid);
|
|
1821
|
+
};
|
|
1822
|
+
|
|
1780
1823
|
module.exports = {
|
|
1781
1824
|
consumeServerlessNFT,
|
|
1782
1825
|
forEachBlocklet,
|
|
1783
1826
|
getBlockletMetaFromUrl: (url) => getBlockletMetaFromUrl(url, { logger }),
|
|
1784
|
-
|
|
1785
|
-
parseChildren,
|
|
1827
|
+
parseComponents,
|
|
1786
1828
|
getComponentDirs,
|
|
1787
1829
|
getAppSystemEnvironments,
|
|
1788
1830
|
getAppOverwrittenEnvironments,
|
|
@@ -1807,15 +1849,14 @@ module.exports = {
|
|
|
1807
1849
|
getDiskInfo,
|
|
1808
1850
|
getRuntimeInfo,
|
|
1809
1851
|
mergeMeta,
|
|
1810
|
-
fixAndVerifyMetaFromStore,
|
|
1811
1852
|
getUpdateMetaList,
|
|
1812
1853
|
getTypeFromInstallParams,
|
|
1813
1854
|
findWebInterface,
|
|
1814
1855
|
checkDuplicateComponents,
|
|
1856
|
+
filterDuplicateComponents,
|
|
1815
1857
|
getDiffFiles,
|
|
1816
1858
|
getBundleDir,
|
|
1817
1859
|
needBlockletDownload,
|
|
1818
|
-
findAvailableDid,
|
|
1819
1860
|
ensureMeta,
|
|
1820
1861
|
getBlocklet,
|
|
1821
1862
|
ensureEnvDefault,
|
|
@@ -1829,4 +1870,8 @@ module.exports = {
|
|
|
1829
1870
|
checkDuplicateMountPoint,
|
|
1830
1871
|
validateStore,
|
|
1831
1872
|
validateInServerless,
|
|
1873
|
+
checkStructVersion,
|
|
1874
|
+
checkVersionCompatibility,
|
|
1875
|
+
validateBlockletMeta,
|
|
1876
|
+
getBlockletKnownAs,
|
|
1832
1877
|
};
|
|
@@ -1,22 +1,13 @@
|
|
|
1
|
-
const { DEFAULT_IP_DOMAIN_SUFFIX } = require('@abtnode/constant');
|
|
1
|
+
const { DEFAULT_IP_DOMAIN_SUFFIX, SLOT_FOR_IP_DNS_SITE } = require('@abtnode/constant');
|
|
2
2
|
const { encode: encodeBase32 } = require('@abtnode/util/lib/base32');
|
|
3
3
|
const formatName = require('@abtnode/util/lib/format-name');
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
const hiddenInterfaceNames = ['public', 'publicUrl'];
|
|
8
|
-
|
|
9
|
-
const getIpDnsDomainForBlocklet = (blocklet, blockletInterface) => {
|
|
10
|
-
const nameSuffix = blocklet.meta.did.slice(-3).toLowerCase();
|
|
11
|
-
const iName = hiddenInterfaceNames.includes(blockletInterface.name) ? '' : blockletInterface.name.toLowerCase();
|
|
12
|
-
|
|
13
|
-
return `${formatName(blocklet.meta.name)}-${nameSuffix}${
|
|
14
|
-
iName ? '-' : ''
|
|
15
|
-
}${iName}-${SLOT_FOR_IP_DNS_SITE}.${DEFAULT_IP_DOMAIN_SUFFIX}`;
|
|
5
|
+
const getIpDnsDomainForBlocklet = (blocklet) => {
|
|
6
|
+
return `${encodeBase32(blocklet.meta.did)}-${SLOT_FOR_IP_DNS_SITE}.${DEFAULT_IP_DOMAIN_SUFFIX}`;
|
|
16
7
|
};
|
|
17
8
|
|
|
18
|
-
const getDidDomainForBlocklet = ({
|
|
19
|
-
return `${encodeBase32(
|
|
9
|
+
const getDidDomainForBlocklet = ({ appPid, didDomain }) => {
|
|
10
|
+
return `${encodeBase32(appPid)}.${didDomain}`;
|
|
20
11
|
};
|
|
21
12
|
|
|
22
13
|
module.exports = { getIpDnsDomainForBlocklet, getDidDomainForBlocklet, formatName };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const { isFreeBlocklet } = require('@blocklet/meta/lib/util');
|
|
2
|
+
|
|
3
|
+
const logger = require('@abtnode/logger')('getMetaFromUrl');
|
|
4
|
+
|
|
5
|
+
const { getBlockletMetaFromUrl } = require('./blocklet');
|
|
6
|
+
const { getStoreMeta, parseSourceUrl } = require('./store');
|
|
7
|
+
const { getFactoryState } = require('./chain');
|
|
8
|
+
|
|
9
|
+
const getMetaFromUrl = async ({ url, checkPrice = false }) => {
|
|
10
|
+
const meta = await getBlockletMetaFromUrl(url);
|
|
11
|
+
let isFree = isFreeBlocklet(meta);
|
|
12
|
+
|
|
13
|
+
if (checkPrice && !isFree && meta.nftFactory) {
|
|
14
|
+
try {
|
|
15
|
+
const registryMeta = await getStoreMeta(new URL(url).origin);
|
|
16
|
+
|
|
17
|
+
if (registryMeta.chainHost) {
|
|
18
|
+
const state = await getFactoryState(registryMeta.chainHost, meta.nftFactory);
|
|
19
|
+
if (state) {
|
|
20
|
+
isFree = false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
} catch (error) {
|
|
24
|
+
logger.warn('failed when checking if the blocklet is free', { did: meta.did, error });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const { inStore, registryUrl } = await parseSourceUrl(url);
|
|
29
|
+
|
|
30
|
+
return { meta, isFree, inStore, registryUrl };
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
module.exports = getMetaFromUrl;
|
package/lib/util/index.js
CHANGED
|
@@ -16,7 +16,6 @@ const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
|
|
|
16
16
|
const axios = require('@abtnode/util/lib/axios');
|
|
17
17
|
const { encode: encodeBase32 } = require('@abtnode/util/lib/base32');
|
|
18
18
|
const parseBlockletMeta = require('@blocklet/meta/lib/parse');
|
|
19
|
-
const { validateMeta, fixAndValidateService } = require('@blocklet/meta/lib/validate');
|
|
20
19
|
const { BlockletStatus } = require('@blocklet/constant');
|
|
21
20
|
const { replaceSlotToIp } = require('@blocklet/meta/lib/util');
|
|
22
21
|
const {
|
|
@@ -493,10 +492,6 @@ const lib = {
|
|
|
493
492
|
getDataDirs,
|
|
494
493
|
getBaseUrls,
|
|
495
494
|
getBlockletMeta: parseBlockletMeta,
|
|
496
|
-
validateBlockletMeta: (meta, opts = {}) => {
|
|
497
|
-
fixAndValidateService(meta);
|
|
498
|
-
return validateMeta(meta, opts);
|
|
499
|
-
},
|
|
500
495
|
expandBundle,
|
|
501
496
|
findInterfaceByName,
|
|
502
497
|
findInterfacePortByName,
|
package/lib/util/store.js
CHANGED
|
@@ -3,14 +3,47 @@ const pick = require('lodash/pick');
|
|
|
3
3
|
const isBase64 = require('is-base64');
|
|
4
4
|
|
|
5
5
|
const { BLOCKLET_STORE_API_PREFIX, BLOCKLET_STORE_META_PATH } = require('@abtnode/constant');
|
|
6
|
+
const { validateMeta, fixAndValidateService } = require('@blocklet/meta/lib/validate');
|
|
7
|
+
const verifyMultiSig = require('@blocklet/meta/lib/verify-multi-sig');
|
|
6
8
|
|
|
7
9
|
const { name } = require('../../package.json');
|
|
8
10
|
const logger = require('@abtnode/logger')(`${name}:util:registry`); // eslint-disable-line
|
|
9
11
|
|
|
10
12
|
const request = require('./request');
|
|
11
|
-
const { fixAndVerifyMetaFromStore } = require('./blocklet');
|
|
12
13
|
|
|
13
|
-
const
|
|
14
|
+
const fixAndVerifyMetaFromStore = (meta) => {
|
|
15
|
+
const sanitized = validateMeta(meta, { ensureDist: false, schemaOptions: { noDefaults: true, stripUnknown: true } });
|
|
16
|
+
|
|
17
|
+
if (meta.children) {
|
|
18
|
+
sanitized.children = meta.children;
|
|
19
|
+
delete sanitized.components;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (meta.navigation) {
|
|
23
|
+
meta.navigation.forEach((nav, index) => {
|
|
24
|
+
if (nav.child) {
|
|
25
|
+
sanitized.navigation[index].child = nav.child;
|
|
26
|
+
delete sanitized.navigation[index].component;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// verify
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const result = verifyMultiSig(sanitized);
|
|
35
|
+
if (!result) {
|
|
36
|
+
throw new Error('invalid signature from developer or store');
|
|
37
|
+
}
|
|
38
|
+
} catch (err) {
|
|
39
|
+
throw new Error(`Invalid blocklet meta: ${err.message}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// this step comes last because it has side effects: the meta is changed
|
|
43
|
+
return fixAndValidateService(meta);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const validateStoreUrl = async (registry) => {
|
|
14
47
|
const url = joinURL(registry, BLOCKLET_STORE_API_PREFIX, `/blocklets.json?__t__=${Date.now()}`);
|
|
15
48
|
try {
|
|
16
49
|
const res = await request.get(url);
|
|
@@ -26,7 +59,7 @@ const validateRegistryURL = async (registry) => {
|
|
|
26
59
|
}
|
|
27
60
|
};
|
|
28
61
|
|
|
29
|
-
const
|
|
62
|
+
const getStoreMeta = async (registry) => {
|
|
30
63
|
try {
|
|
31
64
|
const url = joinURL(registry, BLOCKLET_STORE_META_PATH, `?__t__=${Date.now()}`);
|
|
32
65
|
const { data } = await request.get(url);
|
|
@@ -66,7 +99,7 @@ const parseSourceUrl = async (url) => {
|
|
|
66
99
|
const match = pathname.match(/^\/api\/blocklets\/(\w*)\/blocklet\.json/);
|
|
67
100
|
if (match) {
|
|
68
101
|
try {
|
|
69
|
-
const m = await
|
|
102
|
+
const m = await getStoreMeta(origin);
|
|
70
103
|
if (m && m.id) {
|
|
71
104
|
return {
|
|
72
105
|
inStore: true,
|
|
@@ -108,6 +141,9 @@ const resolveTarballURL = async ({ did, tarball = '', storeUrl = '' }) => {
|
|
|
108
141
|
return joinURL(storeUrl, 'api', 'blocklets', did, tarball);
|
|
109
142
|
};
|
|
110
143
|
|
|
144
|
+
const getBlockletMetaUrl = ({ did, storeUrl }) =>
|
|
145
|
+
joinURL(storeUrl, BLOCKLET_STORE_API_PREFIX, `/blocklets/${did}/blocklet.json`);
|
|
146
|
+
|
|
111
147
|
const getBlockletMeta = async ({ did, storeUrl }) => {
|
|
112
148
|
const url = joinURL(storeUrl, BLOCKLET_STORE_API_PREFIX, `/blocklets/${did}/blocklet.json?__t__=${Date.now()}`);
|
|
113
149
|
|
|
@@ -133,9 +169,11 @@ const getBlockletMeta = async ({ did, storeUrl }) => {
|
|
|
133
169
|
};
|
|
134
170
|
|
|
135
171
|
module.exports = {
|
|
136
|
-
|
|
137
|
-
|
|
172
|
+
validateStoreUrl,
|
|
173
|
+
getStoreMeta,
|
|
138
174
|
parseSourceUrl,
|
|
139
175
|
getBlockletMeta,
|
|
140
176
|
resolveTarballURL,
|
|
177
|
+
getBlockletMetaUrl,
|
|
178
|
+
fixAndVerifyMetaFromStore,
|
|
141
179
|
};
|
|
@@ -35,6 +35,9 @@ class WalletSender extends BaseSender {
|
|
|
35
35
|
|
|
36
36
|
await sendToUser(adminUsers, message, sender, process.env.ABT_NODE_SERVICE_PORT);
|
|
37
37
|
} catch (error) {
|
|
38
|
+
delete error.request;
|
|
39
|
+
delete error.response;
|
|
40
|
+
delete error.config;
|
|
38
41
|
logger.error('failed to push notification to wallet', { error });
|
|
39
42
|
}
|
|
40
43
|
}
|