@abtnode/core 1.16.25-next-44800645 → 1.16.25-next-1ada5956
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 +32 -4
- package/lib/blocklet/manager/disk.js +104 -24
- package/lib/blocklet/manager/helper/install-application-from-general.js +0 -10
- package/lib/index.js +21 -0
- package/lib/util/blocklet.js +1 -8
- package/lib/util/launcher.js +96 -2
- package/lib/validators/util.js +0 -1
- package/package.json +21 -21
package/lib/api/team.js
CHANGED
|
@@ -15,6 +15,7 @@ const {
|
|
|
15
15
|
STORE_DETAIL_PAGE_PATH_PREFIX,
|
|
16
16
|
MAIN_CHAIN_ENDPOINT,
|
|
17
17
|
USER_AVATAR_URL_PREFIX,
|
|
18
|
+
SESSION_TTL,
|
|
18
19
|
} = require('@abtnode/constant');
|
|
19
20
|
const { isValid: isValidDid } = require('@arcblock/did');
|
|
20
21
|
const { BlockletEvents, TeamEvents } = require('@blocklet/constant');
|
|
@@ -266,12 +267,18 @@ class TeamAPI extends EventEmitter {
|
|
|
266
267
|
list = doc.list;
|
|
267
268
|
paging = doc.paging;
|
|
268
269
|
}
|
|
270
|
+
const now = Date.now();
|
|
271
|
+
let sessionTtl = SESSION_TTL;
|
|
272
|
+
if (teamDid !== nodeInfo.did) {
|
|
273
|
+
const blocklet = await getBlocklet({ did: teamDid, states: this.states, dataDirs: this.dataDirs });
|
|
274
|
+
sessionTtl = blocklet.settings?.session?.ttl || SESSION_TTL;
|
|
275
|
+
}
|
|
269
276
|
|
|
270
277
|
return {
|
|
271
278
|
// FIXME: @zhanghan 这里做字段过滤的目的是?gql 本身已经对字段做了过滤了
|
|
272
279
|
users: list.map(
|
|
273
|
-
(d) =>
|
|
274
|
-
pick(d, [
|
|
280
|
+
(d) => {
|
|
281
|
+
const pickData = pick(d, [
|
|
275
282
|
'did',
|
|
276
283
|
'pk',
|
|
277
284
|
'role',
|
|
@@ -293,7 +300,16 @@ class TeamAPI extends EventEmitter {
|
|
|
293
300
|
'sourceProvider',
|
|
294
301
|
'sourceAppPid',
|
|
295
302
|
'connectedAccounts',
|
|
296
|
-
])
|
|
303
|
+
]);
|
|
304
|
+
if (pickData?.userSessions) {
|
|
305
|
+
pickData.userSessions = pickData.userSessions.map((x) => {
|
|
306
|
+
const status = now - new Date(x.updatedAt).getTime() > sessionTtl * 1000 ? 'expired' : x.status;
|
|
307
|
+
return { ...x, status };
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
return pickData;
|
|
311
|
+
}
|
|
312
|
+
|
|
297
313
|
// eslint-disable-next-line function-paren-newline
|
|
298
314
|
),
|
|
299
315
|
paging,
|
|
@@ -1403,7 +1419,19 @@ class TeamAPI extends EventEmitter {
|
|
|
1403
1419
|
|
|
1404
1420
|
const userSessions = await state.model.findAll({
|
|
1405
1421
|
where,
|
|
1406
|
-
attributes: [
|
|
1422
|
+
attributes: [
|
|
1423
|
+
'id',
|
|
1424
|
+
'appPid',
|
|
1425
|
+
'userDid',
|
|
1426
|
+
'visitorId',
|
|
1427
|
+
'passportId',
|
|
1428
|
+
'createdAt',
|
|
1429
|
+
'updatedAt',
|
|
1430
|
+
'extra',
|
|
1431
|
+
'ua',
|
|
1432
|
+
'lastLoginIp',
|
|
1433
|
+
'status',
|
|
1434
|
+
],
|
|
1407
1435
|
include: {
|
|
1408
1436
|
model: userState.model,
|
|
1409
1437
|
as: 'user',
|
|
@@ -10,7 +10,6 @@ const merge = require('lodash/merge');
|
|
|
10
10
|
const pick = require('lodash/pick');
|
|
11
11
|
const isEmpty = require('lodash/isEmpty');
|
|
12
12
|
const cloneDeep = require('lodash/cloneDeep');
|
|
13
|
-
const { isNFTExpired, getNftExpirationDate } = require('@abtnode/util/lib/nft');
|
|
14
13
|
const { sign } = require('@arcblock/jwt');
|
|
15
14
|
const sleep = require('@abtnode/util/lib/sleep');
|
|
16
15
|
const getBlockletInfo = require('@blocklet/meta/lib/info');
|
|
@@ -31,6 +30,7 @@ const {
|
|
|
31
30
|
DEFAULT_DID_DOMAIN,
|
|
32
31
|
FEDERATED,
|
|
33
32
|
CHECK_UPDATE,
|
|
33
|
+
SESSION_CACHE_TTL,
|
|
34
34
|
} = require('@abtnode/constant');
|
|
35
35
|
|
|
36
36
|
const getBlockletEngine = require('@blocklet/meta/lib/engine');
|
|
@@ -102,7 +102,7 @@ const formatContext = require('@abtnode/util/lib/format-context');
|
|
|
102
102
|
const md5 = require('@abtnode/util/lib/md5');
|
|
103
103
|
const { callFederated } = require('@abtnode/auth/lib/util/federated');
|
|
104
104
|
const pAll = require('p-all');
|
|
105
|
-
const
|
|
105
|
+
const launcher = require('../../util/launcher');
|
|
106
106
|
const util = require('../../util');
|
|
107
107
|
const {
|
|
108
108
|
refresh: refreshAccessibleExternalNodeIp,
|
|
@@ -140,7 +140,6 @@ const {
|
|
|
140
140
|
getProcessState,
|
|
141
141
|
getBlockletStatus,
|
|
142
142
|
shouldSkipComponent,
|
|
143
|
-
exceedRedemptionPeriod,
|
|
144
143
|
ensureAppPortsNotOccupied,
|
|
145
144
|
getComponentNamesWithVersion,
|
|
146
145
|
updateDidDocument: updateBlockletDocument,
|
|
@@ -264,6 +263,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
264
263
|
backupQueue,
|
|
265
264
|
restoreQueue,
|
|
266
265
|
checkUpdateQueue,
|
|
266
|
+
reportComponentUsageQueue,
|
|
267
267
|
daemon = false,
|
|
268
268
|
teamManager,
|
|
269
269
|
}) {
|
|
@@ -279,6 +279,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
279
279
|
this.backupQueue = backupQueue;
|
|
280
280
|
this.restoreQueue = restoreQueue;
|
|
281
281
|
this.checkUpdateQueue = checkUpdateQueue;
|
|
282
|
+
this.reportComponentUsageQueue = reportComponentUsageQueue;
|
|
282
283
|
this.teamManager = teamManager;
|
|
283
284
|
|
|
284
285
|
if (isFunction(this.backupQueue.on)) {
|
|
@@ -1196,6 +1197,27 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
1196
1197
|
|
|
1197
1198
|
const newBlocklet = await this.getBlocklet(rootDid);
|
|
1198
1199
|
|
|
1200
|
+
if (newBlocklet.controller) {
|
|
1201
|
+
const componentDids = [did];
|
|
1202
|
+
const eventType = 'uninstall';
|
|
1203
|
+
|
|
1204
|
+
this.reportComponentUsageQueue.push({
|
|
1205
|
+
entity: 'blocklet',
|
|
1206
|
+
action: 'reportComponentUsage',
|
|
1207
|
+
did: newBlocklet.meta.did,
|
|
1208
|
+
time: new Date().toISOString(),
|
|
1209
|
+
componentDids,
|
|
1210
|
+
eventType,
|
|
1211
|
+
context,
|
|
1212
|
+
});
|
|
1213
|
+
|
|
1214
|
+
logger.info('pushed reporting uninstall components event job to queue', {
|
|
1215
|
+
did: newBlocklet.meta.did,
|
|
1216
|
+
componentDids,
|
|
1217
|
+
eventType,
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1199
1221
|
await this._updateDependents(rootDid);
|
|
1200
1222
|
|
|
1201
1223
|
// support edge case
|
|
@@ -1807,7 +1829,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
1807
1829
|
sessionConfig.cacheTtl = validateConfig.cacheTtl;
|
|
1808
1830
|
} else {
|
|
1809
1831
|
// NOTE: 将 cacheTtl 默认值设置为 3600s
|
|
1810
|
-
sessionConfig.cacheTtl =
|
|
1832
|
+
sessionConfig.cacheTtl = SESSION_CACHE_TTL; // seconds
|
|
1811
1833
|
}
|
|
1812
1834
|
if (validateConfig.ttl) {
|
|
1813
1835
|
sessionConfig.ttl = validateConfig.ttl;
|
|
@@ -2025,6 +2047,10 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
2025
2047
|
if (job.action === 'autoCheckUpdate') {
|
|
2026
2048
|
await this._onCheckForComponentUpdate(job);
|
|
2027
2049
|
}
|
|
2050
|
+
|
|
2051
|
+
if (job.action === 'reportComponentUsage') {
|
|
2052
|
+
await this._reportComponentUsage(job);
|
|
2053
|
+
}
|
|
2028
2054
|
}
|
|
2029
2055
|
}
|
|
2030
2056
|
|
|
@@ -2069,7 +2095,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
2069
2095
|
},
|
|
2070
2096
|
{
|
|
2071
2097
|
name: 'clean-expired-blocklet-data',
|
|
2072
|
-
time: '0
|
|
2098
|
+
time: '0 10 0 * * *', // 每天凌晨 0:10
|
|
2073
2099
|
options: { runOnInit: false },
|
|
2074
2100
|
fn: () => this.cleanExpiredBlocklets(),
|
|
2075
2101
|
},
|
|
@@ -2526,6 +2552,23 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
2526
2552
|
context,
|
|
2527
2553
|
action: postAction,
|
|
2528
2554
|
});
|
|
2555
|
+
|
|
2556
|
+
const newBlocklet = await this.getBlocklet(did);
|
|
2557
|
+
if (newBlocklet.controller) {
|
|
2558
|
+
const eventType = 'install';
|
|
2559
|
+
|
|
2560
|
+
this.reportComponentUsageQueue.push({
|
|
2561
|
+
entity: 'blocklet',
|
|
2562
|
+
action: 'reportComponentUsage',
|
|
2563
|
+
time: new Date().toISOString(),
|
|
2564
|
+
did,
|
|
2565
|
+
componentDids,
|
|
2566
|
+
eventType: 'install',
|
|
2567
|
+
context,
|
|
2568
|
+
});
|
|
2569
|
+
|
|
2570
|
+
logger.info('pushed reporting install components event job to queue', { did, componentDids, eventType });
|
|
2571
|
+
}
|
|
2529
2572
|
} catch (err) {
|
|
2530
2573
|
logger.error('blocklet onUpgrade error', { error: err });
|
|
2531
2574
|
}
|
|
@@ -2925,6 +2968,25 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
2925
2968
|
}
|
|
2926
2969
|
}
|
|
2927
2970
|
|
|
2971
|
+
async _reportComponentUsage({ did, componentDids, eventType, time }) {
|
|
2972
|
+
try {
|
|
2973
|
+
logger.info('start report component usage', { did, componentDids, eventType });
|
|
2974
|
+
|
|
2975
|
+
const blocklet = await this.getBlocklet(did);
|
|
2976
|
+
await launcher.reportComponentsEvent({
|
|
2977
|
+
blocklet,
|
|
2978
|
+
dids: componentDids,
|
|
2979
|
+
type: eventType,
|
|
2980
|
+
time: time || new Date().toISOString(),
|
|
2981
|
+
});
|
|
2982
|
+
|
|
2983
|
+
logger.info('report component usage success', { did, componentDids, eventType });
|
|
2984
|
+
} catch (error) {
|
|
2985
|
+
logger.error('report component usage failed', { did, componentDids, eventType, error });
|
|
2986
|
+
throw error; // 一定要 throw 出去,否则会导致队列任务不会重试
|
|
2987
|
+
}
|
|
2988
|
+
}
|
|
2989
|
+
|
|
2928
2990
|
/**
|
|
2929
2991
|
* @description send notification to multiple users, and send different notifications according to the user's own language
|
|
2930
2992
|
* @param {{
|
|
@@ -3345,10 +3407,10 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3345
3407
|
if (blocklet.controller && process.env.NODE_ENV !== 'test') {
|
|
3346
3408
|
let isNFTConsumed = false;
|
|
3347
3409
|
if (blocklet.controller.launcherSessionId && blocklet.controller.launcherUrl) {
|
|
3348
|
-
await consumeLauncherSession({ params: blocklet.controller, blocklet });
|
|
3410
|
+
await launcher.consumeLauncherSession({ params: blocklet.controller, blocklet });
|
|
3349
3411
|
isNFTConsumed = true;
|
|
3350
3412
|
} else if (blocklet.controller.nftId) {
|
|
3351
|
-
await consumeServerlessNFT({ nftId: blocklet.controller.nftId, blocklet });
|
|
3413
|
+
await launcher.consumeServerlessNFT({ nftId: blocklet.controller.nftId, blocklet });
|
|
3352
3414
|
isNFTConsumed = true;
|
|
3353
3415
|
}
|
|
3354
3416
|
|
|
@@ -3920,8 +3982,8 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3920
3982
|
continue;
|
|
3921
3983
|
}
|
|
3922
3984
|
|
|
3923
|
-
const
|
|
3924
|
-
|
|
3985
|
+
const isExpired = await launcher.isBlockletExpired(data.did, data.controller);
|
|
3986
|
+
|
|
3925
3987
|
if (isExpired === false) {
|
|
3926
3988
|
logger.info('blocklet is renewed', { did: data.did });
|
|
3927
3989
|
await states.blockletExtras.updateByDid(data.did, {
|
|
@@ -3955,24 +4017,21 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3955
4017
|
);
|
|
3956
4018
|
|
|
3957
4019
|
if (blockletExtras.length === 0) {
|
|
3958
|
-
logger.info('no
|
|
4020
|
+
logger.info('no serverless blocklet');
|
|
3959
4021
|
return;
|
|
3960
4022
|
}
|
|
3961
4023
|
|
|
3962
|
-
logger.info('
|
|
4024
|
+
logger.info('serverless blocklet count', { count: blockletExtras.length });
|
|
3963
4025
|
|
|
3964
4026
|
for (const data of blockletExtras) {
|
|
3965
4027
|
try {
|
|
3966
4028
|
const { did } = data;
|
|
3967
|
-
const
|
|
3968
|
-
const isExpired = isNFTExpired(assetState);
|
|
4029
|
+
const isExpired = await launcher.isBlockletExpired(did, data.controller);
|
|
3969
4030
|
|
|
3970
4031
|
if (isExpired) {
|
|
3971
|
-
const expiredAt = getNftExpirationDate(assetState);
|
|
3972
4032
|
logger.info('the blocklet already expired and will be stopped', {
|
|
3973
4033
|
blockletDid: did,
|
|
3974
4034
|
nftId: data.controller.nftId,
|
|
3975
|
-
expiredAt,
|
|
3976
4035
|
});
|
|
3977
4036
|
|
|
3978
4037
|
// 如果 Blocklet 没停止, 先停止
|
|
@@ -4028,22 +4087,43 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
4028
4087
|
for (const data of blockletExtras) {
|
|
4029
4088
|
try {
|
|
4030
4089
|
const { did } = data;
|
|
4031
|
-
const
|
|
4032
|
-
|
|
4090
|
+
const launcherSession = await launcher.getLauncherSession({
|
|
4091
|
+
launcherUrl: data.controller.launcherUrl,
|
|
4092
|
+
launcherSessionId: data.controller.launcherSessionId,
|
|
4093
|
+
});
|
|
4094
|
+
const isTerminated = launcher.isLaunchSessionTerminated(launcherSession);
|
|
4095
|
+
|
|
4096
|
+
if (!isTerminated) {
|
|
4097
|
+
logger.info('skip cleaning the non-exceed redemption blocklet', {
|
|
4098
|
+
blockletDid: did,
|
|
4099
|
+
controller: data.controller,
|
|
4100
|
+
launcherSession,
|
|
4101
|
+
});
|
|
4102
|
+
continue;
|
|
4103
|
+
}
|
|
4104
|
+
|
|
4105
|
+
if (!launcherSession.terminatedAt) {
|
|
4106
|
+
logger.error('the blocklet launch session does not have terminatedAt, skip', {
|
|
4107
|
+
blockletDid: did,
|
|
4108
|
+
controller: data.controller,
|
|
4109
|
+
launcherSession,
|
|
4110
|
+
});
|
|
4111
|
+
continue;
|
|
4112
|
+
}
|
|
4033
4113
|
|
|
4034
|
-
|
|
4035
|
-
|
|
4114
|
+
// 订阅终止后需要再保留一段时间数据
|
|
4115
|
+
if (!launcher.isDataRetentionExceeded(launcherSession.terminatedAt)) {
|
|
4116
|
+
logger.info('skip cleaning the non-exceed redemption blocklet', {
|
|
4036
4117
|
blockletDid: did,
|
|
4037
|
-
|
|
4118
|
+
controller: data.controller,
|
|
4119
|
+
launcherSession,
|
|
4038
4120
|
});
|
|
4039
|
-
// eslint-disable-next-line no-continue
|
|
4040
4121
|
continue;
|
|
4041
4122
|
}
|
|
4042
4123
|
|
|
4043
4124
|
logger.info('the blocklet already exceed redemption and will be deleted', {
|
|
4044
4125
|
blockletDid: did,
|
|
4045
4126
|
nftId: data.controller.nftId,
|
|
4046
|
-
expiredAt,
|
|
4047
4127
|
});
|
|
4048
4128
|
|
|
4049
4129
|
// TODO: 如果绑定了 DID Space 备份到 DID Space
|
|
@@ -4274,9 +4354,9 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
4274
4354
|
return;
|
|
4275
4355
|
}
|
|
4276
4356
|
|
|
4277
|
-
const
|
|
4357
|
+
const isExpired = await launcher.isBlockletExpired(blocklet.meta.did, blocklet.controller);
|
|
4278
4358
|
|
|
4279
|
-
if (
|
|
4359
|
+
if (isExpired) {
|
|
4280
4360
|
logger.error(`try to ${action} an expired blocklet`, {
|
|
4281
4361
|
did: blocklet.meta.did,
|
|
4282
4362
|
nftId: blocklet.controller?.nftId,
|
|
@@ -53,16 +53,6 @@ const installApplicationFromGeneral = async ({
|
|
|
53
53
|
throw new Error(`Should not be here: unknown type ${type}`);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
// check install count
|
|
57
|
-
if (controller?.nftId) {
|
|
58
|
-
const installedCount = await states.blockletExtras.count({ 'controller.nftId': controller.nftId });
|
|
59
|
-
if (installedCount >= (controller.appMaxCount || 1)) {
|
|
60
|
-
throw new Error(
|
|
61
|
-
`You can only install ${controller.appMaxCount} blocklet with this credential: ${controller.nftId}`
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
56
|
let blockletWalletType;
|
|
67
57
|
|
|
68
58
|
// create component
|
package/lib/index.js
CHANGED
|
@@ -189,6 +189,26 @@ function ABTNode(options) {
|
|
|
189
189
|
},
|
|
190
190
|
});
|
|
191
191
|
|
|
192
|
+
const reportComponentUsageQueue = createQueue({
|
|
193
|
+
daemon: options.daemon,
|
|
194
|
+
model: states.job,
|
|
195
|
+
name: 'report_component_usage_queue',
|
|
196
|
+
onJob: async (job) => {
|
|
197
|
+
/* eslint-disable no-use-before-define */
|
|
198
|
+
|
|
199
|
+
if (typeof blockletManager.onJob === 'function') {
|
|
200
|
+
await blockletManager.onJob(job);
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
options: {
|
|
204
|
+
concurrency,
|
|
205
|
+
maxRetries: 30,
|
|
206
|
+
retryDelay: 60 * 1000, // retry after 1 minute
|
|
207
|
+
maxTimeout: 60 * 1000 * 30, // throw timeout error after 15 minutes
|
|
208
|
+
id: (job) => (job ? md5(`${job.entity}-${job.action}-${job.id}`) : ''),
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
|
|
192
212
|
const certManager = new Cert({
|
|
193
213
|
maintainerEmail: DEFAULT_CERTIFICATE_EMAIL,
|
|
194
214
|
dataDir: dataDirs.certManagerModule,
|
|
@@ -216,6 +236,7 @@ function ABTNode(options) {
|
|
|
216
236
|
backupQueue,
|
|
217
237
|
restoreQueue,
|
|
218
238
|
checkUpdateQueue,
|
|
239
|
+
reportComponentUsageQueue,
|
|
219
240
|
daemon: options.daemon,
|
|
220
241
|
teamManager,
|
|
221
242
|
});
|
package/lib/util/blocklet.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs-extra');
|
|
4
4
|
const path = require('path');
|
|
5
|
-
const dayjs = require('@abtnode/util/lib/dayjs');
|
|
6
5
|
const shelljs = require('shelljs');
|
|
7
6
|
const os = require('os');
|
|
8
7
|
const tar = require('tar');
|
|
@@ -38,7 +37,6 @@ const {
|
|
|
38
37
|
BLOCKLET_MAX_MEM_LIMIT_IN_MB,
|
|
39
38
|
BLOCKLET_INSTALL_TYPE,
|
|
40
39
|
APP_STRUCT_VERSION,
|
|
41
|
-
EXPIRED_BLOCKLET_DATA_RETENTION_DAYS,
|
|
42
40
|
NODE_MODES,
|
|
43
41
|
} = require('@abtnode/constant');
|
|
44
42
|
const {
|
|
@@ -1801,10 +1799,6 @@ const shouldSkipComponent = (componentDid, whiteList) => {
|
|
|
1801
1799
|
return !arr.includes(componentDid);
|
|
1802
1800
|
};
|
|
1803
1801
|
|
|
1804
|
-
const exceedRedemptionPeriod = (expirationDate) => {
|
|
1805
|
-
return dayjs().diff(dayjs(expirationDate), 'day') > EXPIRED_BLOCKLET_DATA_RETENTION_DAYS;
|
|
1806
|
-
};
|
|
1807
|
-
|
|
1808
1802
|
const ensureAppPortsNotOccupied = async ({
|
|
1809
1803
|
blocklet,
|
|
1810
1804
|
componentDids: inputDids,
|
|
@@ -1858,7 +1852,7 @@ const getComponentNamesWithVersion = (app = {}, componentDids = []) => {
|
|
|
1858
1852
|
};
|
|
1859
1853
|
|
|
1860
1854
|
const shouldEnableSlpDomain = ({ mode, launcher }) => {
|
|
1861
|
-
if (process.env.
|
|
1855
|
+
if (process.env.ABT_NODE_ENABLE_SLP_DOMAIN === 'true') {
|
|
1862
1856
|
return true;
|
|
1863
1857
|
}
|
|
1864
1858
|
|
|
@@ -2045,7 +2039,6 @@ module.exports = {
|
|
|
2045
2039
|
getBlockletStatus,
|
|
2046
2040
|
shouldSkipComponent,
|
|
2047
2041
|
getBlockletURLForLauncher,
|
|
2048
|
-
exceedRedemptionPeriod,
|
|
2049
2042
|
ensureAppPortsNotOccupied,
|
|
2050
2043
|
getComponentNamesWithVersion,
|
|
2051
2044
|
updateDidDocument,
|
package/lib/util/launcher.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const fs = require('fs-extra');
|
|
3
3
|
const joinUrl = require('url-join');
|
|
4
|
+
const dayjs = require('@abtnode/util/lib/dayjs');
|
|
4
5
|
const pick = require('lodash/pick');
|
|
6
|
+
const uniq = require('lodash/uniq');
|
|
5
7
|
const isEmpty = require('lodash/isEmpty');
|
|
6
8
|
const {
|
|
7
9
|
getUserAvatarUrl,
|
|
@@ -24,6 +26,7 @@ const {
|
|
|
24
26
|
NODE_DATA_DIR_NAME,
|
|
25
27
|
USER_AVATAR_URL_PREFIX,
|
|
26
28
|
ROLES,
|
|
29
|
+
LAUNCH_SESSION_STATUS,
|
|
27
30
|
} = require('@abtnode/constant');
|
|
28
31
|
|
|
29
32
|
const logger = require('@abtnode/logger')('@abtnode/core:util:launcher');
|
|
@@ -58,16 +61,59 @@ const consumeServerlessNFT = async ({ nftId, blocklet }) => {
|
|
|
58
61
|
}
|
|
59
62
|
};
|
|
60
63
|
|
|
64
|
+
const reportComponentsEvent = async ({ blocklet, dids, type, time }) => {
|
|
65
|
+
const { controller } = blocklet;
|
|
66
|
+
const componentDids = uniq(dids);
|
|
67
|
+
|
|
68
|
+
const payload = {
|
|
69
|
+
componentDids,
|
|
70
|
+
type,
|
|
71
|
+
time,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const info = await states.node.read();
|
|
76
|
+
|
|
77
|
+
const result = await doRequest(info.sk, {
|
|
78
|
+
launcherUrl: controller.launcherUrl,
|
|
79
|
+
pathname: `/api/launches/${controller.launcherSessionId}/events`,
|
|
80
|
+
payload,
|
|
81
|
+
method: 'post',
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
logger.info(`reported components event ${type}`, {
|
|
85
|
+
controller,
|
|
86
|
+
blockletPid: blocklet.appPid,
|
|
87
|
+
componentDids,
|
|
88
|
+
type,
|
|
89
|
+
result,
|
|
90
|
+
});
|
|
91
|
+
} catch (error) {
|
|
92
|
+
logger.error('report components event failed', { controller, blockletPid: blocklet.appPid, componentDids, type });
|
|
93
|
+
throw new Error(`report components event failed of blocklet ${blocklet.appPid}`);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
61
97
|
const consumeLauncherSession = async ({ params, blocklet }) => {
|
|
62
98
|
try {
|
|
63
99
|
const info = await states.node.read();
|
|
64
100
|
const { name } = getBlockletInfo(blocklet, info.sk);
|
|
65
101
|
const appUrl = getBlockletURLForLauncher({ blocklet, nodeInfo: info });
|
|
66
102
|
|
|
103
|
+
let componentDids = blocklet.children.map((x) => x.meta.did);
|
|
104
|
+
componentDids = uniq(componentDids);
|
|
105
|
+
|
|
67
106
|
const result = await doRequest(info.sk, {
|
|
68
107
|
launcherUrl: params.launcherUrl,
|
|
69
108
|
pathname: `/api/launches/${params.launcherSessionId}/installed`,
|
|
70
|
-
payload: {
|
|
109
|
+
payload: {
|
|
110
|
+
appDid: blocklet.appPid,
|
|
111
|
+
appUrl,
|
|
112
|
+
appName: name,
|
|
113
|
+
installedAt: blocklet.installedAt,
|
|
114
|
+
ownerDid: params.ownerDid,
|
|
115
|
+
componentDids,
|
|
116
|
+
},
|
|
71
117
|
method: 'post',
|
|
72
118
|
});
|
|
73
119
|
|
|
@@ -213,7 +259,15 @@ const getLauncherSession = async ({ launcherUrl, launcherSessionId, external = t
|
|
|
213
259
|
|
|
214
260
|
// strip sensitive data if call from external
|
|
215
261
|
if (external && result.launcherSession) {
|
|
216
|
-
result.launcherSession = pick(result.launcherSession, [
|
|
262
|
+
result.launcherSession = pick(result.launcherSession, [
|
|
263
|
+
'_id',
|
|
264
|
+
'status',
|
|
265
|
+
'statusText',
|
|
266
|
+
'subscription',
|
|
267
|
+
'expirationDate',
|
|
268
|
+
'terminatedAt',
|
|
269
|
+
'reservedUntil',
|
|
270
|
+
]);
|
|
217
271
|
}
|
|
218
272
|
|
|
219
273
|
return result;
|
|
@@ -240,10 +294,50 @@ const isLauncherSessionConsumed = async (params) => {
|
|
|
240
294
|
return consumed;
|
|
241
295
|
};
|
|
242
296
|
|
|
297
|
+
const getLaunchSessionStatus = async (blockletDid, controller) => {
|
|
298
|
+
logger.info('checking blocklet status', { blockletDid, controller });
|
|
299
|
+
|
|
300
|
+
const { launcherSessionId } = controller;
|
|
301
|
+
// 新版都通过 Launcher 判断过期状态
|
|
302
|
+
const { error, launcherSession } = await getLauncherSession({
|
|
303
|
+
launcherSessionId,
|
|
304
|
+
launcherUrl: controller.launcherUrl || 'https://launcher.arcblock.io/',
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
if (error) {
|
|
308
|
+
logger.error('get launcher session failed', { error, blockletDid, controller });
|
|
309
|
+
throw new Error(error);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
logger.info('get launcher session success', { launcherSessionId, launcherSession });
|
|
313
|
+
return launcherSession.status;
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
const isBlockletExpired = async (blockletDid, controller) => {
|
|
317
|
+
const status = await getLaunchSessionStatus(blockletDid, controller);
|
|
318
|
+
return [LAUNCH_SESSION_STATUS.expired, LAUNCH_SESSION_STATUS.terminated].includes(status);
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
const isLaunchSessionTerminated = (session) => session.status === LAUNCH_SESSION_STATUS.terminated;
|
|
322
|
+
|
|
323
|
+
const isBlockletTerminated = async (blockletDid, controller) => {
|
|
324
|
+
const status = await getLaunchSessionStatus(blockletDid, controller);
|
|
325
|
+
return status === LAUNCH_SESSION_STATUS.terminated;
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
const DATA_RETENTION_DAYS = 30;
|
|
329
|
+
const isDataRetentionExceeded = (terminatedAt) =>
|
|
330
|
+
!!terminatedAt && dayjs().diff(dayjs(terminatedAt), 'days') > DATA_RETENTION_DAYS;
|
|
331
|
+
|
|
243
332
|
module.exports = {
|
|
244
333
|
consumeServerlessNFT,
|
|
245
334
|
consumeLauncherSession,
|
|
335
|
+
reportComponentsEvent,
|
|
246
336
|
setupAppOwner,
|
|
247
337
|
getLauncherSession,
|
|
338
|
+
isDataRetentionExceeded,
|
|
339
|
+
isLaunchSessionTerminated,
|
|
248
340
|
isLauncherSessionConsumed,
|
|
341
|
+
isBlockletExpired,
|
|
342
|
+
isBlockletTerminated,
|
|
249
343
|
};
|
package/lib/validators/util.js
CHANGED
|
@@ -14,7 +14,6 @@ const blockletController = Joi.object({
|
|
|
14
14
|
nftId: Joi.DID().required(),
|
|
15
15
|
nftOwner: Joi.DID().required(),
|
|
16
16
|
chainHost: Joi.string().uri().required(),
|
|
17
|
-
appMaxCount: Joi.number().required().min(1),
|
|
18
17
|
launcherUrl: Joi.string().uri().optional(),
|
|
19
18
|
launcherSessionId: Joi.string().optional(),
|
|
20
19
|
ownerDid: Joi.DID().optional(),
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.16.25-next-
|
|
6
|
+
"version": "1.16.25-next-1ada5956",
|
|
7
7
|
"description": "",
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"files": [
|
|
@@ -19,19 +19,19 @@
|
|
|
19
19
|
"author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
|
|
20
20
|
"license": "Apache-2.0",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@abtnode/analytics": "1.16.25-next-
|
|
23
|
-
"@abtnode/auth": "1.16.25-next-
|
|
24
|
-
"@abtnode/certificate-manager": "1.16.25-next-
|
|
25
|
-
"@abtnode/constant": "1.16.25-next-
|
|
26
|
-
"@abtnode/cron": "1.16.25-next-
|
|
27
|
-
"@abtnode/logger": "1.16.25-next-
|
|
28
|
-
"@abtnode/models": "1.16.25-next-
|
|
29
|
-
"@abtnode/queue": "1.16.25-next-
|
|
30
|
-
"@abtnode/rbac": "1.16.25-next-
|
|
31
|
-
"@abtnode/router-provider": "1.16.25-next-
|
|
32
|
-
"@abtnode/static-server": "1.16.25-next-
|
|
33
|
-
"@abtnode/timemachine": "1.16.25-next-
|
|
34
|
-
"@abtnode/util": "1.16.25-next-
|
|
22
|
+
"@abtnode/analytics": "1.16.25-next-1ada5956",
|
|
23
|
+
"@abtnode/auth": "1.16.25-next-1ada5956",
|
|
24
|
+
"@abtnode/certificate-manager": "1.16.25-next-1ada5956",
|
|
25
|
+
"@abtnode/constant": "1.16.25-next-1ada5956",
|
|
26
|
+
"@abtnode/cron": "1.16.25-next-1ada5956",
|
|
27
|
+
"@abtnode/logger": "1.16.25-next-1ada5956",
|
|
28
|
+
"@abtnode/models": "1.16.25-next-1ada5956",
|
|
29
|
+
"@abtnode/queue": "1.16.25-next-1ada5956",
|
|
30
|
+
"@abtnode/rbac": "1.16.25-next-1ada5956",
|
|
31
|
+
"@abtnode/router-provider": "1.16.25-next-1ada5956",
|
|
32
|
+
"@abtnode/static-server": "1.16.25-next-1ada5956",
|
|
33
|
+
"@abtnode/timemachine": "1.16.25-next-1ada5956",
|
|
34
|
+
"@abtnode/util": "1.16.25-next-1ada5956",
|
|
35
35
|
"@arcblock/did": "1.18.113",
|
|
36
36
|
"@arcblock/did-auth": "1.18.113",
|
|
37
37
|
"@arcblock/did-ext": "^1.18.113",
|
|
@@ -42,12 +42,12 @@
|
|
|
42
42
|
"@arcblock/pm2-events": "^0.0.5",
|
|
43
43
|
"@arcblock/validator": "^1.18.113",
|
|
44
44
|
"@arcblock/vc": "1.18.113",
|
|
45
|
-
"@blocklet/constant": "1.16.25-next-
|
|
46
|
-
"@blocklet/env": "1.16.25-next-
|
|
47
|
-
"@blocklet/meta": "1.16.25-next-
|
|
48
|
-
"@blocklet/resolver": "1.16.25-next-
|
|
49
|
-
"@blocklet/sdk": "1.16.25-next-
|
|
50
|
-
"@did-space/client": "^0.3.
|
|
45
|
+
"@blocklet/constant": "1.16.25-next-1ada5956",
|
|
46
|
+
"@blocklet/env": "1.16.25-next-1ada5956",
|
|
47
|
+
"@blocklet/meta": "1.16.25-next-1ada5956",
|
|
48
|
+
"@blocklet/resolver": "1.16.25-next-1ada5956",
|
|
49
|
+
"@blocklet/sdk": "1.16.25-next-1ada5956",
|
|
50
|
+
"@did-space/client": "^0.3.71",
|
|
51
51
|
"@fidm/x509": "^1.2.1",
|
|
52
52
|
"@ocap/mcrypto": "1.18.113",
|
|
53
53
|
"@ocap/util": "1.18.113",
|
|
@@ -102,5 +102,5 @@
|
|
|
102
102
|
"jest": "^29.7.0",
|
|
103
103
|
"unzipper": "^0.10.11"
|
|
104
104
|
},
|
|
105
|
-
"gitHead": "
|
|
105
|
+
"gitHead": "acabb2f45de011bb6db8038202c5632e85aef8f5"
|
|
106
106
|
}
|