@abtnode/core 1.16.11-next-069c3537 → 1.16.11-next-a232f5fb
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/node.js +1 -1
- package/lib/blocklet/manager/disk.js +16 -8
- package/lib/blocklet/manager/helper/install-application-from-general.js +18 -3
- package/lib/index.js +7 -1
- package/lib/states/blocklet-extras.js +10 -0
- package/lib/util/blocklet.js +2 -53
- package/lib/util/launcher.js +237 -0
- package/lib/validators/util.js +3 -0
- package/package.json +18 -18
package/lib/api/node.js
CHANGED
|
@@ -66,6 +66,7 @@ const {
|
|
|
66
66
|
RESTORE_PROGRESS_STATUS,
|
|
67
67
|
CHAIN_PROP_MAP_REVERSE,
|
|
68
68
|
} = require('@blocklet/constant');
|
|
69
|
+
const { consumeServerlessNFT, consumeLauncherSession } = require('../../util/launcher');
|
|
69
70
|
const util = require('../../util');
|
|
70
71
|
const {
|
|
71
72
|
refresh: refreshAccessibleExternalNodeIp,
|
|
@@ -94,7 +95,6 @@ const {
|
|
|
94
95
|
getBlocklet,
|
|
95
96
|
ensureEnvDefault,
|
|
96
97
|
getConfigFromPreferences,
|
|
97
|
-
consumeServerlessNFT,
|
|
98
98
|
validateAppConfig,
|
|
99
99
|
checkDuplicateMountPoint,
|
|
100
100
|
validateStore,
|
|
@@ -2308,14 +2308,22 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2308
2308
|
});
|
|
2309
2309
|
}
|
|
2310
2310
|
|
|
2311
|
-
if (blocklet
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
})
|
|
2311
|
+
if (blocklet.controller && process.env.NODE_ENV !== 'test') {
|
|
2312
|
+
let isNFTConsumed = false;
|
|
2313
|
+
if (blocklet.controller.launcherSessionId && blocklet.controller.launcherUrl) {
|
|
2314
|
+
await consumeLauncherSession({ params: blocklet.controller, blocklet });
|
|
2315
|
+
isNFTConsumed = true;
|
|
2316
|
+
} else if (blocklet.controller.nftId) {
|
|
2317
|
+
await consumeServerlessNFT({ nftId: blocklet.controller.nftId, blocklet });
|
|
2318
|
+
isNFTConsumed = true;
|
|
2319
|
+
}
|
|
2317
2320
|
|
|
2318
|
-
|
|
2321
|
+
if (isNFTConsumed) {
|
|
2322
|
+
await states.blockletExtras.updateByDid(did, {
|
|
2323
|
+
controller: { ...blocklet.controller, consumedAt: new Date().toISOString() },
|
|
2324
|
+
});
|
|
2325
|
+
this.emit(BlockletEvents.nftConsumed, { blocklet, context });
|
|
2326
|
+
}
|
|
2319
2327
|
}
|
|
2320
2328
|
|
|
2321
2329
|
if (createNotification) {
|
|
@@ -54,9 +54,7 @@ const installApplicationFromGeneral = async ({
|
|
|
54
54
|
|
|
55
55
|
// check install count
|
|
56
56
|
if (controller?.nftId) {
|
|
57
|
-
// sometimes nedb will throw error if use states.blocklet.count({ ['controller.nftId']: controller.nftId })
|
|
58
57
|
const installedCount = await states.blockletExtras.count({ 'controller.nftId': controller.nftId });
|
|
59
|
-
|
|
60
58
|
if (installedCount >= (controller.appMaxCount || 1)) {
|
|
61
59
|
throw new Error(
|
|
62
60
|
`You can only install ${controller.appMaxCount} blocklet with this credential: ${controller.nftId}`
|
|
@@ -120,7 +118,24 @@ const installApplicationFromGeneral = async ({
|
|
|
120
118
|
},
|
|
121
119
|
]);
|
|
122
120
|
} catch (err) {
|
|
123
|
-
|
|
121
|
+
const componentDid = component?.meta?.did;
|
|
122
|
+
logger.error('failed to create blocklet extras', { did, componentDid, error: err });
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
await manager._rollback('install', did, {});
|
|
126
|
+
} catch (e) {
|
|
127
|
+
logger.error('failed to remove blocklet on create extras error', { did, componentDid, error: e });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
manager._createNotification(did, {
|
|
131
|
+
title: 'Blocklet Install Failed',
|
|
132
|
+
description: `Blocklet ${title || component?.meta?.title} install failed with error: ${err.message}`,
|
|
133
|
+
entityType: 'blocklet',
|
|
134
|
+
entityId: did,
|
|
135
|
+
severity: 'error',
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
throw err;
|
|
124
139
|
}
|
|
125
140
|
|
|
126
141
|
if (!blocklet.children.length) {
|
package/lib/index.js
CHANGED
|
@@ -16,6 +16,7 @@ const getRouterHelpers = require('./router/helper');
|
|
|
16
16
|
const TeamManager = require('./team/manager');
|
|
17
17
|
const NodeAPI = require('./api/node');
|
|
18
18
|
const TeamAPI = require('./api/team');
|
|
19
|
+
const { getLauncherSession, isLauncherSessionConsumed, setupAppOwner } = require('./util/launcher');
|
|
19
20
|
const WebHook = require('./webhook');
|
|
20
21
|
const states = require('./states');
|
|
21
22
|
const Cert = require('./cert');
|
|
@@ -250,7 +251,6 @@ function ABTNode(options) {
|
|
|
250
251
|
devBlocklet: blockletManager.dev.bind(blockletManager),
|
|
251
252
|
checkComponentsForUpdates: blockletManager.checkComponentsForUpdates.bind(blockletManager),
|
|
252
253
|
upgradeComponents: blockletManager.upgradeComponents.bind(blockletManager),
|
|
253
|
-
getBlockletMetaFromUrl: getMetaFromUrl.bind(blockletManager),
|
|
254
254
|
getBlockletForLauncher: blockletManager.getBlockletForLauncher.bind(blockletManager),
|
|
255
255
|
resetBlocklet: blockletManager.reset.bind(blockletManager),
|
|
256
256
|
deleteBlockletProcess: blockletManager.deleteProcess.bind(blockletManager),
|
|
@@ -462,6 +462,12 @@ function ABTNode(options) {
|
|
|
462
462
|
|
|
463
463
|
// for exporting blocklet data dir
|
|
464
464
|
createBlockletDataArchive: createDataArchive,
|
|
465
|
+
|
|
466
|
+
// public utils used in launch workflow
|
|
467
|
+
getBlockletMetaFromUrl: getMetaFromUrl,
|
|
468
|
+
getLauncherSession,
|
|
469
|
+
isLauncherSessionConsumed,
|
|
470
|
+
setupAppOwner,
|
|
465
471
|
};
|
|
466
472
|
|
|
467
473
|
const events = createEvents({
|
|
@@ -226,6 +226,16 @@ class BlockletExtrasState extends BaseState {
|
|
|
226
226
|
return docs.filter((x) => x.controller?.nftId).filter((x) => x.expiredAt <= deadline);
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
+
async isLauncherSessionConsumed(sessionId) {
|
|
230
|
+
const count = await super.count({ 'controller.sessionId': sessionId });
|
|
231
|
+
return count > 0;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
async isLauncherNftConsumed(nftId) {
|
|
235
|
+
const count = await super.count({ 'controller.nftId': nftId });
|
|
236
|
+
return count > 0;
|
|
237
|
+
}
|
|
238
|
+
|
|
229
239
|
encryptSecurityData({ data, _rootDid } = {}) {
|
|
230
240
|
if (!data) {
|
|
231
241
|
return data;
|
package/lib/util/blocklet.js
CHANGED
|
@@ -3,10 +3,8 @@
|
|
|
3
3
|
const fs = require('fs-extra');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const dayjs = require('dayjs');
|
|
6
|
-
const joinURL = require('url-join');
|
|
7
6
|
const shelljs = require('shelljs');
|
|
8
7
|
const os = require('os');
|
|
9
|
-
const pRetry = require('p-retry');
|
|
10
8
|
const tar = require('tar');
|
|
11
9
|
const get = require('lodash/get');
|
|
12
10
|
const uniq = require('lodash/uniq');
|
|
@@ -19,19 +17,16 @@ const diff = require('deep-diff');
|
|
|
19
17
|
const createArchive = require('archiver');
|
|
20
18
|
const isUrl = require('is-url');
|
|
21
19
|
const semver = require('semver');
|
|
22
|
-
const axios = require('@abtnode/util/lib/axios');
|
|
23
|
-
const { stableStringify } = require('@arcblock/vc');
|
|
24
20
|
const { chainInfo: chainInfoSchema } = require('@arcblock/did-auth/lib/schema');
|
|
25
21
|
|
|
26
22
|
const { fromSecretKey } = require('@ocap/wallet');
|
|
27
|
-
const { toHex,
|
|
23
|
+
const { toHex, isHex, toDid, toAddress } = require('@ocap/util');
|
|
28
24
|
const { isValid: isValidDid, isEthereumDid } = require('@arcblock/did');
|
|
29
25
|
const logger = require('@abtnode/logger')('@abtnode/core:util:blocklet');
|
|
30
26
|
const pm2 = require('@abtnode/util/lib/async-pm2');
|
|
31
27
|
const sleep = require('@abtnode/util/lib/sleep');
|
|
32
28
|
const getPm2ProcessInfo = require('@abtnode/util/lib/get-pm2-process-info');
|
|
33
29
|
const killProcessOccupiedPorts = require('@abtnode/util/lib/kill-process-occupied-ports');
|
|
34
|
-
const getNodeWallet = require('@abtnode/util/lib/get-app-wallet');
|
|
35
30
|
const { formatEnv } = require('@abtnode/util/lib/security');
|
|
36
31
|
const ensureEndpointHealthy = require('@abtnode/util/lib/ensure-endpoint-healthy');
|
|
37
32
|
const CustomError = require('@abtnode/util/lib/custom-error');
|
|
@@ -100,7 +95,7 @@ const { validate: validateEngine, get: getEngine } = require('../blocklet/manage
|
|
|
100
95
|
|
|
101
96
|
const isRequirementsSatisfied = require('./requirement');
|
|
102
97
|
const { getDidDomainForBlocklet } = require('./get-domain-for-blocklet');
|
|
103
|
-
const { expandBundle, findInterfacePortByName, prettyURL,
|
|
98
|
+
const { expandBundle, findInterfacePortByName, prettyURL, templateReplace } = require('./index');
|
|
104
99
|
const externalIpUtil = require('./get-accessible-external-node-ip');
|
|
105
100
|
const { getIpDnsDomainForBlocklet } = require('./get-domain-for-blocklet');
|
|
106
101
|
const { replaceDomainSlot } = require('./index');
|
|
@@ -1538,51 +1533,6 @@ const getBlockletURLForLauncher = async ({ blocklet, nodeInfo }) => {
|
|
|
1538
1533
|
const didDomain = getDidDomainForBlocklet({ appPid: blocklet.appPid, didDomain: nodeInfo.didDomain });
|
|
1539
1534
|
return `https://${didDomain}`;
|
|
1540
1535
|
};
|
|
1541
|
-
|
|
1542
|
-
const consumeServerlessNFT = async ({ nftId, nodeInfo, blocklet }) => {
|
|
1543
|
-
try {
|
|
1544
|
-
const state = await getNFTState(blocklet.controller.chainHost, nftId);
|
|
1545
|
-
if (!state) {
|
|
1546
|
-
throw new Error(`get nft state failed, chainHost: ${blocklet.controller.chainHost}, nftId: ${nftId}`);
|
|
1547
|
-
}
|
|
1548
|
-
|
|
1549
|
-
const appURL = await getBlockletURLForLauncher({ blocklet, nodeInfo });
|
|
1550
|
-
|
|
1551
|
-
logger.info('app url when consuming nft', { appURL, nftId });
|
|
1552
|
-
|
|
1553
|
-
const body = { nftId, appURL };
|
|
1554
|
-
|
|
1555
|
-
const { launcherUrl } = state.data.value;
|
|
1556
|
-
|
|
1557
|
-
const wallet = getNodeWallet(nodeInfo.sk);
|
|
1558
|
-
const func = async () => {
|
|
1559
|
-
const { data } = await axios.post(joinURL(launcherUrl, '/api/serverless/consume'), body, {
|
|
1560
|
-
headers: {
|
|
1561
|
-
'x-sig': toBase58(wallet.sign(stableStringify(body))),
|
|
1562
|
-
},
|
|
1563
|
-
});
|
|
1564
|
-
|
|
1565
|
-
return data;
|
|
1566
|
-
};
|
|
1567
|
-
|
|
1568
|
-
const delay = 10 * 1000;
|
|
1569
|
-
const result = await pRetry(func, {
|
|
1570
|
-
retries: 3,
|
|
1571
|
-
minTimeout: delay,
|
|
1572
|
-
maxTimeout: delay,
|
|
1573
|
-
onFailedAttempt: (error) => {
|
|
1574
|
-
logger.error(`attempt consume nft ${nftId} failed`, { error });
|
|
1575
|
-
},
|
|
1576
|
-
});
|
|
1577
|
-
|
|
1578
|
-
logger.info('consume serverless nft success', { nftId, hash: result.hash });
|
|
1579
|
-
} catch (error) {
|
|
1580
|
-
logger.error('consume serverless nft failed', { nftId, error });
|
|
1581
|
-
|
|
1582
|
-
throw new Error(`consume nft ${nftId} failed`);
|
|
1583
|
-
}
|
|
1584
|
-
};
|
|
1585
|
-
|
|
1586
1536
|
const createDataArchive = (dataDir, fileName) => {
|
|
1587
1537
|
const zipPath = path.join(os.tmpdir(), fileName);
|
|
1588
1538
|
if (fs.existsSync(zipPath)) {
|
|
@@ -1958,7 +1908,6 @@ const shouldCleanExpiredBlocklet = (expirationDate) => {
|
|
|
1958
1908
|
|
|
1959
1909
|
module.exports = {
|
|
1960
1910
|
updateBlockletFallbackLogo,
|
|
1961
|
-
consumeServerlessNFT,
|
|
1962
1911
|
forEachBlocklet,
|
|
1963
1912
|
getBlockletMetaFromUrl: (url) => getBlockletMetaFromUrl(url, { logger }),
|
|
1964
1913
|
parseComponents,
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const joinUrl = require('url-join');
|
|
4
|
+
const pick = require('lodash/pick');
|
|
5
|
+
const joinURL = require('url-join');
|
|
6
|
+
const { parseUserAvatar, extractUserAvatar, getAvatarFile, getAvatarByUrl } = require('@abtnode/util/lib/user');
|
|
7
|
+
const { Hasher } = require('@ocap/mcrypto');
|
|
8
|
+
const { getDisplayName } = require('@blocklet/meta/lib/util');
|
|
9
|
+
const getBlockletInfo = require('@blocklet/meta/lib/info');
|
|
10
|
+
|
|
11
|
+
const { getLauncherUser, getLauncherSession: getLauncherSessionRaw, doRequest } = require('@abtnode/auth/lib/launcher');
|
|
12
|
+
const { createAuthToken, getPassportStatusEndpoint } = require('@abtnode/auth/lib/auth');
|
|
13
|
+
const { createPassportVC, createPassport, createUserPassport } = require('@abtnode/auth/lib/passport');
|
|
14
|
+
|
|
15
|
+
const { BlockletStatus } = require('@blocklet/constant');
|
|
16
|
+
const {
|
|
17
|
+
WELLKNOWN_SERVICE_PATH_PREFIX,
|
|
18
|
+
NODE_DATA_DIR_NAME,
|
|
19
|
+
USER_AVATAR_URL_PREFIX,
|
|
20
|
+
ROLES,
|
|
21
|
+
} = require('@abtnode/constant');
|
|
22
|
+
|
|
23
|
+
const logger = require('@abtnode/logger')('@abtnode/core:util:launcher');
|
|
24
|
+
|
|
25
|
+
const { getNFTState } = require('./index');
|
|
26
|
+
const { getBlockletURLForLauncher } = require('./blocklet');
|
|
27
|
+
const states = require('../states');
|
|
28
|
+
|
|
29
|
+
const consumeServerlessNFT = async ({ nftId, blocklet }) => {
|
|
30
|
+
try {
|
|
31
|
+
const state = await getNFTState(blocklet.controller.chainHost, nftId);
|
|
32
|
+
if (!state) {
|
|
33
|
+
throw new Error(`Get nft state failed, chainHost: ${blocklet.controller.chainHost}, nftId: ${nftId}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const nodeInfo = await states.node.read();
|
|
37
|
+
const appURL = await getBlockletURLForLauncher({ blocklet, nodeInfo });
|
|
38
|
+
logger.info('Consuming serverless nft params', { appURL, nftId });
|
|
39
|
+
|
|
40
|
+
const { launcherUrl } = state.data.value;
|
|
41
|
+
const result = await doRequest(nodeInfo.sk, {
|
|
42
|
+
launcherUrl,
|
|
43
|
+
pathname: '/api/serverless/consume',
|
|
44
|
+
payload: { nftId, appURL },
|
|
45
|
+
method: 'post',
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
logger.info('Consume serverless nft success', { nftId, hash: result.hash });
|
|
49
|
+
} catch (error) {
|
|
50
|
+
logger.error('Consume serverless nft failed', { nftId, error });
|
|
51
|
+
throw new Error(`consume nft ${nftId} failed`);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const consumeLauncherSession = async ({ params, blocklet }) => {
|
|
56
|
+
try {
|
|
57
|
+
const info = await states.node.read();
|
|
58
|
+
const { appUrl, name } = getBlockletInfo(blocklet, info.sk);
|
|
59
|
+
const result = await doRequest(info.sk, {
|
|
60
|
+
launcherUrl: params.launcherUrl,
|
|
61
|
+
pathname: `/api/launches/${params.launcherSessionId}/installed`,
|
|
62
|
+
payload: { appDid: blocklet.appPid, appUrl, appName: name, ownerDid: params.ownerDid },
|
|
63
|
+
method: 'post',
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
logger.info('Consume launcher session success', { params, result });
|
|
67
|
+
} catch (error) {
|
|
68
|
+
logger.error('Consume launcher session failed', { params, error });
|
|
69
|
+
throw new Error(`Consume launcher session ${params.launcherSessionId} failed`);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const setupAppOwner = async (node, sessionId) => {
|
|
74
|
+
const session = await node.getSession({ id: sessionId });
|
|
75
|
+
if (!session) {
|
|
76
|
+
throw new Error(`Blocklet setup session not found in server: ${sessionId}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const { appDid, userDid, ownerDid, ownerPk, lastLoginIp, context, locale, launcherUrl, launcherSessionId } = session;
|
|
80
|
+
const [info, blocklet] = await Promise.all([states.node.read(), states.blocklet.getBlocklet(appDid)]);
|
|
81
|
+
if (!blocklet) {
|
|
82
|
+
throw new Error(`Blocklet not found in server: ${appDid}`);
|
|
83
|
+
}
|
|
84
|
+
if (blocklet.status !== BlockletStatus.installed) {
|
|
85
|
+
throw new Error(`Blocklet status not expected: ${appDid}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const { wallet } = getBlockletInfo(blocklet, info.sk);
|
|
89
|
+
const appUrl = blocklet.environments.find((x) => x.key === 'BLOCKLET_APP_URL').value;
|
|
90
|
+
const dataDir = blocklet.environments.find((x) => x.key === 'BLOCKLET_DATA_DIR').value;
|
|
91
|
+
const serverDataDir = path.join(node.dataDirs.data, NODE_DATA_DIR_NAME);
|
|
92
|
+
|
|
93
|
+
let user;
|
|
94
|
+
let appOwnerProfile;
|
|
95
|
+
// get user from launcher or server
|
|
96
|
+
if (launcherUrl && launcherSessionId) {
|
|
97
|
+
user = await getLauncherUser(info.sk, { launcherUrl, launcherSessionId });
|
|
98
|
+
if (!user) {
|
|
99
|
+
throw new Error(`Owner user not found from launcher: ${launcherUrl}`);
|
|
100
|
+
}
|
|
101
|
+
appOwnerProfile = pick(user, ['fullName', 'email', 'avatar']);
|
|
102
|
+
const avatarBase64 = await getAvatarByUrl(joinURL(launcherUrl, user.avatar));
|
|
103
|
+
appOwnerProfile.avatar = await extractUserAvatar(avatarBase64, { dataDir });
|
|
104
|
+
logger.info('Create owner from launcher for blocklet', { appDid, ownerDid, ownerPk, sessionId, appOwnerProfile });
|
|
105
|
+
} else {
|
|
106
|
+
user = await node.getUser({ teamDid: info.did, user: { did: userDid } });
|
|
107
|
+
if (!user) {
|
|
108
|
+
throw new Error(`Owner user not found in server: ${userDid}`);
|
|
109
|
+
}
|
|
110
|
+
appOwnerProfile = pick(user, ['fullName', 'email', 'avatar']);
|
|
111
|
+
if (user.avatar && user.avatar.startsWith(USER_AVATAR_URL_PREFIX)) {
|
|
112
|
+
const filename = user.avatar.split('/').pop();
|
|
113
|
+
const srcFile = getAvatarFile(serverDataDir, filename);
|
|
114
|
+
const destFile = getAvatarFile(dataDir, filename);
|
|
115
|
+
fs.mkdirpSync(path.dirname(destFile));
|
|
116
|
+
fs.copyFileSync(srcFile, destFile);
|
|
117
|
+
} else {
|
|
118
|
+
appOwnerProfile.avatar = extractUserAvatar(user.avatar, { dataDir });
|
|
119
|
+
}
|
|
120
|
+
logger.info('Create owner from session for blocklet', { appDid, ownerDid, ownerPk, sessionId, appOwnerProfile });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// create and send owner passport
|
|
124
|
+
const role = ROLES.OWNER;
|
|
125
|
+
const vc = createPassportVC({
|
|
126
|
+
issuerName: getDisplayName(blocklet),
|
|
127
|
+
issuerWallet: wallet,
|
|
128
|
+
ownerDid,
|
|
129
|
+
passport: await createPassport({
|
|
130
|
+
name: role,
|
|
131
|
+
node,
|
|
132
|
+
teamDid: appDid,
|
|
133
|
+
locale,
|
|
134
|
+
endpoint: appUrl,
|
|
135
|
+
}),
|
|
136
|
+
endpoint: getPassportStatusEndpoint({
|
|
137
|
+
baseUrl: joinUrl(appUrl, WELLKNOWN_SERVICE_PATH_PREFIX),
|
|
138
|
+
userDid: ownerDid,
|
|
139
|
+
teamDid: appDid,
|
|
140
|
+
}),
|
|
141
|
+
ownerProfile: { ...appOwnerProfile, avatar: parseUserAvatar(user.avatar, { dataDir: serverDataDir }) },
|
|
142
|
+
preferredColor: 'default',
|
|
143
|
+
expirationDate: undefined,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// create app owner passport
|
|
147
|
+
const passport = createUserPassport(vc, { role });
|
|
148
|
+
|
|
149
|
+
// add app owner to database
|
|
150
|
+
const doc = await node.loginUser({
|
|
151
|
+
teamDid: appDid,
|
|
152
|
+
user: {
|
|
153
|
+
...appOwnerProfile,
|
|
154
|
+
did: ownerDid,
|
|
155
|
+
pk: ownerPk,
|
|
156
|
+
passport,
|
|
157
|
+
locale,
|
|
158
|
+
lastLoginIp,
|
|
159
|
+
connectedAccount: {
|
|
160
|
+
provider: 'wallet',
|
|
161
|
+
did: ownerDid,
|
|
162
|
+
pk: ownerPk,
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
await node.createAuditLog(
|
|
167
|
+
{
|
|
168
|
+
action: 'addUser',
|
|
169
|
+
args: { teamDid: appDid, userDid: ownerDid, reason: 'launch blocklet' },
|
|
170
|
+
context: { ...context, user: doc },
|
|
171
|
+
result: doc,
|
|
172
|
+
},
|
|
173
|
+
node
|
|
174
|
+
);
|
|
175
|
+
logger.info('Create owner for blocklet', { appDid, ownerDid, sessionId });
|
|
176
|
+
|
|
177
|
+
// create owner login token that can be used to login on blocklet site
|
|
178
|
+
const appSecret = Hasher.SHA3.hash256(Buffer.concat([wallet.secretKey, wallet.address].map(Buffer.from)));
|
|
179
|
+
const setupToken = createAuthToken({
|
|
180
|
+
did: ownerDid,
|
|
181
|
+
passport,
|
|
182
|
+
role,
|
|
183
|
+
secret: appSecret,
|
|
184
|
+
expiresIn: '7d',
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
await node.endSession({ id: sessionId });
|
|
188
|
+
logger.info('Complete install for blocklet', { appDid, ownerDid, sessionId });
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
session,
|
|
192
|
+
blocklet,
|
|
193
|
+
setupToken,
|
|
194
|
+
passport: vc,
|
|
195
|
+
};
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const getLauncherSession = async ({ launcherUrl, launcherSessionId, external = true }) => {
|
|
199
|
+
const info = await states.node.read();
|
|
200
|
+
const result = await getLauncherSessionRaw(info.sk, { launcherUrl, launcherSessionId });
|
|
201
|
+
|
|
202
|
+
// strip sensitive data if call from external
|
|
203
|
+
if (external && result.launcherSession) {
|
|
204
|
+
result.launcherSession = pick(result.launcherSession, ['_id', 'status']);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return result;
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// Check local first, then remote, FIXME: @wangshijun should we check on chain nft?
|
|
211
|
+
const isLauncherSessionConsumed = async (params) => {
|
|
212
|
+
let consumed = await states.blockletExtras.isLauncherSessionConsumed(params.launcherSessionId);
|
|
213
|
+
logger.info('Launcher session consumed at local?', { params, consumed });
|
|
214
|
+
if (consumed) {
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const { error, launcherSession } = await getLauncherSession(params);
|
|
219
|
+
if (error) {
|
|
220
|
+
throw new Error(error);
|
|
221
|
+
}
|
|
222
|
+
if (!launcherSession) {
|
|
223
|
+
throw new Error('Launcher session not found');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
consumed = launcherSession.status > 40;
|
|
227
|
+
logger.info('Launcher session consumed at remote?', { params, consumed });
|
|
228
|
+
return consumed;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
module.exports = {
|
|
232
|
+
consumeServerlessNFT,
|
|
233
|
+
consumeLauncherSession,
|
|
234
|
+
setupAppOwner,
|
|
235
|
+
getLauncherSession,
|
|
236
|
+
isLauncherSessionConsumed,
|
|
237
|
+
};
|
package/lib/validators/util.js
CHANGED
|
@@ -15,6 +15,9 @@ const blockletController = Joi.object({
|
|
|
15
15
|
nftOwner: Joi.DID().required(),
|
|
16
16
|
chainHost: Joi.string().uri().required(),
|
|
17
17
|
appMaxCount: Joi.number().required().min(1),
|
|
18
|
+
launcherUrl: Joi.string().uri().optional(),
|
|
19
|
+
launcherSessionId: Joi.string().optional(),
|
|
20
|
+
ownerDid: Joi.DID().optional(),
|
|
18
21
|
}).options({ stripUnknown: true });
|
|
19
22
|
|
|
20
23
|
const createValidator = (schema) => (entity) => schema.validateAsync(entity);
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.16.11-next-
|
|
6
|
+
"version": "1.16.11-next-a232f5fb",
|
|
7
7
|
"description": "",
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"files": [
|
|
@@ -19,18 +19,18 @@
|
|
|
19
19
|
"author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
|
|
20
20
|
"license": "Apache-2.0",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@abtnode/auth": "1.16.11-next-
|
|
23
|
-
"@abtnode/certificate-manager": "1.16.11-next-
|
|
24
|
-
"@abtnode/constant": "1.16.11-next-
|
|
25
|
-
"@abtnode/cron": "1.16.11-next-
|
|
26
|
-
"@abtnode/logger": "1.16.11-next-
|
|
27
|
-
"@abtnode/models": "1.16.11-next-
|
|
28
|
-
"@abtnode/queue": "1.16.11-next-
|
|
29
|
-
"@abtnode/rbac": "1.16.11-next-
|
|
30
|
-
"@abtnode/router-provider": "1.16.11-next-
|
|
31
|
-
"@abtnode/static-server": "1.16.11-next-
|
|
32
|
-
"@abtnode/timemachine": "1.16.11-next-
|
|
33
|
-
"@abtnode/util": "1.16.11-next-
|
|
22
|
+
"@abtnode/auth": "1.16.11-next-a232f5fb",
|
|
23
|
+
"@abtnode/certificate-manager": "1.16.11-next-a232f5fb",
|
|
24
|
+
"@abtnode/constant": "1.16.11-next-a232f5fb",
|
|
25
|
+
"@abtnode/cron": "1.16.11-next-a232f5fb",
|
|
26
|
+
"@abtnode/logger": "1.16.11-next-a232f5fb",
|
|
27
|
+
"@abtnode/models": "1.16.11-next-a232f5fb",
|
|
28
|
+
"@abtnode/queue": "1.16.11-next-a232f5fb",
|
|
29
|
+
"@abtnode/rbac": "1.16.11-next-a232f5fb",
|
|
30
|
+
"@abtnode/router-provider": "1.16.11-next-a232f5fb",
|
|
31
|
+
"@abtnode/static-server": "1.16.11-next-a232f5fb",
|
|
32
|
+
"@abtnode/timemachine": "1.16.11-next-a232f5fb",
|
|
33
|
+
"@abtnode/util": "1.16.11-next-a232f5fb",
|
|
34
34
|
"@arcblock/did": "1.18.80",
|
|
35
35
|
"@arcblock/did-auth": "1.18.80",
|
|
36
36
|
"@arcblock/did-ext": "^1.18.80",
|
|
@@ -41,10 +41,10 @@
|
|
|
41
41
|
"@arcblock/pm2-events": "^0.0.5",
|
|
42
42
|
"@arcblock/validator": "^1.18.80",
|
|
43
43
|
"@arcblock/vc": "1.18.80",
|
|
44
|
-
"@blocklet/constant": "1.16.11-next-
|
|
45
|
-
"@blocklet/meta": "1.16.11-next-
|
|
46
|
-
"@blocklet/sdk": "1.16.11-next-
|
|
47
|
-
"@did-space/client": "^0.2.
|
|
44
|
+
"@blocklet/constant": "1.16.11-next-a232f5fb",
|
|
45
|
+
"@blocklet/meta": "1.16.11-next-a232f5fb",
|
|
46
|
+
"@blocklet/sdk": "1.16.11-next-a232f5fb",
|
|
47
|
+
"@did-space/client": "^0.2.112",
|
|
48
48
|
"@fidm/x509": "^1.2.1",
|
|
49
49
|
"@ocap/mcrypto": "1.18.80",
|
|
50
50
|
"@ocap/util": "1.18.80",
|
|
@@ -96,5 +96,5 @@
|
|
|
96
96
|
"express": "^4.18.2",
|
|
97
97
|
"jest": "^27.5.1"
|
|
98
98
|
},
|
|
99
|
-
"gitHead": "
|
|
99
|
+
"gitHead": "1af264e9b6648aa191fbf23c67717262ac717697"
|
|
100
100
|
}
|