@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 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: ['id', 'appPid', 'userDid', 'visitorId', 'passportId', 'updatedAt', 'extra', 'ua', 'lastLoginIp'],
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 { consumeServerlessNFT, consumeLauncherSession } = require('../../util/launcher');
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 = 60 * 60; // seconds
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 */20 0 * * *', // 每天凌晨 0 点的每 20 分钟
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 assetState = await util.getNFTState(data.controller.chainHost, data.controller.nftId);
3924
- const isExpired = isNFTExpired(assetState);
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 expired blocklet');
4020
+ logger.info('no serverless blocklet');
3959
4021
  return;
3960
4022
  }
3961
4023
 
3962
- logger.info('expired blocklet count', { count: blockletExtras.length });
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 assetState = await util.getNFTState(data.controller.chainHost, data.controller.nftId);
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 assetState = await util.getNFTState(data.controller.chainHost, data.controller.nftId);
4032
- const expiredAt = getNftExpirationDate(assetState);
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
- if (!exceedRedemptionPeriod(expiredAt)) {
4035
- logger.error('skip cleaning the non-exceed redemption blocklet', {
4114
+ // 订阅终止后需要再保留一段时间数据
4115
+ if (!launcher.isDataRetentionExceeded(launcherSession.terminatedAt)) {
4116
+ logger.info('skip cleaning the non-exceed redemption blocklet', {
4036
4117
  blockletDid: did,
4037
- expiredAt,
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 assetState = await util.getNFTState(blocklet.controller?.chainHost, blocklet.controller?.nftId);
4357
+ const isExpired = await launcher.isBlockletExpired(blocklet.meta.did, blocklet.controller);
4278
4358
 
4279
- if (isNFTExpired(assetState)) {
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
  });
@@ -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.NODE_ENV === 'development' && process.env.ABT_NODE_ENABLE_SLP_DOMAIN === 'true') {
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,
@@ -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: { appDid: blocklet.appPid, appUrl, appName: name, ownerDid: params.ownerDid },
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, ['_id', 'status']);
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
  };
@@ -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-44800645",
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-44800645",
23
- "@abtnode/auth": "1.16.25-next-44800645",
24
- "@abtnode/certificate-manager": "1.16.25-next-44800645",
25
- "@abtnode/constant": "1.16.25-next-44800645",
26
- "@abtnode/cron": "1.16.25-next-44800645",
27
- "@abtnode/logger": "1.16.25-next-44800645",
28
- "@abtnode/models": "1.16.25-next-44800645",
29
- "@abtnode/queue": "1.16.25-next-44800645",
30
- "@abtnode/rbac": "1.16.25-next-44800645",
31
- "@abtnode/router-provider": "1.16.25-next-44800645",
32
- "@abtnode/static-server": "1.16.25-next-44800645",
33
- "@abtnode/timemachine": "1.16.25-next-44800645",
34
- "@abtnode/util": "1.16.25-next-44800645",
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-44800645",
46
- "@blocklet/env": "1.16.25-next-44800645",
47
- "@blocklet/meta": "1.16.25-next-44800645",
48
- "@blocklet/resolver": "1.16.25-next-44800645",
49
- "@blocklet/sdk": "1.16.25-next-44800645",
50
- "@did-space/client": "^0.3.69",
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": "7d09cee17eb88bcfe6a4b298a0394c5945aa5f2e"
105
+ "gitHead": "acabb2f45de011bb6db8038202c5632e85aef8f5"
106
106
  }