@abtnode/core 1.8.37 → 1.8.38
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 -5
- package/lib/blocklet/manager/disk.js +222 -75
- package/lib/index.js +0 -4
- package/lib/states/blocklet.js +15 -0
- package/lib/states/notification.js +14 -28
- package/lib/team/manager.js +9 -0
- package/lib/util/blocklet.js +35 -2
- package/lib/util/index.js +44 -0
- package/lib/validators/blocklet.js +0 -2
- package/lib/validators/role.js +4 -0
- package/package.json +25 -25
package/lib/api/team.js
CHANGED
|
@@ -180,6 +180,7 @@ class TeamAPI extends EventEmitter {
|
|
|
180
180
|
'lastLoginAt',
|
|
181
181
|
'remark',
|
|
182
182
|
'avatar',
|
|
183
|
+
'locale',
|
|
183
184
|
])
|
|
184
185
|
// eslint-disable-next-line function-paren-newline
|
|
185
186
|
),
|
|
@@ -692,11 +693,7 @@ class TeamAPI extends EventEmitter {
|
|
|
692
693
|
// Access Control
|
|
693
694
|
|
|
694
695
|
async getRoles({ teamDid }) {
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
const roles = await rbac.getRoles();
|
|
698
|
-
|
|
699
|
-
return roles.map((d) => pick(d, ['name', 'grants', 'title', 'description']));
|
|
696
|
+
return this.teamManager.getRoles(teamDid);
|
|
700
697
|
}
|
|
701
698
|
|
|
702
699
|
async createRole({ teamDid, name, description, title, childName, permissions = [] }) {
|
|
@@ -13,6 +13,7 @@ const capitalize = require('lodash/capitalize');
|
|
|
13
13
|
const { Throttle } = require('stream-throttle');
|
|
14
14
|
const LRU = require('lru-cache');
|
|
15
15
|
const joi = require('joi');
|
|
16
|
+
const { isNFTExpired } = require('@abtnode/util/lib/nft');
|
|
16
17
|
const { sign } = require('@arcblock/jwt');
|
|
17
18
|
const { isValid: isValidDid } = require('@arcblock/did');
|
|
18
19
|
const { verifyPresentation } = require('@arcblock/vc');
|
|
@@ -25,7 +26,12 @@ const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager');
|
|
|
25
26
|
const downloadFile = require('@abtnode/util/lib/download-file');
|
|
26
27
|
const Lock = require('@abtnode/util/lib/lock');
|
|
27
28
|
const { getVcFromPresentation } = require('@abtnode/util/lib/vc');
|
|
28
|
-
const {
|
|
29
|
+
const {
|
|
30
|
+
VC_TYPE_BLOCKLET_PURCHASE,
|
|
31
|
+
WHO_CAN_ACCESS,
|
|
32
|
+
SERVER_ROLES,
|
|
33
|
+
WHO_CAN_ACCESS_PREFIX_ROLES,
|
|
34
|
+
} = require('@abtnode/constant');
|
|
29
35
|
|
|
30
36
|
const getBlockletEngine = require('@blocklet/meta/lib/engine');
|
|
31
37
|
const {
|
|
@@ -40,13 +46,21 @@ const {
|
|
|
40
46
|
forEachChild,
|
|
41
47
|
getComponentId,
|
|
42
48
|
getComponentBundleId,
|
|
49
|
+
isPreferenceKey,
|
|
50
|
+
getRolesFromAuthConfig,
|
|
43
51
|
} = require('@blocklet/meta/lib/util');
|
|
44
52
|
const getComponentProcessId = require('@blocklet/meta/lib/get-component-process-id');
|
|
45
53
|
const validateBlockletEntry = require('@blocklet/meta/lib/entry');
|
|
46
54
|
const toBlockletDid = require('@blocklet/meta/lib/did');
|
|
47
55
|
const { validateMeta } = require('@blocklet/meta/lib/validate');
|
|
48
56
|
const { update: updateMetaFile } = require('@blocklet/meta/lib/file');
|
|
49
|
-
const {
|
|
57
|
+
const {
|
|
58
|
+
titleSchema,
|
|
59
|
+
descriptionSchema,
|
|
60
|
+
mountPointSchema,
|
|
61
|
+
logoSchema,
|
|
62
|
+
environmentNameSchema,
|
|
63
|
+
} = require('@blocklet/meta/lib/schema');
|
|
50
64
|
const hasReservedKey = require('@blocklet/meta/lib/has-reserved-key');
|
|
51
65
|
|
|
52
66
|
const { toExternalBlocklet } = toBlockletDid;
|
|
@@ -110,6 +124,7 @@ const {
|
|
|
110
124
|
getBlocklet,
|
|
111
125
|
ensureEnvDefault,
|
|
112
126
|
getConfigFromPreferences,
|
|
127
|
+
consumeServerlessNFT,
|
|
113
128
|
} = require('../../util/blocklet');
|
|
114
129
|
const StoreUtil = require('../../util/store');
|
|
115
130
|
const states = require('../../states');
|
|
@@ -121,6 +136,7 @@ const runMigrationScripts = require('../migration');
|
|
|
121
136
|
const hooks = require('../hooks');
|
|
122
137
|
const { formatName } = require('../../util/get-domain-for-blocklet');
|
|
123
138
|
const handleInstanceInStore = require('../../util/public-to-store');
|
|
139
|
+
const { getNFTState } = require('../../util');
|
|
124
140
|
|
|
125
141
|
const {
|
|
126
142
|
isInProgress,
|
|
@@ -174,7 +190,7 @@ const getSkippedProcessIds = ({ newBlocklet, oldBlocklet, context = {} }) => {
|
|
|
174
190
|
};
|
|
175
191
|
|
|
176
192
|
const getBlockletIndex = (meta, controller) =>
|
|
177
|
-
controller ? toExternalBlocklet(meta.name, controller.
|
|
193
|
+
controller ? toExternalBlocklet(meta.name, controller.nftId) : { did: meta.did, name: meta.name };
|
|
178
194
|
|
|
179
195
|
class BlockletManager extends BaseBlockletManager {
|
|
180
196
|
/**
|
|
@@ -378,7 +394,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
378
394
|
return { meta, isFree, inStore, registryUrl };
|
|
379
395
|
}
|
|
380
396
|
|
|
381
|
-
async getBlockletByBundle({ did, name }, context) {
|
|
397
|
+
async getBlockletByBundle({ did, name, serverlessNftId }, context) {
|
|
382
398
|
if (toBlockletDid(name) !== did) {
|
|
383
399
|
throw new Error('did and name does not match');
|
|
384
400
|
}
|
|
@@ -387,11 +403,15 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
387
403
|
throw new Error('user does not exist');
|
|
388
404
|
}
|
|
389
405
|
|
|
406
|
+
if (context.user.role === SERVER_ROLES.EXTERNAL_BLOCKLET_CONTROLLER && !serverlessNftId) {
|
|
407
|
+
throw new Error('serverless nft id is required');
|
|
408
|
+
}
|
|
409
|
+
|
|
390
410
|
let blockletDid = did;
|
|
391
411
|
let isExternal = false;
|
|
392
412
|
|
|
393
|
-
if (
|
|
394
|
-
blockletDid = toExternalBlocklet(name,
|
|
413
|
+
if (serverlessNftId) {
|
|
414
|
+
blockletDid = toExternalBlocklet(name, serverlessNftId, { didOnly: true });
|
|
395
415
|
isExternal = true;
|
|
396
416
|
}
|
|
397
417
|
|
|
@@ -513,7 +533,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
513
533
|
const error = Array.isArray(err) ? err[0] : err;
|
|
514
534
|
logger.error('Failed to start blocklet', { error, did, name: blocklet.meta.name });
|
|
515
535
|
const description = `Start blocklet ${blocklet.meta.name} failed with error: ${error.message}`;
|
|
516
|
-
|
|
536
|
+
this._createNotification(did, {
|
|
517
537
|
title: 'Start Blocklet Failed',
|
|
518
538
|
description,
|
|
519
539
|
entityType: 'blocklet',
|
|
@@ -565,7 +585,12 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
565
585
|
|
|
566
586
|
if (updateStatus) {
|
|
567
587
|
const res = await this.status(did, { forceSync: true });
|
|
588
|
+
// send notification to websocket channel
|
|
568
589
|
this.emit(BlockletEvents.statusChange, res);
|
|
590
|
+
|
|
591
|
+
// send notification to wallet
|
|
592
|
+
this.emit(BlockletEvents.stopped, res);
|
|
593
|
+
|
|
569
594
|
return res;
|
|
570
595
|
}
|
|
571
596
|
|
|
@@ -586,7 +611,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
586
611
|
const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped);
|
|
587
612
|
this.emit(BlockletEvents.statusChange, state);
|
|
588
613
|
|
|
589
|
-
|
|
614
|
+
this._createNotification(did, {
|
|
590
615
|
title: 'Blocklet Restart Failed',
|
|
591
616
|
description: `Blocklet ${did} restart failed with error: ${err.message || 'queue exception'}`,
|
|
592
617
|
entityType: 'blocklet',
|
|
@@ -637,7 +662,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
637
662
|
});
|
|
638
663
|
|
|
639
664
|
const doc = await this._deleteBlocklet({ did, keepData, keepLogsDir, keepConfigs }, context);
|
|
640
|
-
|
|
665
|
+
this._createNotification(doc.meta.did, {
|
|
641
666
|
title: 'Blocklet Deleted',
|
|
642
667
|
description: `Blocklet ${doc.meta.name}@${doc.meta.version} is deleted.`,
|
|
643
668
|
entityType: 'blocklet',
|
|
@@ -651,7 +676,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
651
676
|
logger.info('blocklet is corrupted, will delete again', { did });
|
|
652
677
|
const doc = await this._deleteBlocklet({ did, keepData, keepLogsDir, keepConfigs }, context);
|
|
653
678
|
|
|
654
|
-
|
|
679
|
+
this._createNotification(doc.meta.did, {
|
|
655
680
|
title: 'Blocklet Deleted',
|
|
656
681
|
description: `Blocklet ${doc.meta.name}@${doc.meta.version} is deleted.`,
|
|
657
682
|
entityType: 'blocklet',
|
|
@@ -776,7 +801,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
776
801
|
const newBlocklet = await this.ensureBlocklet(rootDid);
|
|
777
802
|
this.emit(BlockletEvents.upgraded, { blocklet: newBlocklet, context: { ...context, createAuditLog: false } }); // trigger router refresh
|
|
778
803
|
|
|
779
|
-
|
|
804
|
+
this._createNotification(newBlocklet.meta.did, {
|
|
780
805
|
title: 'Component Deleted',
|
|
781
806
|
description: `Component ${child.meta.name} of ${newBlocklet.meta.name} is successfully deleted.`,
|
|
782
807
|
entityType: 'blocklet',
|
|
@@ -933,69 +958,88 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
933
958
|
// run hook
|
|
934
959
|
const nodeEnvironments = await states.node.getEnvironments();
|
|
935
960
|
for (const x of newConfigs) {
|
|
936
|
-
if (
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
}
|
|
961
|
+
if (x.custom === true) {
|
|
962
|
+
// custom key
|
|
963
|
+
await environmentNameSchema.validateAsync(x.key);
|
|
964
|
+
} else if (BLOCKLET_CONFIGURABLE_KEY[x.key] && x.key.startsWith('BLOCKLET_')) {
|
|
965
|
+
// app key
|
|
966
|
+
if (childDids.length) {
|
|
967
|
+
logger.error(`Cannot set ${x.key} to child blocklet`, [dids]);
|
|
968
|
+
throw new Error(`Cannot set ${x.key} to child blocklet`);
|
|
969
|
+
}
|
|
940
970
|
|
|
941
|
-
|
|
942
|
-
try {
|
|
943
|
-
fromSecretKey(x.value);
|
|
944
|
-
} catch {
|
|
971
|
+
if (x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK) {
|
|
945
972
|
try {
|
|
946
|
-
fromSecretKey(x.value
|
|
973
|
+
fromSecretKey(x.value);
|
|
947
974
|
} catch {
|
|
948
|
-
|
|
975
|
+
try {
|
|
976
|
+
fromSecretKey(x.value, 'eth');
|
|
977
|
+
} catch {
|
|
978
|
+
throw new Error('Invalid custom blocklet secret key');
|
|
979
|
+
}
|
|
949
980
|
}
|
|
950
|
-
}
|
|
951
981
|
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
982
|
+
// Ensure sk is not used by other blocklets, otherwise we may encounter appDid collision
|
|
983
|
+
const blocklets = await states.blocklet.getBlocklets({});
|
|
984
|
+
const others = blocklets.filter((b) => b.meta.did !== did);
|
|
985
|
+
if (
|
|
986
|
+
others.some(
|
|
987
|
+
(b) => b.environments.find((e) => e.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK).value === x.value
|
|
988
|
+
)
|
|
989
|
+
) {
|
|
990
|
+
throw new Error('Invalid custom blocklet secret key: already used by another blocklet');
|
|
991
|
+
}
|
|
957
992
|
}
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
if (x.key === 'BLOCKLET_APP_NAME') {
|
|
961
|
-
x.value = await titleSchema.validateAsync(x.value);
|
|
962
|
-
}
|
|
963
993
|
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
994
|
+
if (x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_NAME) {
|
|
995
|
+
x.value = await titleSchema.validateAsync(x.value);
|
|
996
|
+
}
|
|
967
997
|
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
998
|
+
if (x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_DESCRIPTION) {
|
|
999
|
+
x.value = await descriptionSchema.validateAsync(x.value);
|
|
1000
|
+
}
|
|
971
1001
|
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1002
|
+
if (
|
|
1003
|
+
[BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_LOGO, BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_LOGO_SQUARE].includes(
|
|
1004
|
+
x.key
|
|
1005
|
+
)
|
|
1006
|
+
) {
|
|
1007
|
+
x.value = await logoSchema.validateAsync(x.value);
|
|
975
1008
|
}
|
|
976
|
-
}
|
|
977
1009
|
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1010
|
+
if (x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_WALLET_TYPE) {
|
|
1011
|
+
if (['default', 'eth'].includes(x.value) === false) {
|
|
1012
|
+
throw new Error('Invalid blocklet wallet type, only "default" and "eth" are supported');
|
|
1013
|
+
}
|
|
981
1014
|
}
|
|
982
|
-
}
|
|
983
1015
|
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
throw new Error('BLOCKLET_PASSPORT_COLOR must be a hex encoded color, eg. #ffeeaa');
|
|
1016
|
+
if (x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_DELETABLE) {
|
|
1017
|
+
if (['yes', 'no'].includes(x.value) === false) {
|
|
1018
|
+
throw new Error('BLOCKLET_DELETABLE must be either "yes" or "no"');
|
|
988
1019
|
}
|
|
989
1020
|
}
|
|
990
|
-
}
|
|
991
1021
|
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1022
|
+
if (x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_PASSPORT_COLOR) {
|
|
1023
|
+
if (x.value && x.value !== 'auto') {
|
|
1024
|
+
if (x.value.length !== 7 || !isHex(x.value.slice(-6))) {
|
|
1025
|
+
throw new Error('BLOCKLET_PASSPORT_COLOR must be a hex encoded color, eg. #ffeeaa');
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
995
1028
|
}
|
|
996
1029
|
|
|
997
|
-
if (
|
|
998
|
-
|
|
1030
|
+
if (x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SPACE_ENDPOINT) {
|
|
1031
|
+
if (isEmpty(x.value)) {
|
|
1032
|
+
throw new Error(`${x.key} can not be empty`);
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
if (!urlHttp(x.value)) {
|
|
1036
|
+
throw new Error(`${x.key}(${x.value}) is not a valid http address`);
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
} else if (!BLOCKLET_CONFIGURABLE_KEY[x.key] && !isPreferenceKey(x)) {
|
|
1040
|
+
if (!(blocklet.meta.environments || []).some((y) => y.name === x.key)) {
|
|
1041
|
+
// forbid unknown format key
|
|
1042
|
+
throw new Error(`unknown format key: ${x.key}`);
|
|
999
1043
|
}
|
|
1000
1044
|
}
|
|
1001
1045
|
|
|
@@ -1037,18 +1081,53 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1037
1081
|
}
|
|
1038
1082
|
|
|
1039
1083
|
async updateWhoCanAccess({ did, whoCanAccess }) {
|
|
1040
|
-
|
|
1041
|
-
throw new Error('The blocklet does not exist');
|
|
1042
|
-
}
|
|
1084
|
+
const dids = Array.isArray(did) ? did : [did];
|
|
1043
1085
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1086
|
+
const [rootDid] = dids;
|
|
1087
|
+
|
|
1088
|
+
const isApp = dids.length === 1;
|
|
1089
|
+
|
|
1090
|
+
try {
|
|
1091
|
+
// check exist
|
|
1092
|
+
if (!(await this.hasBlocklet({ did: rootDid }))) {
|
|
1093
|
+
throw new Error('The blocklet does not exist');
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
// validate input
|
|
1097
|
+
if (
|
|
1098
|
+
!whoCanAccess.startsWith(WHO_CAN_ACCESS_PREFIX_ROLES) &&
|
|
1099
|
+
!Object.values(WHO_CAN_ACCESS).includes(whoCanAccess)
|
|
1100
|
+
) {
|
|
1101
|
+
throw new Error(`The value of whoCanAccess is invalid: ${whoCanAccess}`);
|
|
1102
|
+
} else if (whoCanAccess.startsWith(WHO_CAN_ACCESS_PREFIX_ROLES)) {
|
|
1103
|
+
if (!whoCanAccess.substring(WHO_CAN_ACCESS_PREFIX_ROLES.length).trim()) {
|
|
1104
|
+
throw new Error('Roles in whoCanAccess cannot be empty');
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
if (whoCanAccess.length > 200) {
|
|
1108
|
+
throw new Error('The length of whoCanAccess should not exceed 200');
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
const roleNames = (await this.teamManager.getRoles(rootDid)).map((x) => x.name);
|
|
1112
|
+
const accessRoleNames = getRolesFromAuthConfig({ whoCanAccess });
|
|
1113
|
+
const noExistNames = accessRoleNames.filter((x) => !roleNames.includes(x));
|
|
1114
|
+
if (noExistNames.length) {
|
|
1115
|
+
throw new Error(`Found no exist role names: ${noExistNames.join(',')}`);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
} catch (error) {
|
|
1119
|
+
logger.error(error.message);
|
|
1120
|
+
throw error;
|
|
1047
1121
|
}
|
|
1048
1122
|
|
|
1049
|
-
|
|
1123
|
+
if (isApp) {
|
|
1124
|
+
await states.blockletExtras.setSettings(rootDid, { whoCanAccess });
|
|
1125
|
+
} else {
|
|
1126
|
+
const configs = [{ key: BLOCKLET_CONFIGURABLE_KEY.COMPONENT_ACCESS_WHO, value: whoCanAccess }];
|
|
1127
|
+
await states.blockletExtras.setConfigs(dids, configs);
|
|
1128
|
+
}
|
|
1050
1129
|
|
|
1051
|
-
const blocklet = await this.ensureBlocklet(
|
|
1130
|
+
const blocklet = await this.ensureBlocklet(rootDid);
|
|
1052
1131
|
|
|
1053
1132
|
this.emit(BlockletEvents.updated, { meta: { did: blocklet.meta.did } });
|
|
1054
1133
|
|
|
@@ -1360,7 +1439,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1360
1439
|
logger.error('queue failed', { entity: 'blocklet', action, did, version, name, error: err });
|
|
1361
1440
|
await this._rollback(action, did, oldBlocklet);
|
|
1362
1441
|
this.emit(`blocklet.${action}.failed`, { did, version, err });
|
|
1363
|
-
|
|
1442
|
+
this._createNotification(did, {
|
|
1364
1443
|
title: `Blocklet ${capitalize(action)} Failed`,
|
|
1365
1444
|
description: `Blocklet ${name}@${version} ${action} failed with error: ${err.message || 'queue exception'}`,
|
|
1366
1445
|
entityType: 'blocklet',
|
|
@@ -1759,7 +1838,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1759
1838
|
message: err.message,
|
|
1760
1839
|
},
|
|
1761
1840
|
});
|
|
1762
|
-
|
|
1841
|
+
this._createNotification(did, {
|
|
1763
1842
|
title: 'Blocklet Download Failed',
|
|
1764
1843
|
description: `Blocklet ${name}@${version} download failed with error: ${err.message}`,
|
|
1765
1844
|
entityType: 'blocklet',
|
|
@@ -1897,7 +1976,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1897
1976
|
await this.deleteProcess({ did }, context);
|
|
1898
1977
|
await states.blocklet.setBlockletStatus(did, BlockletStatus.error);
|
|
1899
1978
|
|
|
1900
|
-
|
|
1979
|
+
this._createNotification(did, {
|
|
1901
1980
|
title: 'Blocklet Start Failed',
|
|
1902
1981
|
description: `Blocklet ${name} start failed: ${error.message}`,
|
|
1903
1982
|
entityType: 'blocklet',
|
|
@@ -2519,6 +2598,11 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2519
2598
|
await refreshAccessibleExternalNodeIp(nodeInfo);
|
|
2520
2599
|
},
|
|
2521
2600
|
},
|
|
2601
|
+
{
|
|
2602
|
+
name: 'delete-expired-external-blocklet',
|
|
2603
|
+
time: '0 */30 * * * *', // 30min
|
|
2604
|
+
fn: () => this._deleteExpiredExternalBlocklet(),
|
|
2605
|
+
},
|
|
2522
2606
|
];
|
|
2523
2607
|
}
|
|
2524
2608
|
|
|
@@ -2638,7 +2722,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2638
2722
|
logger.error('failed to remove blocklet on install error', { did: meta.did, error: e });
|
|
2639
2723
|
}
|
|
2640
2724
|
|
|
2641
|
-
|
|
2725
|
+
this._createNotification(did, {
|
|
2642
2726
|
title: 'Blocklet Install Failed',
|
|
2643
2727
|
description: `Blocklet ${name}@${version} install failed with error: ${err.message || 'queue exception'}`,
|
|
2644
2728
|
entityType: 'blocklet',
|
|
@@ -2651,7 +2735,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2651
2735
|
return blocklet1;
|
|
2652
2736
|
} catch (err) {
|
|
2653
2737
|
logger.error('failed to install blocklet', { name, did, version, error: err });
|
|
2654
|
-
|
|
2738
|
+
this._createNotification(did, {
|
|
2655
2739
|
title: 'Blocklet Install Failed',
|
|
2656
2740
|
description: `Blocklet ${name}@${version} install failed with error: ${err.message || 'queue exception'}`,
|
|
2657
2741
|
entityType: 'blocklet',
|
|
@@ -2749,7 +2833,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2749
2833
|
downgrade: BlockletEvents.downgradeFailed,
|
|
2750
2834
|
};
|
|
2751
2835
|
this.emit(eventNames[action], { blocklet: oldBlocklet, context });
|
|
2752
|
-
|
|
2836
|
+
this._createNotification(did, {
|
|
2753
2837
|
title: `Blocklet ${capitalize(action)} Failed`,
|
|
2754
2838
|
description: `Blocklet ${name}@${version} ${action} failed with error: ${err.message || 'queue exception'}`,
|
|
2755
2839
|
entityType: 'blocklet',
|
|
@@ -2940,7 +3024,12 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2940
3024
|
});
|
|
2941
3025
|
}
|
|
2942
3026
|
|
|
2943
|
-
|
|
3027
|
+
if (blocklet.controller && process.env.NODE_ENV !== 'test') {
|
|
3028
|
+
const nodeInfo = await states.node.read();
|
|
3029
|
+
await consumeServerlessNFT({ nftId: blocklet.controller.nftId, nodeInfo, blocklet });
|
|
3030
|
+
}
|
|
3031
|
+
|
|
3032
|
+
this._createNotification(did, {
|
|
2944
3033
|
title: 'Blocklet Installed',
|
|
2945
3034
|
description: `Blocklet ${meta.name}@${meta.version} is installed successfully. (Source: ${
|
|
2946
3035
|
deployedFrom || fromBlockletSource(source)
|
|
@@ -2963,7 +3052,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2963
3052
|
message: err.message,
|
|
2964
3053
|
},
|
|
2965
3054
|
});
|
|
2966
|
-
|
|
3055
|
+
this._createNotification(did, {
|
|
2967
3056
|
title: 'Blocklet Install Failed',
|
|
2968
3057
|
description: `Blocklet ${meta.name}@${meta.version} install failed with error: ${err.message}`,
|
|
2969
3058
|
entityType: 'blocklet',
|
|
@@ -3058,7 +3147,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3058
3147
|
downgrade: BlockletEvents.downgraded,
|
|
3059
3148
|
};
|
|
3060
3149
|
this.emit(eventNames[action], { blocklet, context });
|
|
3061
|
-
|
|
3150
|
+
this._createNotification(did, {
|
|
3062
3151
|
title: `Blocklet ${capitalize(action)} Success`,
|
|
3063
3152
|
description: `Blocklet ${name}@${version} ${action} successfully. (Source: ${
|
|
3064
3153
|
deployedFrom || fromBlockletSource(source)
|
|
@@ -3086,9 +3175,9 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3086
3175
|
upgrade: BlockletEvents.upgradeFailed,
|
|
3087
3176
|
downgrade: BlockletEvents.downgradeFailed,
|
|
3088
3177
|
};
|
|
3089
|
-
this.emit(eventNames[action], { blocklet: oldBlocklet, context });
|
|
3178
|
+
this.emit(eventNames[action], { blocklet: { ...oldBlocklet, error: { message: err.message } }, context });
|
|
3090
3179
|
|
|
3091
|
-
|
|
3180
|
+
this._createNotification(did, {
|
|
3092
3181
|
title: `Blocklet ${capitalize(action)} Failed`,
|
|
3093
3182
|
description: `Blocklet ${name}@${version} ${action} failed with error: ${err.message}`,
|
|
3094
3183
|
entityType: 'blocklet',
|
|
@@ -3643,6 +3732,64 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3643
3732
|
|
|
3644
3733
|
await forEachBlocklet(blocklet, postInstall, { parallel: true });
|
|
3645
3734
|
}
|
|
3735
|
+
|
|
3736
|
+
async _createNotification(did, notification) {
|
|
3737
|
+
try {
|
|
3738
|
+
const isExternal = await states.blocklet.isExternalBlocklet(did);
|
|
3739
|
+
|
|
3740
|
+
if (isExternal) {
|
|
3741
|
+
return;
|
|
3742
|
+
}
|
|
3743
|
+
|
|
3744
|
+
await states.notification.create(notification);
|
|
3745
|
+
} catch (error) {
|
|
3746
|
+
logger.error('create notification failed', { error });
|
|
3747
|
+
}
|
|
3748
|
+
}
|
|
3749
|
+
|
|
3750
|
+
async _deleteExpiredExternalBlocklet() {
|
|
3751
|
+
try {
|
|
3752
|
+
logger.info('start check expired external blocklet');
|
|
3753
|
+
const blocklets = await states.blocklet.getBlocklets({
|
|
3754
|
+
controller: {
|
|
3755
|
+
$exists: true,
|
|
3756
|
+
},
|
|
3757
|
+
});
|
|
3758
|
+
|
|
3759
|
+
const nodeInfo = await states.node.read();
|
|
3760
|
+
|
|
3761
|
+
const tasks = blocklets.map(async (blocklet) => {
|
|
3762
|
+
try {
|
|
3763
|
+
const assetState = await getNFTState(nodeInfo.launcher.chainHost, blocklet.controller.nftId);
|
|
3764
|
+
const isExpired = isNFTExpired(assetState);
|
|
3765
|
+
if (isExpired) {
|
|
3766
|
+
logger.info('the blocklet already expired', {
|
|
3767
|
+
blockletId: blocklet._id,
|
|
3768
|
+
nftId: blocklet.controller.nftId,
|
|
3769
|
+
});
|
|
3770
|
+
|
|
3771
|
+
// FIXME: 后面需要考虑将数据保留一段时间
|
|
3772
|
+
await this.delete({ did: blocklet.meta.did, keepData: false, keepConfigs: false, keepLogsDir: false });
|
|
3773
|
+
logger.info('the expired blocklet already deleted', {
|
|
3774
|
+
blockletId: blocklet._id,
|
|
3775
|
+
nftId: blocklet.controller.nftId,
|
|
3776
|
+
});
|
|
3777
|
+
}
|
|
3778
|
+
} catch (error) {
|
|
3779
|
+
logger.error('get asset state failed when check expired external blocklet', {
|
|
3780
|
+
blockletId: blocklet._id,
|
|
3781
|
+
nftId: blocklet.controller.nftId,
|
|
3782
|
+
});
|
|
3783
|
+
}
|
|
3784
|
+
});
|
|
3785
|
+
|
|
3786
|
+
await Promise.all(tasks);
|
|
3787
|
+
|
|
3788
|
+
logger.info('check expired external blocklet end');
|
|
3789
|
+
} catch (error) {
|
|
3790
|
+
logger.info('check expired external blocklet failed', { error });
|
|
3791
|
+
}
|
|
3792
|
+
}
|
|
3646
3793
|
}
|
|
3647
3794
|
|
|
3648
3795
|
module.exports = BlockletManager;
|
package/lib/index.js
CHANGED
|
@@ -6,7 +6,6 @@ const Cron = require('@abtnode/cron');
|
|
|
6
6
|
|
|
7
7
|
const logger = require('@abtnode/logger')('@abtnode/core');
|
|
8
8
|
const { fromBlockletStatus, toBlockletStatus, fromBlockletSource, toBlockletSource } = require('@blocklet/constant');
|
|
9
|
-
const { getServiceMetas } = require('@blocklet/meta/lib/service');
|
|
10
9
|
const { listProviders } = require('@abtnode/router-provider');
|
|
11
10
|
const { DEFAULT_CERTIFICATE_EMAIL, EVENTS } = require('@abtnode/constant');
|
|
12
11
|
|
|
@@ -384,9 +383,6 @@ function ABTNode(options) {
|
|
|
384
383
|
),
|
|
385
384
|
endSession: (params, context) => states.session.end(params.id, context),
|
|
386
385
|
|
|
387
|
-
// Services
|
|
388
|
-
getServices: (params, context) => getServiceMetas({ stringifySchema: true }, context),
|
|
389
|
-
|
|
390
386
|
// Utilities: moved here because some deps require native build
|
|
391
387
|
getSysInfo,
|
|
392
388
|
getSysInfoEmitter: (interval) => new SysInfoEmitter(interval),
|
package/lib/states/blocklet.js
CHANGED
|
@@ -106,6 +106,21 @@ class BlockletState extends BaseState {
|
|
|
106
106
|
});
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
async isExternalBlocklet(did) {
|
|
110
|
+
if (!did) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const exist = await this.findOne({
|
|
115
|
+
$or: [{ 'meta.did': did }, { appDid: did }],
|
|
116
|
+
controller: {
|
|
117
|
+
$exists: true,
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return !!exist;
|
|
122
|
+
}
|
|
123
|
+
|
|
109
124
|
async getBlockletStatus(did) {
|
|
110
125
|
return new Promise((resolve, reject) => {
|
|
111
126
|
if (!did) {
|
|
@@ -76,44 +76,30 @@ class NotificationState extends BaseState {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
// eslint-disable-next-line no-unused-vars
|
|
79
|
-
read({ id }, context) {
|
|
79
|
+
async read({ id }, context) {
|
|
80
80
|
const idList = id.split(',').map((x) => x.trim());
|
|
81
81
|
logger.info('mark notification as read', { idList });
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
(err, numAffected) => {
|
|
89
|
-
if (err) {
|
|
90
|
-
return reject(err);
|
|
91
|
-
}
|
|
83
|
+
const [numAffected] = await this.update(
|
|
84
|
+
{ _id: { $in: idList } },
|
|
85
|
+
{ $set: { read: true } },
|
|
86
|
+
{ multi: true, upsert: false, returnUpdatedDocs: false }
|
|
87
|
+
);
|
|
92
88
|
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
);
|
|
96
|
-
});
|
|
89
|
+
return numAffected;
|
|
97
90
|
}
|
|
98
91
|
|
|
99
92
|
// eslint-disable-next-line no-unused-vars
|
|
100
|
-
unread({ id }, context) {
|
|
93
|
+
async unread({ id }, context) {
|
|
101
94
|
const idList = Array.isArray(id) ? id : [id];
|
|
102
95
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
(err, numAffected) => {
|
|
109
|
-
if (err) {
|
|
110
|
-
return reject(err);
|
|
111
|
-
}
|
|
96
|
+
const [numAffected] = await this.update(
|
|
97
|
+
{ _id: { $in: idList } },
|
|
98
|
+
{ $set: { read: false } },
|
|
99
|
+
{ multi: true, upsert: false, returnUpdatedDocs: false }
|
|
100
|
+
);
|
|
112
101
|
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
);
|
|
116
|
-
});
|
|
102
|
+
return numAffected;
|
|
117
103
|
}
|
|
118
104
|
}
|
|
119
105
|
|
package/lib/team/manager.js
CHANGED
|
@@ -5,6 +5,7 @@ const path = require('path');
|
|
|
5
5
|
const { EventEmitter } = require('events');
|
|
6
6
|
const upperFirst = require('lodash/upperFirst');
|
|
7
7
|
const get = require('lodash/get');
|
|
8
|
+
const pick = require('lodash/pick');
|
|
8
9
|
|
|
9
10
|
const { createRBAC, MemoryStorage, NedbStorage } = require('@abtnode/rbac');
|
|
10
11
|
const logger = require('@abtnode/logger')('@abtnode/core:team:manager');
|
|
@@ -314,6 +315,14 @@ class TeamManager extends EventEmitter {
|
|
|
314
315
|
return owner;
|
|
315
316
|
}
|
|
316
317
|
|
|
318
|
+
async getRoles(did) {
|
|
319
|
+
const rbac = await this.getRBAC(did);
|
|
320
|
+
|
|
321
|
+
const roles = await rbac.getRoles();
|
|
322
|
+
|
|
323
|
+
return roles.map((d) => pick(d, ['name', 'grants', 'title', 'description']));
|
|
324
|
+
}
|
|
325
|
+
|
|
317
326
|
async initTeam(did) {
|
|
318
327
|
if (!did) {
|
|
319
328
|
logger.error('initTeam: did does not exist');
|
package/lib/util/blocklet.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs-extra');
|
|
4
4
|
const path = require('path');
|
|
5
|
+
const joinURL = require('url-join');
|
|
5
6
|
const shelljs = require('shelljs');
|
|
6
7
|
const os = require('os');
|
|
7
8
|
const tar = require('tar');
|
|
@@ -10,8 +11,12 @@ const streamToPromise = require('stream-to-promise');
|
|
|
10
11
|
const { Throttle } = require('stream-throttle');
|
|
11
12
|
const ssri = require('ssri');
|
|
12
13
|
const diff = require('deep-diff');
|
|
14
|
+
const axios = require('@abtnode/util/lib/axios');
|
|
15
|
+
const { stableStringify } = require('@arcblock/vc');
|
|
13
16
|
|
|
14
|
-
const {
|
|
17
|
+
const { fromSecretKey, WalletType } = require('@ocap/wallet');
|
|
18
|
+
const { toHex, toBase58 } = require('@ocap/util');
|
|
19
|
+
const { types } = require('@ocap/mcrypto');
|
|
15
20
|
const { isValid: isValidDid } = require('@arcblock/did');
|
|
16
21
|
const logger = require('@abtnode/logger')('@abtnode/core:util:blocklet');
|
|
17
22
|
const pm2 = require('@abtnode/util/lib/async-pm2');
|
|
@@ -1257,7 +1262,7 @@ const needBlockletDownload = (blocklet, oldBlocklet) => {
|
|
|
1257
1262
|
return true;
|
|
1258
1263
|
}
|
|
1259
1264
|
|
|
1260
|
-
return
|
|
1265
|
+
return get(oldBlocklet, 'meta.dist.integrity') !== get(blocklet, 'meta.dist.integrity');
|
|
1261
1266
|
};
|
|
1262
1267
|
|
|
1263
1268
|
const findAvailableDid = (meta, siblings) => {
|
|
@@ -1436,7 +1441,35 @@ const getConfigFromPreferences = (blocklet) => {
|
|
|
1436
1441
|
return result;
|
|
1437
1442
|
};
|
|
1438
1443
|
|
|
1444
|
+
const consumeServerlessNFT = async ({ nftId, nodeInfo, blocklet }) => {
|
|
1445
|
+
try {
|
|
1446
|
+
const { url } = nodeInfo.launcher;
|
|
1447
|
+
const type = WalletType({
|
|
1448
|
+
role: types.RoleType.ROLE_APPLICATION,
|
|
1449
|
+
pk: types.KeyType.ED25519,
|
|
1450
|
+
hash: types.HashType.SHA3,
|
|
1451
|
+
});
|
|
1452
|
+
const wallet = fromSecretKey(nodeInfo.sk, type);
|
|
1453
|
+
const appURL = blocklet.environments.find((item) => item.key === 'BLOCKLET_APP_URL').value;
|
|
1454
|
+
|
|
1455
|
+
const body = { nftId, appURL };
|
|
1456
|
+
|
|
1457
|
+
const { data } = await axios.post(joinURL(url, '/api/serverless/consume'), body, {
|
|
1458
|
+
headers: {
|
|
1459
|
+
'x-sig': toBase58(wallet.sign(stableStringify(body))),
|
|
1460
|
+
},
|
|
1461
|
+
});
|
|
1462
|
+
|
|
1463
|
+
logger.error('consume serverless nft success', { nftId, hash: data.hash });
|
|
1464
|
+
} catch (error) {
|
|
1465
|
+
logger.error('consume serverless nft failed', { nftId, error });
|
|
1466
|
+
|
|
1467
|
+
throw new Error(`consume nft ${nftId} failed`);
|
|
1468
|
+
}
|
|
1469
|
+
};
|
|
1470
|
+
|
|
1439
1471
|
module.exports = {
|
|
1472
|
+
consumeServerlessNFT,
|
|
1440
1473
|
forEachBlocklet,
|
|
1441
1474
|
getBlockletMetaFromUrl: (url) => getBlockletMetaFromUrl(url, { logger }),
|
|
1442
1475
|
parseChildrenFromMeta,
|
package/lib/util/index.js
CHANGED
|
@@ -394,6 +394,49 @@ const getDelegateState = async (chainHost, address) => {
|
|
|
394
394
|
return get(result.data, 'data.getDelegateState.state');
|
|
395
395
|
};
|
|
396
396
|
|
|
397
|
+
const getNFTState = async (chainHost, nftId) => {
|
|
398
|
+
const url = joinUrl(new URL(chainHost).origin, '/api/gql/');
|
|
399
|
+
|
|
400
|
+
const result = await axios.post(
|
|
401
|
+
url,
|
|
402
|
+
JSON.stringify({
|
|
403
|
+
query: `{
|
|
404
|
+
getAssetState(address: "${nftId}") {
|
|
405
|
+
state {
|
|
406
|
+
address
|
|
407
|
+
data {
|
|
408
|
+
typeUrl
|
|
409
|
+
value
|
|
410
|
+
}
|
|
411
|
+
display {
|
|
412
|
+
type
|
|
413
|
+
content
|
|
414
|
+
}
|
|
415
|
+
issuer
|
|
416
|
+
owner
|
|
417
|
+
parent
|
|
418
|
+
tags
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}`,
|
|
422
|
+
}),
|
|
423
|
+
{
|
|
424
|
+
headers: {
|
|
425
|
+
'Content-Type': 'application/json',
|
|
426
|
+
Accept: 'application/json',
|
|
427
|
+
},
|
|
428
|
+
timeout: 60 * 1000,
|
|
429
|
+
}
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
const state = get(result, 'data.data.getAssetState.state');
|
|
433
|
+
if (state && state.data.typeUrl === 'json') {
|
|
434
|
+
state.data.value = JSON.parse(state.data.value);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return state;
|
|
438
|
+
};
|
|
439
|
+
|
|
397
440
|
const lib = {
|
|
398
441
|
validateOwner,
|
|
399
442
|
getProviderFromNodeInfo,
|
|
@@ -428,6 +471,7 @@ const lib = {
|
|
|
428
471
|
memoizeAsync,
|
|
429
472
|
getStateCrons,
|
|
430
473
|
getDelegateState,
|
|
474
|
+
getNFTState,
|
|
431
475
|
};
|
|
432
476
|
|
|
433
477
|
module.exports = lib;
|
|
@@ -4,11 +4,9 @@ const { didExtension } = require('@blocklet/meta/lib/extension');
|
|
|
4
4
|
const Joi = JOI.extend(didExtension);
|
|
5
5
|
|
|
6
6
|
const blockletController = Joi.object({
|
|
7
|
-
id: Joi.DID().required(), // userDid
|
|
8
7
|
nftId: Joi.DID().required(),
|
|
9
8
|
nftOwner: Joi.DID().required(),
|
|
10
9
|
appMaxCount: Joi.number().required().min(1),
|
|
11
|
-
expireDate: Joi.date(),
|
|
12
10
|
}).options({ stripUnknown: true });
|
|
13
11
|
|
|
14
12
|
module.exports = {
|
package/lib/validators/role.js
CHANGED
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.38",
|
|
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.38",
|
|
23
|
+
"@abtnode/certificate-manager": "1.8.38",
|
|
24
|
+
"@abtnode/constant": "1.8.38",
|
|
25
|
+
"@abtnode/cron": "1.8.38",
|
|
26
|
+
"@abtnode/db": "1.8.38",
|
|
27
|
+
"@abtnode/logger": "1.8.38",
|
|
28
|
+
"@abtnode/queue": "1.8.38",
|
|
29
|
+
"@abtnode/rbac": "1.8.38",
|
|
30
|
+
"@abtnode/router-provider": "1.8.38",
|
|
31
|
+
"@abtnode/static-server": "1.8.38",
|
|
32
|
+
"@abtnode/timemachine": "1.8.38",
|
|
33
|
+
"@abtnode/util": "1.8.38",
|
|
34
|
+
"@arcblock/did": "1.18.18",
|
|
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.18",
|
|
37
|
+
"@arcblock/event-hub": "1.18.18",
|
|
38
|
+
"@arcblock/jwt": "^1.18.18",
|
|
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.18",
|
|
41
|
+
"@blocklet/constant": "1.8.38",
|
|
42
|
+
"@blocklet/meta": "1.8.38",
|
|
43
|
+
"@blocklet/sdk": "1.8.38",
|
|
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.18",
|
|
46
|
+
"@ocap/util": "1.18.18",
|
|
47
|
+
"@ocap/wallet": "1.18.18",
|
|
48
48
|
"@slack/webhook": "^5.0.4",
|
|
49
49
|
"axios": "^0.27.2",
|
|
50
50
|
"axon": "^2.0.3",
|
|
@@ -82,5 +82,5 @@
|
|
|
82
82
|
"express": "^4.18.2",
|
|
83
83
|
"jest": "^27.5.1"
|
|
84
84
|
},
|
|
85
|
-
"gitHead": "
|
|
85
|
+
"gitHead": "cbb88f107eb6b403c8cd47a765920575860c7bc7"
|
|
86
86
|
}
|