@abtnode/core 1.8.13 → 1.8.16
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 +124 -2
- package/lib/blocklet/manager/disk.js +27 -72
- package/lib/index.js +3 -1
- package/lib/states/audit-log.js +11 -1
- package/lib/states/blocklet.js +4 -1
- package/lib/util/blocklet.js +81 -84
- package/lib/util/index.js +0 -64
- package/package.json +24 -24
package/lib/api/team.js
CHANGED
|
@@ -1,13 +1,35 @@
|
|
|
1
1
|
const { EventEmitter } = require('events');
|
|
2
2
|
const pick = require('lodash/pick');
|
|
3
|
+
const joinUrl = require('url-join');
|
|
4
|
+
|
|
3
5
|
const logger = require('@abtnode/logger')('@abtnode/core:api:team');
|
|
4
|
-
const {
|
|
6
|
+
const {
|
|
7
|
+
ROLES,
|
|
8
|
+
genPermissionName,
|
|
9
|
+
EVENTS,
|
|
10
|
+
WHO_CAN_ACCESS,
|
|
11
|
+
PASSPORT_STATUS,
|
|
12
|
+
WELLKNOWN_SERVICE_PATH_PREFIX,
|
|
13
|
+
} = require('@abtnode/constant');
|
|
5
14
|
const { isValid: isValidDid } = require('@arcblock/did');
|
|
6
15
|
const { BlockletEvents } = require('@blocklet/meta/lib/constants');
|
|
16
|
+
const { sendToUser } = require('@blocklet/sdk/lib/util/send-notification');
|
|
17
|
+
const {
|
|
18
|
+
createPassportVC,
|
|
19
|
+
createPassport,
|
|
20
|
+
upsertToPassports,
|
|
21
|
+
createUserPassport,
|
|
22
|
+
} = require('@abtnode/auth/lib/passport');
|
|
23
|
+
const { getPassportStatusEndpoint } = require('@abtnode/auth/lib/auth');
|
|
24
|
+
const getBlockletInfo = require('@blocklet/meta/lib/info');
|
|
25
|
+
const { parseUserAvatar } = require('@abtnode/util/lib/user-avatar');
|
|
26
|
+
|
|
7
27
|
const { validateTrustedPassportIssuers } = require('../validators/trusted-passport');
|
|
8
28
|
const { validateCreateRole, validateUpdateRole } = require('../validators/role');
|
|
9
29
|
const { validateCreatePermission, validateUpdatePermission } = require('../validators/permission');
|
|
10
30
|
|
|
31
|
+
const { getBlocklet } = require('../util/blocklet');
|
|
32
|
+
|
|
11
33
|
const MAX_USER_PAGE_SIZE = 100;
|
|
12
34
|
|
|
13
35
|
const validateReservedRole = (role) => {
|
|
@@ -17,19 +39,53 @@ const validateReservedRole = (role) => {
|
|
|
17
39
|
return true;
|
|
18
40
|
};
|
|
19
41
|
|
|
42
|
+
const sendPassportVcNotification = ({ userDid, appWallet: wallet, locale, vc }) => {
|
|
43
|
+
try {
|
|
44
|
+
const receiver = userDid;
|
|
45
|
+
const sender = { appDid: wallet.address, appSk: wallet.secretKey };
|
|
46
|
+
const message = {
|
|
47
|
+
title: {
|
|
48
|
+
en: 'Receive a Passport',
|
|
49
|
+
zh: '获得通行证',
|
|
50
|
+
}[locale],
|
|
51
|
+
body: {
|
|
52
|
+
en: 'You got a passport',
|
|
53
|
+
zh: '你获得了一张通行证',
|
|
54
|
+
}[locale],
|
|
55
|
+
attachments: [
|
|
56
|
+
{
|
|
57
|
+
type: 'vc',
|
|
58
|
+
data: {
|
|
59
|
+
credential: vc,
|
|
60
|
+
tag: vc.credentialSubject.passport.name,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
sendToUser(receiver, message, sender, process.env.ABT_NODE_SERVICE_PORT).catch((error) => {
|
|
67
|
+
logger.error('Failed send passport vc to wallet', { error });
|
|
68
|
+
});
|
|
69
|
+
} catch (error) {
|
|
70
|
+
logger.error('Failed send passport vc to wallet', { error });
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
20
74
|
class TeamAPI extends EventEmitter {
|
|
21
75
|
/**
|
|
22
76
|
*
|
|
23
77
|
* @param {object} states abtnode core StateDB
|
|
24
78
|
*/
|
|
25
|
-
constructor({ teamManager, states }) {
|
|
79
|
+
constructor({ teamManager, states, dataDirs }) {
|
|
26
80
|
super();
|
|
27
81
|
|
|
28
82
|
this.notification = states.notification;
|
|
29
83
|
this.node = states.node;
|
|
84
|
+
this.states = states;
|
|
30
85
|
this.memberInviteExpireTime = 1000 * 3600 * 24 * 30; // 30 days
|
|
31
86
|
this.serverInviteExpireTime = 1000 * 3600; // 1 hour
|
|
32
87
|
this.teamManager = teamManager;
|
|
88
|
+
this.dataDirs = dataDirs;
|
|
33
89
|
}
|
|
34
90
|
|
|
35
91
|
// User && Invitation
|
|
@@ -223,6 +279,72 @@ class TeamAPI extends EventEmitter {
|
|
|
223
279
|
return doc;
|
|
224
280
|
}
|
|
225
281
|
|
|
282
|
+
async issuePassportToUser({ teamDid, userDid, role: roleName, notify = true }, context = {}) {
|
|
283
|
+
const { locale = 'en' } = context;
|
|
284
|
+
|
|
285
|
+
const userState = await this.getUserState(teamDid);
|
|
286
|
+
const user = await userState.getUser(userDid);
|
|
287
|
+
|
|
288
|
+
if (!user) {
|
|
289
|
+
throw new Error(`user does not exist: ${userDid}`);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (!user.approved) {
|
|
293
|
+
throw new Error(`the user is revoked: ${userDid}`);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const rbac = await this.getRBAC(teamDid);
|
|
297
|
+
const role = await rbac.getRole(roleName);
|
|
298
|
+
|
|
299
|
+
if (!role) {
|
|
300
|
+
throw new Error(`passport does not exist: ${roleName}`);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// create vc
|
|
304
|
+
const blocklet = await getBlocklet({ did: teamDid, states: this.states, dataDirs: this.dataDirs });
|
|
305
|
+
const nodeInfo = await this.node.read();
|
|
306
|
+
const blockletInfo = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
307
|
+
const { wallet, passportColor, appUrl, name: issuerName } = blockletInfo;
|
|
308
|
+
|
|
309
|
+
const vc = createPassportVC({
|
|
310
|
+
issuerName,
|
|
311
|
+
issuerWallet: wallet,
|
|
312
|
+
ownerDid: userDid,
|
|
313
|
+
passport: await createPassport({
|
|
314
|
+
role,
|
|
315
|
+
}),
|
|
316
|
+
endpoint: getPassportStatusEndpoint({
|
|
317
|
+
baseUrl: joinUrl(appUrl, WELLKNOWN_SERVICE_PATH_PREFIX),
|
|
318
|
+
userDid,
|
|
319
|
+
teamDid,
|
|
320
|
+
}),
|
|
321
|
+
ownerProfile: {
|
|
322
|
+
...user,
|
|
323
|
+
avatar: await parseUserAvatar(user.avatar, { dataDir: blocklet.env.dataDir }),
|
|
324
|
+
},
|
|
325
|
+
preferredColor: passportColor,
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
// write passport to db
|
|
329
|
+
const passport = createUserPassport(vc, { role: role.name });
|
|
330
|
+
user.passports = upsertToPassports(user.passports || [], passport);
|
|
331
|
+
await this.updateUser({
|
|
332
|
+
teamDid,
|
|
333
|
+
user: {
|
|
334
|
+
did: user.did,
|
|
335
|
+
pk: user.pk,
|
|
336
|
+
passports: user.passports,
|
|
337
|
+
},
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
// send vc to wallet
|
|
341
|
+
if (notify) {
|
|
342
|
+
sendPassportVcNotification({ userDid, appWallet: wallet, locale, vc });
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return user;
|
|
346
|
+
}
|
|
347
|
+
|
|
226
348
|
async revokeUserPassport({ teamDid, userDid, passportId } = {}) {
|
|
227
349
|
if (!passportId) {
|
|
228
350
|
throw new Error('Revoked passport should not be empty');
|
|
@@ -36,7 +36,6 @@ const {
|
|
|
36
36
|
forEachBlocklet,
|
|
37
37
|
forEachChild,
|
|
38
38
|
getComponentId,
|
|
39
|
-
getComponentName,
|
|
40
39
|
getComponentBundleId,
|
|
41
40
|
} = require('@blocklet/meta/lib/util');
|
|
42
41
|
const getComponentProcessId = require('@blocklet/meta/lib/get-component-process-id');
|
|
@@ -73,9 +72,7 @@ const {
|
|
|
73
72
|
getFromCache: getAccessibleExternalNodeIp,
|
|
74
73
|
} = require('../../util/get-accessible-external-node-ip');
|
|
75
74
|
const {
|
|
76
|
-
getComponentDirs,
|
|
77
75
|
getBlockletMetaFromUrl,
|
|
78
|
-
fillBlockletConfigs,
|
|
79
76
|
ensureBlockletExpanded,
|
|
80
77
|
getAppSystemEnvironments,
|
|
81
78
|
getComponentSystemEnvironments,
|
|
@@ -104,6 +101,7 @@ const {
|
|
|
104
101
|
needBlockletDownload,
|
|
105
102
|
findAvailableDid,
|
|
106
103
|
ensureMeta,
|
|
104
|
+
getBlocklet,
|
|
107
105
|
} = require('../../util/blocklet');
|
|
108
106
|
const { parseSourceUrl } = require('../../util/registry');
|
|
109
107
|
const states = require('../../states');
|
|
@@ -217,7 +215,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
217
215
|
* @param {{
|
|
218
216
|
* url: string;
|
|
219
217
|
* sync: boolean = false;
|
|
220
|
-
*
|
|
218
|
+
* downloadTokenList: Array<{did: string, token: string};
|
|
221
219
|
* }} params
|
|
222
220
|
* @param {{
|
|
223
221
|
* [key: string]: any
|
|
@@ -228,20 +226,16 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
228
226
|
async install(params, context = {}) {
|
|
229
227
|
logger.debug('install blocklet', { params, context });
|
|
230
228
|
|
|
231
|
-
|
|
232
|
-
const info = await states.node.read();
|
|
229
|
+
const info = await states.node.read();
|
|
233
230
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}),
|
|
243
|
-
});
|
|
244
|
-
}
|
|
231
|
+
context.headers = Object.assign(context?.headers || {}, {
|
|
232
|
+
'x-server-did': info.did,
|
|
233
|
+
'x-server-public-key': info.pk,
|
|
234
|
+
'x-server-signature': sign(info.did, info.sk, {
|
|
235
|
+
exp: (Date.now() + 5 * 60 * 1000) / 1000,
|
|
236
|
+
}),
|
|
237
|
+
});
|
|
238
|
+
context.downloadTokenList = params.downloadTokenList || [];
|
|
245
239
|
|
|
246
240
|
const source = getSourceFromInstallParams(params);
|
|
247
241
|
if (typeof context.startImmediately === 'undefined') {
|
|
@@ -289,7 +283,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
289
283
|
* @param {ConfigEntry} configs pre configs
|
|
290
284
|
*/
|
|
291
285
|
async installComponent(
|
|
292
|
-
{ rootDid, mountPoint, url, file, did, diffVersion, deleteSet, title, name, configs,
|
|
286
|
+
{ rootDid, mountPoint, url, file, did, diffVersion, deleteSet, title, name, configs, downloadTokenList },
|
|
293
287
|
context = {}
|
|
294
288
|
) {
|
|
295
289
|
logger.debug('start install component', { rootDid, mountPoint, url });
|
|
@@ -308,7 +302,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
308
302
|
did,
|
|
309
303
|
name,
|
|
310
304
|
configs,
|
|
311
|
-
|
|
305
|
+
downloadTokenList,
|
|
312
306
|
});
|
|
313
307
|
}
|
|
314
308
|
|
|
@@ -1298,49 +1292,8 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1298
1292
|
return rootBlocklet;
|
|
1299
1293
|
}
|
|
1300
1294
|
|
|
1301
|
-
async ensureBlocklet(did,
|
|
1302
|
-
|
|
1303
|
-
throw new Error(`Blocklet did is invalid: ${did}`);
|
|
1304
|
-
}
|
|
1305
|
-
|
|
1306
|
-
const blocklet = await states.blocklet.getBlocklet(did);
|
|
1307
|
-
if (!blocklet) {
|
|
1308
|
-
if (throwOnNotExist) {
|
|
1309
|
-
throw new Error(`Can not find blocklet in database by did ${did}`);
|
|
1310
|
-
}
|
|
1311
|
-
return null;
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
|
-
// app settings
|
|
1315
|
-
const settings = await states.blockletExtras.getSettings(blocklet.meta.did);
|
|
1316
|
-
blocklet.trustedPassports = get(settings, 'trustedPassports') || [];
|
|
1317
|
-
blocklet.enablePassportIssuance = get(settings, 'enablePassportIssuance', true);
|
|
1318
|
-
blocklet.settings = settings || {};
|
|
1319
|
-
|
|
1320
|
-
// app site
|
|
1321
|
-
blocklet.site = await this.getSiteByDid(blocklet.meta.did);
|
|
1322
|
-
|
|
1323
|
-
await forEachBlocklet(blocklet, async (component, { id, level, ancestors }) => {
|
|
1324
|
-
// component env
|
|
1325
|
-
component.env = {
|
|
1326
|
-
id,
|
|
1327
|
-
name: getComponentName(component, ancestors),
|
|
1328
|
-
processId: getComponentProcessId(component, ancestors),
|
|
1329
|
-
...getComponentDirs(component, {
|
|
1330
|
-
dataDirs: this.dataDirs,
|
|
1331
|
-
ensure: true,
|
|
1332
|
-
validate: validateEnv,
|
|
1333
|
-
ancestors,
|
|
1334
|
-
e2eMode: level === 0 ? e2eMode : false,
|
|
1335
|
-
}),
|
|
1336
|
-
};
|
|
1337
|
-
|
|
1338
|
-
// component config
|
|
1339
|
-
const configs = await states.blockletExtras.getConfigs([...ancestors.map((x) => x.meta.did), component.meta.did]);
|
|
1340
|
-
fillBlockletConfigs(component, configs);
|
|
1341
|
-
});
|
|
1342
|
-
|
|
1343
|
-
return blocklet;
|
|
1295
|
+
async ensureBlocklet(did, opts = {}) {
|
|
1296
|
+
return getBlocklet({ ...opts, states, dataDirs: this.dataDirs, did });
|
|
1344
1297
|
}
|
|
1345
1298
|
|
|
1346
1299
|
async hasBlocklet({ did }) {
|
|
@@ -1889,7 +1842,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1889
1842
|
name: inputName,
|
|
1890
1843
|
did: inputDid,
|
|
1891
1844
|
configs,
|
|
1892
|
-
|
|
1845
|
+
downloadTokenList,
|
|
1893
1846
|
}) {
|
|
1894
1847
|
const blocklet = await this._getBlockletForInstallation(rootDid);
|
|
1895
1848
|
if (!blocklet) {
|
|
@@ -1909,14 +1862,12 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1909
1862
|
...context,
|
|
1910
1863
|
headers: {
|
|
1911
1864
|
'x-server-did': info.did,
|
|
1912
|
-
'x-download-token': downloadToken,
|
|
1913
|
-
// FIXME: 先保证兼容性,后续删除
|
|
1914
|
-
'x-server-publick-key': info.pk,
|
|
1915
1865
|
'x-server-public-key': info.pk,
|
|
1916
1866
|
'x-server-signature': sign(info.did, info.sk, {
|
|
1917
1867
|
exp: (Date.now() + 5 * 60 * 1000) / 1000,
|
|
1918
1868
|
}),
|
|
1919
1869
|
},
|
|
1870
|
+
downloadTokenList: downloadTokenList || [],
|
|
1920
1871
|
};
|
|
1921
1872
|
}
|
|
1922
1873
|
|
|
@@ -2482,14 +2433,12 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2482
2433
|
...context,
|
|
2483
2434
|
headers: {
|
|
2484
2435
|
'x-server-did': info.did,
|
|
2485
|
-
'x-download-token': blocklet?.tokens?.paidBlockletDownloadToken,
|
|
2486
|
-
// FIXME: 先保证兼容性,后续删除
|
|
2487
|
-
'x-server-publick-key': info.pk,
|
|
2488
2436
|
'x-server-public-key': info.pk,
|
|
2489
2437
|
'x-server-signature': sign(info.did, info.sk, {
|
|
2490
2438
|
exp: (Date.now() + 5 * 60 * 1000) / 1000,
|
|
2491
2439
|
}),
|
|
2492
2440
|
},
|
|
2441
|
+
downloadTokenList: blocklet?.tokens?.downloadTokenList || [],
|
|
2493
2442
|
};
|
|
2494
2443
|
}
|
|
2495
2444
|
|
|
@@ -2726,10 +2675,10 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2726
2675
|
// Update dynamic component meta in blocklet settings
|
|
2727
2676
|
await this._ensureDynamicChildrenInSettings(blocklet);
|
|
2728
2677
|
|
|
2729
|
-
if (context?.
|
|
2678
|
+
if (context?.downloadTokenList?.length) {
|
|
2730
2679
|
await states.blocklet.updateBlocklet(did, {
|
|
2731
2680
|
tokens: {
|
|
2732
|
-
|
|
2681
|
+
downloadTokenList: context.downloadTokenList,
|
|
2733
2682
|
},
|
|
2734
2683
|
});
|
|
2735
2684
|
}
|
|
@@ -2968,7 +2917,13 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2968
2917
|
}
|
|
2969
2918
|
ctrlStore[rootDid].set(did, cancelCtrl);
|
|
2970
2919
|
|
|
2971
|
-
|
|
2920
|
+
const headers = context.headers ? cloneDeep(context.headers) : {};
|
|
2921
|
+
const exist = (context.downloadTokenList || []).find((x) => x.did === did);
|
|
2922
|
+
if (exist) {
|
|
2923
|
+
headers['x-download-token'] = exist.token;
|
|
2924
|
+
}
|
|
2925
|
+
|
|
2926
|
+
await downloadFile(url, path.join(cwd, tarballName), { cancelCtrl }, { ...context, headers });
|
|
2972
2927
|
|
|
2973
2928
|
if (ctrlStore[rootDid]) {
|
|
2974
2929
|
ctrlStore[rootDid].delete(did);
|
package/lib/index.js
CHANGED
|
@@ -163,7 +163,7 @@ function ABTNode(options) {
|
|
|
163
163
|
} = getRouterHelpers({ dataDirs, routingSnapshot, routerManager, blockletManager, certManager });
|
|
164
164
|
|
|
165
165
|
const nodeAPI = new NodeAPI(states.node);
|
|
166
|
-
const teamAPI = new TeamAPI({ states, teamManager });
|
|
166
|
+
const teamAPI = new TeamAPI({ states, teamManager, dataDirs });
|
|
167
167
|
|
|
168
168
|
blockletManager.getRoutingRulesByDid = getRoutingRulesByDid;
|
|
169
169
|
blockletManager.getSiteByDid = getSiteByDid;
|
|
@@ -298,6 +298,8 @@ function ABTNode(options) {
|
|
|
298
298
|
// Passport
|
|
299
299
|
revokeUserPassport: teamAPI.revokeUserPassport.bind(teamAPI),
|
|
300
300
|
enableUserPassport: teamAPI.enableUserPassport.bind(teamAPI),
|
|
301
|
+
issuePassportToUser: teamAPI.issuePassportToUser.bind(teamAPI),
|
|
302
|
+
|
|
301
303
|
createPassportIssuance: teamAPI.createPassportIssuance.bind(teamAPI),
|
|
302
304
|
getPassportIssuances: teamAPI.getPassportIssuances.bind(teamAPI),
|
|
303
305
|
getPassportIssuance: teamAPI.getPassportIssuance.bind(teamAPI),
|
package/lib/states/audit-log.js
CHANGED
|
@@ -3,7 +3,7 @@ const pick = require('lodash/pick');
|
|
|
3
3
|
const get = require('lodash/get');
|
|
4
4
|
const joinUrl = require('url-join');
|
|
5
5
|
const { getDisplayName } = require('@blocklet/meta/lib/util');
|
|
6
|
-
const { BLOCKLET_SITE_GROUP_SUFFIX } = require('@abtnode/constant');
|
|
6
|
+
const { BLOCKLET_SITE_GROUP_SUFFIX, NODE_SERVICES } = require('@abtnode/constant');
|
|
7
7
|
const logger = require('@abtnode/logger')('@abtnode/core:states:audit-log');
|
|
8
8
|
|
|
9
9
|
const BaseState = require('./base');
|
|
@@ -177,6 +177,8 @@ const getLogContent = async (action, args, context, result, info, node) => {
|
|
|
177
177
|
return `updated trusted passport issuers to following for ${team}: \n${args.trustedPassports.map(x => `- ${x.remark}: ${x.issuerDid}`).join('\n')}`; // prettier-ignore
|
|
178
178
|
case 'delegateTransferNFT':
|
|
179
179
|
return `${args.owner} ${args.reason}`;
|
|
180
|
+
case 'issuePassportToUser':
|
|
181
|
+
return `issued **${args.role}** passport to ${user}`;
|
|
180
182
|
|
|
181
183
|
// accessKeys
|
|
182
184
|
case 'createAccessKey':
|
|
@@ -344,6 +346,12 @@ const getScope = (args = {}) => {
|
|
|
344
346
|
return null;
|
|
345
347
|
};
|
|
346
348
|
|
|
349
|
+
const fixActor = (actor) => {
|
|
350
|
+
if ([NODE_SERVICES.AUTH_SERVICE, 'blocklet'].includes(actor?.role)) {
|
|
351
|
+
actor.role = '';
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
|
|
347
355
|
class AuditLogState extends BaseState {
|
|
348
356
|
constructor(baseDir, options = {}) {
|
|
349
357
|
super(baseDir, { filename: 'audit-log.db', ...options });
|
|
@@ -399,6 +407,8 @@ class AuditLogState extends BaseState {
|
|
|
399
407
|
const { ip, ua, user } = context;
|
|
400
408
|
const [info, uaInfo] = await Promise.all([node.states.node.read(), parse(ua)]);
|
|
401
409
|
|
|
410
|
+
fixActor(user);
|
|
411
|
+
|
|
402
412
|
const data = await this.asyncDB.insert({
|
|
403
413
|
scope: getScope(args) || info.did, // server or blocklet did
|
|
404
414
|
action,
|
package/lib/states/blocklet.js
CHANGED
|
@@ -39,7 +39,10 @@ const formatBlocklet = (blocklet, phase, dek) => {
|
|
|
39
39
|
fixInterfaces(b.meta);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
if (phase === 'onRead') {
|
|
43
|
+
b.children = b.children || [];
|
|
44
|
+
b.environments = b.environments || [];
|
|
45
|
+
}
|
|
43
46
|
|
|
44
47
|
if (!b.environments || !b.meta || !dek) {
|
|
45
48
|
return;
|
package/lib/util/blocklet.js
CHANGED
|
@@ -9,10 +9,9 @@ const streamToPromise = require('stream-to-promise');
|
|
|
9
9
|
const { Throttle } = require('stream-throttle');
|
|
10
10
|
const ssri = require('ssri');
|
|
11
11
|
const diff = require('deep-diff');
|
|
12
|
-
const any = require('promise.any');
|
|
13
|
-
const joinUrl = require('url-join');
|
|
14
12
|
|
|
15
13
|
const { toHex } = require('@ocap/util');
|
|
14
|
+
const { isValid: isValidDid } = require('@arcblock/did');
|
|
16
15
|
const logger = require('@abtnode/logger')('@abtnode/core:util:blocklet');
|
|
17
16
|
const pm2 = require('@abtnode/util/lib/async-pm2');
|
|
18
17
|
const sleep = require('@abtnode/util/lib/sleep');
|
|
@@ -22,7 +21,7 @@ const getFolderSize = require('@abtnode/util/lib/get-folder-size');
|
|
|
22
21
|
const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
|
|
23
22
|
const hashFiles = require('@abtnode/util/lib/hash-files');
|
|
24
23
|
const isPathPrefixEqual = require('@abtnode/util/lib/is-path-prefix-equal');
|
|
25
|
-
const { BLOCKLET_MAX_MEM_LIMIT_IN_MB
|
|
24
|
+
const { BLOCKLET_MAX_MEM_LIMIT_IN_MB } = require('@abtnode/constant');
|
|
26
25
|
|
|
27
26
|
const {
|
|
28
27
|
BlockletStatus,
|
|
@@ -55,19 +54,19 @@ const {
|
|
|
55
54
|
} = require('@blocklet/meta/lib/util');
|
|
56
55
|
const toBlockletDid = require('@blocklet/meta/lib/did');
|
|
57
56
|
const { titleSchema, descriptionSchema } = require('@blocklet/meta/lib/schema');
|
|
57
|
+
const {
|
|
58
|
+
getSourceUrlsFromConfig,
|
|
59
|
+
getBlockletMetaFromUrls,
|
|
60
|
+
getBlockletMetaFromUrl,
|
|
61
|
+
} = require('@blocklet/meta/lib/util-meta');
|
|
62
|
+
const getComponentProcessId = require('@blocklet/meta/lib/get-component-process-id');
|
|
58
63
|
|
|
59
64
|
const { validate: validateEngine, get: getEngine } = require('../blocklet/manager/engine');
|
|
60
65
|
|
|
61
66
|
const isRequirementsSatisfied = require('./requirement');
|
|
62
67
|
const { getDidDomainForBlocklet } = require('./get-domain-for-blocklet');
|
|
63
|
-
const {
|
|
64
|
-
|
|
65
|
-
expandBundle,
|
|
66
|
-
getBlockletMetaByUrl,
|
|
67
|
-
validateUrl,
|
|
68
|
-
findInterfacePortByName,
|
|
69
|
-
validateBlockletMeta,
|
|
70
|
-
} = require('./index');
|
|
68
|
+
const { getBlockletDomainGroupName } = require('./router');
|
|
69
|
+
const { isBeforeInstalled, expandBundle, findInterfacePortByName, validateBlockletMeta } = require('./index');
|
|
71
70
|
|
|
72
71
|
/**
|
|
73
72
|
* get blocklet engine info, default is node
|
|
@@ -212,7 +211,8 @@ const fillBlockletConfigs = (blocklet, configs) => {
|
|
|
212
211
|
acc[x.key] = x.value;
|
|
213
212
|
return acc;
|
|
214
213
|
}, {});
|
|
215
|
-
blocklet.
|
|
214
|
+
blocklet.environments = blocklet.environments || [];
|
|
215
|
+
blocklet.environmentObj = blocklet.environments.reduce((acc, x) => {
|
|
216
216
|
acc[x.key] = x.value;
|
|
217
217
|
return acc;
|
|
218
218
|
}, {});
|
|
@@ -406,45 +406,6 @@ const getHealthyCheckTimeout = (blocklet, { checkHealthImmediately } = {}) => {
|
|
|
406
406
|
};
|
|
407
407
|
};
|
|
408
408
|
|
|
409
|
-
const getBlockletMetaFromUrl = async (url) => {
|
|
410
|
-
const meta = await getBlockletMetaByUrl(url);
|
|
411
|
-
delete meta.htmlAst;
|
|
412
|
-
|
|
413
|
-
validateBlockletMeta(meta, { ensureDist: true });
|
|
414
|
-
|
|
415
|
-
try {
|
|
416
|
-
const { href } = new URL(meta.dist.tarball, url);
|
|
417
|
-
const tarball = decodeURIComponent(href);
|
|
418
|
-
|
|
419
|
-
try {
|
|
420
|
-
await validateUrl(tarball, ['application/octet-stream', 'application/x-gzip']);
|
|
421
|
-
} catch (error) {
|
|
422
|
-
if (!error.message.startsWith('Cannot get content-type')) {
|
|
423
|
-
throw error;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
logger.info('resolve tarball url base on meta url', { meta: url, tarball });
|
|
427
|
-
|
|
428
|
-
meta.dist.tarball = tarball;
|
|
429
|
-
} catch (err) {
|
|
430
|
-
const msg = `Invalid blocklet meta: dist.tarball is not a valid url ${err.message}`;
|
|
431
|
-
logger.error(msg);
|
|
432
|
-
throw new Error(msg);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
return meta;
|
|
436
|
-
};
|
|
437
|
-
|
|
438
|
-
const getBlockletMetaFromUrls = async (urls) => {
|
|
439
|
-
try {
|
|
440
|
-
const meta = await any(urls.map(getBlockletMetaFromUrl));
|
|
441
|
-
return meta;
|
|
442
|
-
} catch (err) {
|
|
443
|
-
logger.error('failed get blocklet meta', { urls, error: err });
|
|
444
|
-
throw new Error('Failed get blocklet meta');
|
|
445
|
-
}
|
|
446
|
-
};
|
|
447
|
-
|
|
448
409
|
/**
|
|
449
410
|
* Start all precesses of a blocklet
|
|
450
411
|
* @param {*} blocklet should contain env props
|
|
@@ -695,36 +656,6 @@ const parseChildren = (children, parentMeta = {}, { dynamic } = {}) => {
|
|
|
695
656
|
return children;
|
|
696
657
|
};
|
|
697
658
|
|
|
698
|
-
/**
|
|
699
|
-
* @param {*} config defined in childrenSchema in blocklet meta schema
|
|
700
|
-
*/
|
|
701
|
-
const getSourceUrlsFromConfig = (config) => {
|
|
702
|
-
if (config.source) {
|
|
703
|
-
if (config.source.url) {
|
|
704
|
-
return [config.source.url].flat();
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
const { store, version, name } = config.source;
|
|
708
|
-
return [store]
|
|
709
|
-
.flat()
|
|
710
|
-
.map((x) =>
|
|
711
|
-
joinUrl(
|
|
712
|
-
x,
|
|
713
|
-
BLOCKLET_STORE_API_BLOCKLET_PREFIX,
|
|
714
|
-
toBlockletDid(name),
|
|
715
|
-
!version || version === 'latest' ? '' : version,
|
|
716
|
-
'blocklet.json'
|
|
717
|
-
)
|
|
718
|
-
);
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
if (config.resolved) {
|
|
722
|
-
return [config.resolved];
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
throw new Error('Invalid child config');
|
|
726
|
-
};
|
|
727
|
-
|
|
728
659
|
/**
|
|
729
660
|
* this function has side effect on children
|
|
730
661
|
*/
|
|
@@ -752,7 +683,7 @@ const parseChildrenFromMeta = async (src, context = {}) => {
|
|
|
752
683
|
|
|
753
684
|
let m;
|
|
754
685
|
try {
|
|
755
|
-
m = await getBlockletMetaFromUrls(urls);
|
|
686
|
+
m = await getBlockletMetaFromUrls(urls, { logger });
|
|
756
687
|
} catch {
|
|
757
688
|
throw new Error(`Failed get component meta: ${config.title || config.name}`);
|
|
758
689
|
}
|
|
@@ -1296,9 +1227,75 @@ const ensureMeta = (meta, { name, did } = {}) => {
|
|
|
1296
1227
|
return newMeta;
|
|
1297
1228
|
};
|
|
1298
1229
|
|
|
1230
|
+
const getBlocklet = async ({
|
|
1231
|
+
did,
|
|
1232
|
+
dataDirs,
|
|
1233
|
+
states,
|
|
1234
|
+
e2eMode = false,
|
|
1235
|
+
validateEnv = true,
|
|
1236
|
+
throwOnNotExist = true,
|
|
1237
|
+
ensureDirs = true,
|
|
1238
|
+
} = {}) => {
|
|
1239
|
+
if (!did) {
|
|
1240
|
+
throw new Error('Blocklet did does not exist');
|
|
1241
|
+
}
|
|
1242
|
+
if (!isValidDid(did)) {
|
|
1243
|
+
throw new Error(`Blocklet did is invalid: ${did}`);
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
if (!dataDirs) {
|
|
1247
|
+
throw new Error('dataDirs does not exist');
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
if (!states) {
|
|
1251
|
+
throw new Error('states does not exist');
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
const blocklet = await states.blocklet.getBlocklet(did);
|
|
1255
|
+
if (!blocklet) {
|
|
1256
|
+
if (throwOnNotExist) {
|
|
1257
|
+
throw new Error(`can not find blocklet in database by did ${did}`);
|
|
1258
|
+
}
|
|
1259
|
+
return null;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
// app settings
|
|
1263
|
+
const settings = await states.blockletExtras.getSettings(blocklet.meta.did);
|
|
1264
|
+
blocklet.trustedPassports = get(settings, 'trustedPassports') || [];
|
|
1265
|
+
blocklet.enablePassportIssuance = get(settings, 'enablePassportIssuance', true);
|
|
1266
|
+
blocklet.settings = settings || {};
|
|
1267
|
+
|
|
1268
|
+
// app site
|
|
1269
|
+
const sites = await states.site.getSites();
|
|
1270
|
+
const domain = getBlockletDomainGroupName(blocklet.meta.did);
|
|
1271
|
+
blocklet.site = (sites || []).find((x) => x.domain === domain);
|
|
1272
|
+
|
|
1273
|
+
await forEachBlocklet(blocklet, async (component, { id, level, ancestors }) => {
|
|
1274
|
+
// component env
|
|
1275
|
+
component.env = {
|
|
1276
|
+
id,
|
|
1277
|
+
name: getComponentName(component, ancestors),
|
|
1278
|
+
processId: getComponentProcessId(component, ancestors),
|
|
1279
|
+
...getComponentDirs(component, {
|
|
1280
|
+
dataDirs,
|
|
1281
|
+
ensure: ensureDirs,
|
|
1282
|
+
validate: validateEnv,
|
|
1283
|
+
ancestors,
|
|
1284
|
+
e2eMode: level === 0 ? e2eMode : false,
|
|
1285
|
+
}),
|
|
1286
|
+
};
|
|
1287
|
+
|
|
1288
|
+
// component config
|
|
1289
|
+
const configs = await states.blockletExtras.getConfigs([...ancestors.map((x) => x.meta.did), component.meta.did]);
|
|
1290
|
+
fillBlockletConfigs(component, configs);
|
|
1291
|
+
});
|
|
1292
|
+
|
|
1293
|
+
return blocklet;
|
|
1294
|
+
};
|
|
1295
|
+
|
|
1299
1296
|
module.exports = {
|
|
1300
1297
|
forEachBlocklet,
|
|
1301
|
-
getBlockletMetaFromUrl,
|
|
1298
|
+
getBlockletMetaFromUrl: (url) => getBlockletMetaFromUrl(url, { logger }),
|
|
1302
1299
|
parseChildrenFromMeta,
|
|
1303
1300
|
parseChildren,
|
|
1304
1301
|
getComponentDirs,
|
|
@@ -1334,5 +1331,5 @@ module.exports = {
|
|
|
1334
1331
|
needBlockletDownload,
|
|
1335
1332
|
findAvailableDid,
|
|
1336
1333
|
ensureMeta,
|
|
1337
|
-
|
|
1334
|
+
getBlocklet,
|
|
1338
1335
|
};
|
package/lib/util/index.js
CHANGED
|
@@ -36,8 +36,6 @@ const DEFAULT_WELLKNOWN_PORT = 8088;
|
|
|
36
36
|
|
|
37
37
|
const logger = require('@abtnode/logger')('@abtnode/core:util');
|
|
38
38
|
|
|
39
|
-
const request = require('./request');
|
|
40
|
-
|
|
41
39
|
const validateOwner = (owner) => {
|
|
42
40
|
try {
|
|
43
41
|
return owner && owner.did && owner.pk && isFromPublicKey(owner.did, owner.pk);
|
|
@@ -351,66 +349,6 @@ const getBaseUrls = async (node, ips) => {
|
|
|
351
349
|
}));
|
|
352
350
|
};
|
|
353
351
|
|
|
354
|
-
const getBlockletMetaByUrl = async (url) => {
|
|
355
|
-
const { protocol, pathname } = new URL(url);
|
|
356
|
-
|
|
357
|
-
if (protocol.startsWith('file')) {
|
|
358
|
-
const decoded = decodeURIComponent(pathname);
|
|
359
|
-
if (!fs.existsSync(decoded)) {
|
|
360
|
-
throw new Error(`File does not exist: ${decoded}`);
|
|
361
|
-
}
|
|
362
|
-
const d = await fs.promises.readFile(decoded);
|
|
363
|
-
const meta = JSON.parse(d);
|
|
364
|
-
return meta;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
if (protocol.startsWith('http')) {
|
|
368
|
-
const { data: meta } = await request({ url, method: 'GET', timeout: 1000 * 20 });
|
|
369
|
-
if (Object.prototype.toString.call(meta) !== '[object Object]') {
|
|
370
|
-
throw new Error('Url is not valid');
|
|
371
|
-
}
|
|
372
|
-
return meta;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
throw new Error(`Invalid url protocol: ${protocol.replace(/:$/, '')}`);
|
|
376
|
-
};
|
|
377
|
-
|
|
378
|
-
const validateUrl = async (url, expectedHttpResTypes = ['application/json', 'text/plain']) => {
|
|
379
|
-
const parsed = new URL(url);
|
|
380
|
-
const { protocol, pathname } = parsed;
|
|
381
|
-
|
|
382
|
-
// file
|
|
383
|
-
if (protocol.startsWith('file')) {
|
|
384
|
-
const decoded = decodeURIComponent(pathname);
|
|
385
|
-
if (!fs.existsSync(decoded)) {
|
|
386
|
-
throw new Error(`File does not exist: ${decoded}`);
|
|
387
|
-
}
|
|
388
|
-
return true;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// http(s)
|
|
392
|
-
if (protocol.startsWith('http')) {
|
|
393
|
-
let res;
|
|
394
|
-
|
|
395
|
-
try {
|
|
396
|
-
res = await request({ url, method: 'HEAD', timeout: 1000 * 10 });
|
|
397
|
-
} catch (err) {
|
|
398
|
-
throw new Error(`Cannot get content-type from ${url}: ${err.message}`);
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
if (
|
|
402
|
-
res.headers['content-type'] &&
|
|
403
|
-
expectedHttpResTypes.some((x) => res.headers['content-type'].includes(x)) === false
|
|
404
|
-
) {
|
|
405
|
-
throw new Error(`Unexpected content-type from ${url}: ${res.headers['content-type']}`);
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
return true;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
throw new Error(`Invalid url protocol: ${protocol.replace(/:$/, '')}`);
|
|
412
|
-
};
|
|
413
|
-
|
|
414
352
|
const expandBundle = (bundlePath, destDir) =>
|
|
415
353
|
unzipper.Open.file(bundlePath).then((d) => d.extract({ path: destDir, concurrency: 20 }));
|
|
416
354
|
|
|
@@ -567,8 +505,6 @@ const lib = {
|
|
|
567
505
|
fixAndValidateService(meta);
|
|
568
506
|
return validateMeta(meta, opts);
|
|
569
507
|
},
|
|
570
|
-
getBlockletMetaByUrl,
|
|
571
|
-
validateUrl,
|
|
572
508
|
expandBundle,
|
|
573
509
|
findInterfaceByName,
|
|
574
510
|
findInterfacePortByName,
|
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.16",
|
|
7
7
|
"description": "",
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"files": [
|
|
@@ -19,32 +19,33 @@
|
|
|
19
19
|
"author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@abtnode/
|
|
23
|
-
"@abtnode/
|
|
24
|
-
"@abtnode/
|
|
25
|
-
"@abtnode/
|
|
26
|
-
"@abtnode/
|
|
27
|
-
"@abtnode/
|
|
28
|
-
"@abtnode/
|
|
29
|
-
"@abtnode/
|
|
30
|
-
"@abtnode/
|
|
31
|
-
"@abtnode/
|
|
32
|
-
"@abtnode/
|
|
33
|
-
"@
|
|
22
|
+
"@abtnode/auth": "1.8.16",
|
|
23
|
+
"@abtnode/certificate-manager": "1.8.16",
|
|
24
|
+
"@abtnode/constant": "1.8.16",
|
|
25
|
+
"@abtnode/cron": "1.8.16",
|
|
26
|
+
"@abtnode/db": "1.8.16",
|
|
27
|
+
"@abtnode/logger": "1.8.16",
|
|
28
|
+
"@abtnode/queue": "1.8.16",
|
|
29
|
+
"@abtnode/rbac": "1.8.16",
|
|
30
|
+
"@abtnode/router-provider": "1.8.16",
|
|
31
|
+
"@abtnode/static-server": "1.8.16",
|
|
32
|
+
"@abtnode/timemachine": "1.8.16",
|
|
33
|
+
"@abtnode/util": "1.8.16",
|
|
34
|
+
"@arcblock/did": "1.17.17",
|
|
34
35
|
"@arcblock/did-motif": "^1.1.10",
|
|
35
|
-
"@arcblock/did-util": "1.17.
|
|
36
|
-
"@arcblock/event-hub": "1.17.
|
|
37
|
-
"@arcblock/jwt": "^1.17.
|
|
36
|
+
"@arcblock/did-util": "1.17.17",
|
|
37
|
+
"@arcblock/event-hub": "1.17.17",
|
|
38
|
+
"@arcblock/jwt": "^1.17.17",
|
|
38
39
|
"@arcblock/pm2-events": "^0.0.5",
|
|
39
|
-
"@arcblock/vc": "1.17.
|
|
40
|
-
"@blocklet/meta": "1.8.
|
|
41
|
-
"@blocklet/sdk": "1.8.
|
|
40
|
+
"@arcblock/vc": "1.17.17",
|
|
41
|
+
"@blocklet/meta": "1.8.16",
|
|
42
|
+
"@blocklet/sdk": "1.8.16",
|
|
42
43
|
"@fidm/x509": "^1.2.1",
|
|
43
44
|
"@nedb/core": "^1.3.4",
|
|
44
45
|
"@nedb/multi": "^1.3.4",
|
|
45
|
-
"@ocap/mcrypto": "1.17.
|
|
46
|
-
"@ocap/util": "1.17.
|
|
47
|
-
"@ocap/wallet": "1.17.
|
|
46
|
+
"@ocap/mcrypto": "1.17.17",
|
|
47
|
+
"@ocap/util": "1.17.17",
|
|
48
|
+
"@ocap/wallet": "1.17.17",
|
|
48
49
|
"@slack/webhook": "^5.0.4",
|
|
49
50
|
"axios": "^0.27.2",
|
|
50
51
|
"axon": "^2.0.3",
|
|
@@ -62,7 +63,6 @@
|
|
|
62
63
|
"lodash": "^4.17.21",
|
|
63
64
|
"lru-cache": "^6.0.0",
|
|
64
65
|
"pm2": "^5.2.0",
|
|
65
|
-
"promise.any": "^2.0.4",
|
|
66
66
|
"semver": "^7.3.7",
|
|
67
67
|
"shelljs": "^0.8.5",
|
|
68
68
|
"slugify": "^1.6.5",
|
|
@@ -82,5 +82,5 @@
|
|
|
82
82
|
"express": "^4.18.1",
|
|
83
83
|
"jest": "^27.5.1"
|
|
84
84
|
},
|
|
85
|
-
"gitHead": "
|
|
85
|
+
"gitHead": "2605cf6ebf2a0c3bb5524cb0f1385ad565fd85d6"
|
|
86
86
|
}
|