@abtnode/core 1.16.25-beta-023ec101 → 1.16.25-beta-bc165d9b
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/blocklet/downloader/resolve-download.js +2 -2
- package/lib/blocklet/manager/disk.js +102 -23
- package/lib/blocklet/manager/helper/install-application-from-general.js +0 -10
- package/lib/blocklet/project/create-pack-release.js +12 -10
- package/lib/blocklet/project/index.js +15 -7
- package/lib/index.js +21 -0
- package/lib/states/project.js +3 -1
- package/lib/util/blocklet.js +4 -8
- package/lib/util/install-external-dependencies.js +40 -17
- package/lib/util/launcher.js +96 -2
- package/lib/validators/util.js +0 -1
- package/package.json +21 -21
|
@@ -78,7 +78,7 @@ const resolveDownload = async (tarFile, dist, { removeTarFile = true, logger = d
|
|
|
78
78
|
|
|
79
79
|
await fs.move(downloadDir, installDir, { overwrite: true });
|
|
80
80
|
|
|
81
|
-
installExternalDependencies(installDir);
|
|
81
|
+
await installExternalDependencies({ appDir: installDir, forceInstall: true });
|
|
82
82
|
} catch (error) {
|
|
83
83
|
fs.removeSync(downloadDir);
|
|
84
84
|
fs.removeSync(tmp);
|
|
@@ -145,7 +145,7 @@ const resolveDiffDownload = async (
|
|
|
145
145
|
logger.info('Move downloadDir to installDir', { downloadDir, bundleDir });
|
|
146
146
|
await fs.move(downloadDir, bundleDir, { overwrite: true });
|
|
147
147
|
|
|
148
|
-
installExternalDependencies(bundleDir);
|
|
148
|
+
await installExternalDependencies({ appDir: bundleDir, forceInstall: true });
|
|
149
149
|
|
|
150
150
|
return { meta, installDir: bundleDir };
|
|
151
151
|
} catch (error) {
|
|
@@ -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');
|
|
@@ -103,7 +102,7 @@ const formatContext = require('@abtnode/util/lib/format-context');
|
|
|
103
102
|
const md5 = require('@abtnode/util/lib/md5');
|
|
104
103
|
const { callFederated } = require('@abtnode/auth/lib/util/federated');
|
|
105
104
|
const pAll = require('p-all');
|
|
106
|
-
const
|
|
105
|
+
const launcher = require('../../util/launcher');
|
|
107
106
|
const util = require('../../util');
|
|
108
107
|
const {
|
|
109
108
|
refresh: refreshAccessibleExternalNodeIp,
|
|
@@ -141,7 +140,6 @@ const {
|
|
|
141
140
|
getProcessState,
|
|
142
141
|
getBlockletStatus,
|
|
143
142
|
shouldSkipComponent,
|
|
144
|
-
exceedRedemptionPeriod,
|
|
145
143
|
ensureAppPortsNotOccupied,
|
|
146
144
|
getComponentNamesWithVersion,
|
|
147
145
|
updateDidDocument: updateBlockletDocument,
|
|
@@ -265,6 +263,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
265
263
|
backupQueue,
|
|
266
264
|
restoreQueue,
|
|
267
265
|
checkUpdateQueue,
|
|
266
|
+
reportComponentUsageQueue,
|
|
268
267
|
daemon = false,
|
|
269
268
|
teamManager,
|
|
270
269
|
}) {
|
|
@@ -280,6 +279,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
280
279
|
this.backupQueue = backupQueue;
|
|
281
280
|
this.restoreQueue = restoreQueue;
|
|
282
281
|
this.checkUpdateQueue = checkUpdateQueue;
|
|
282
|
+
this.reportComponentUsageQueue = reportComponentUsageQueue;
|
|
283
283
|
this.teamManager = teamManager;
|
|
284
284
|
|
|
285
285
|
if (isFunction(this.backupQueue.on)) {
|
|
@@ -1197,6 +1197,27 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
1197
1197
|
|
|
1198
1198
|
const newBlocklet = await this.getBlocklet(rootDid);
|
|
1199
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
|
+
|
|
1200
1221
|
await this._updateDependents(rootDid);
|
|
1201
1222
|
|
|
1202
1223
|
// support edge case
|
|
@@ -2026,6 +2047,10 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
2026
2047
|
if (job.action === 'autoCheckUpdate') {
|
|
2027
2048
|
await this._onCheckForComponentUpdate(job);
|
|
2028
2049
|
}
|
|
2050
|
+
|
|
2051
|
+
if (job.action === 'reportComponentUsage') {
|
|
2052
|
+
await this._reportComponentUsage(job);
|
|
2053
|
+
}
|
|
2029
2054
|
}
|
|
2030
2055
|
}
|
|
2031
2056
|
|
|
@@ -2070,7 +2095,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
2070
2095
|
},
|
|
2071
2096
|
{
|
|
2072
2097
|
name: 'clean-expired-blocklet-data',
|
|
2073
|
-
time: '0
|
|
2098
|
+
time: '0 10 0 * * *', // 每天凌晨 0:10
|
|
2074
2099
|
options: { runOnInit: false },
|
|
2075
2100
|
fn: () => this.cleanExpiredBlocklets(),
|
|
2076
2101
|
},
|
|
@@ -2527,6 +2552,23 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
2527
2552
|
context,
|
|
2528
2553
|
action: postAction,
|
|
2529
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
|
+
}
|
|
2530
2572
|
} catch (err) {
|
|
2531
2573
|
logger.error('blocklet onUpgrade error', { error: err });
|
|
2532
2574
|
}
|
|
@@ -2926,6 +2968,25 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
2926
2968
|
}
|
|
2927
2969
|
}
|
|
2928
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
|
+
|
|
2929
2990
|
/**
|
|
2930
2991
|
* @description send notification to multiple users, and send different notifications according to the user's own language
|
|
2931
2992
|
* @param {{
|
|
@@ -3346,10 +3407,10 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3346
3407
|
if (blocklet.controller && process.env.NODE_ENV !== 'test') {
|
|
3347
3408
|
let isNFTConsumed = false;
|
|
3348
3409
|
if (blocklet.controller.launcherSessionId && blocklet.controller.launcherUrl) {
|
|
3349
|
-
await consumeLauncherSession({ params: blocklet.controller, blocklet });
|
|
3410
|
+
await launcher.consumeLauncherSession({ params: blocklet.controller, blocklet });
|
|
3350
3411
|
isNFTConsumed = true;
|
|
3351
3412
|
} else if (blocklet.controller.nftId) {
|
|
3352
|
-
await consumeServerlessNFT({ nftId: blocklet.controller.nftId, blocklet });
|
|
3413
|
+
await launcher.consumeServerlessNFT({ nftId: blocklet.controller.nftId, blocklet });
|
|
3353
3414
|
isNFTConsumed = true;
|
|
3354
3415
|
}
|
|
3355
3416
|
|
|
@@ -3921,8 +3982,8 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3921
3982
|
continue;
|
|
3922
3983
|
}
|
|
3923
3984
|
|
|
3924
|
-
const
|
|
3925
|
-
|
|
3985
|
+
const isExpired = await launcher.isBlockletExpired(data.did, data.controller);
|
|
3986
|
+
|
|
3926
3987
|
if (isExpired === false) {
|
|
3927
3988
|
logger.info('blocklet is renewed', { did: data.did });
|
|
3928
3989
|
await states.blockletExtras.updateByDid(data.did, {
|
|
@@ -3956,24 +4017,21 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
3956
4017
|
);
|
|
3957
4018
|
|
|
3958
4019
|
if (blockletExtras.length === 0) {
|
|
3959
|
-
logger.info('no
|
|
4020
|
+
logger.info('no serverless blocklet');
|
|
3960
4021
|
return;
|
|
3961
4022
|
}
|
|
3962
4023
|
|
|
3963
|
-
logger.info('
|
|
4024
|
+
logger.info('serverless blocklet count', { count: blockletExtras.length });
|
|
3964
4025
|
|
|
3965
4026
|
for (const data of blockletExtras) {
|
|
3966
4027
|
try {
|
|
3967
4028
|
const { did } = data;
|
|
3968
|
-
const
|
|
3969
|
-
const isExpired = isNFTExpired(assetState);
|
|
4029
|
+
const isExpired = await launcher.isBlockletExpired(did, data.controller);
|
|
3970
4030
|
|
|
3971
4031
|
if (isExpired) {
|
|
3972
|
-
const expiredAt = getNftExpirationDate(assetState);
|
|
3973
4032
|
logger.info('the blocklet already expired and will be stopped', {
|
|
3974
4033
|
blockletDid: did,
|
|
3975
4034
|
nftId: data.controller.nftId,
|
|
3976
|
-
expiredAt,
|
|
3977
4035
|
});
|
|
3978
4036
|
|
|
3979
4037
|
// 如果 Blocklet 没停止, 先停止
|
|
@@ -4029,22 +4087,43 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
4029
4087
|
for (const data of blockletExtras) {
|
|
4030
4088
|
try {
|
|
4031
4089
|
const { did } = data;
|
|
4032
|
-
const
|
|
4033
|
-
|
|
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
|
+
}
|
|
4034
4113
|
|
|
4035
|
-
|
|
4036
|
-
|
|
4114
|
+
// 订阅终止后需要再保留一段时间数据
|
|
4115
|
+
if (!launcher.isDataRetentionExceeded(launcherSession.terminatedAt)) {
|
|
4116
|
+
logger.info('skip cleaning the non-exceed redemption blocklet', {
|
|
4037
4117
|
blockletDid: did,
|
|
4038
|
-
|
|
4118
|
+
controller: data.controller,
|
|
4119
|
+
launcherSession,
|
|
4039
4120
|
});
|
|
4040
|
-
// eslint-disable-next-line no-continue
|
|
4041
4121
|
continue;
|
|
4042
4122
|
}
|
|
4043
4123
|
|
|
4044
4124
|
logger.info('the blocklet already exceed redemption and will be deleted', {
|
|
4045
4125
|
blockletDid: did,
|
|
4046
4126
|
nftId: data.controller.nftId,
|
|
4047
|
-
expiredAt,
|
|
4048
4127
|
});
|
|
4049
4128
|
|
|
4050
4129
|
// TODO: 如果绑定了 DID Space 备份到 DID Space
|
|
@@ -4275,9 +4354,9 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
4275
4354
|
return;
|
|
4276
4355
|
}
|
|
4277
4356
|
|
|
4278
|
-
const
|
|
4357
|
+
const isExpired = await launcher.isBlockletExpired(blocklet.meta.did, blocklet.controller);
|
|
4279
4358
|
|
|
4280
|
-
if (
|
|
4359
|
+
if (isExpired) {
|
|
4281
4360
|
logger.error(`try to ${action} an expired blocklet`, {
|
|
4282
4361
|
did: blocklet.meta.did,
|
|
4283
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
|
|
@@ -62,10 +62,6 @@ const createPackRelease = async ({
|
|
|
62
62
|
throw new Error('project not found');
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
if (project0.type !== PROJECT.TYPES.pack) {
|
|
66
|
-
throw new Error('project type is not pack');
|
|
67
|
-
}
|
|
68
|
-
|
|
69
65
|
const action = releaseId ? 'update' : 'create';
|
|
70
66
|
|
|
71
67
|
if (action === 'update') {
|
|
@@ -99,10 +95,8 @@ const createPackRelease = async ({
|
|
|
99
95
|
releaseId,
|
|
100
96
|
status,
|
|
101
97
|
});
|
|
102
|
-
const project = await projectState.updateProject(projectId, params);
|
|
103
98
|
|
|
104
99
|
const _releaseId = release.id;
|
|
105
|
-
|
|
106
100
|
const releaseDir = path.join(projectDir, PROJECT.RELEASE_DIR, `${_releaseId}`);
|
|
107
101
|
const resourceDir = path.join(releaseDir, PROJECT.RESOURCE_DIR);
|
|
108
102
|
const tmpResourceDir = path.join(projectDir, PROJECT.RESOURCE_DIR);
|
|
@@ -118,10 +112,6 @@ const createPackRelease = async ({
|
|
|
118
112
|
await fs.ensureDir(tmpResourceDir);
|
|
119
113
|
}
|
|
120
114
|
|
|
121
|
-
if (status === PROJECT.RELEASE_STATUS.draft) {
|
|
122
|
-
return release;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
115
|
const releaseExportDir = path.join(releaseDir, '.blocklet');
|
|
126
116
|
const releaseBundleDir = path.join(releaseExportDir, 'bundle');
|
|
127
117
|
const releaseReleaseDir = path.join(releaseExportDir, 'release');
|
|
@@ -129,6 +119,18 @@ const createPackRelease = async ({
|
|
|
129
119
|
const moniker = (urlPathFriendly(slugify(release.blockletTitle)) || 'blocklet').toLowerCase();
|
|
130
120
|
const releaseFileName = `${moniker}-${release.blockletVersion}.zip`;
|
|
131
121
|
const releaseFile = path.join(releaseDir, releaseFileName);
|
|
122
|
+
|
|
123
|
+
if (status !== PROJECT.RELEASE_STATUS.draft) {
|
|
124
|
+
params.lastReleaseId = _releaseId;
|
|
125
|
+
params.lastReleaseFiles = [releaseFileName];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const project = await projectState.updateProject(projectId, params);
|
|
129
|
+
|
|
130
|
+
if (status === PROJECT.RELEASE_STATUS.draft) {
|
|
131
|
+
return release;
|
|
132
|
+
}
|
|
133
|
+
|
|
132
134
|
if (fs.existsSync(releaseFile)) {
|
|
133
135
|
logger.error('release file already exists, remove it', releaseFile);
|
|
134
136
|
await fs.remove(releaseFile);
|
|
@@ -150,7 +150,9 @@ const createRelease = async ({
|
|
|
150
150
|
throw new Error('project not found');
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
|
|
153
|
+
const isPack = blockletComponents?.length > 0;
|
|
154
|
+
|
|
155
|
+
if (isPack) {
|
|
154
156
|
return createPackRelease({
|
|
155
157
|
did,
|
|
156
158
|
projectId,
|
|
@@ -201,10 +203,8 @@ const createRelease = async ({
|
|
|
201
203
|
releaseId,
|
|
202
204
|
status,
|
|
203
205
|
});
|
|
204
|
-
const project = await projectState.updateProject(projectId, params);
|
|
205
206
|
|
|
206
207
|
const _releaseId = release.id;
|
|
207
|
-
|
|
208
208
|
const releaseDir = path.join(projectDir, PROJECT.RELEASE_DIR, `${_releaseId}`);
|
|
209
209
|
const resourceDir = path.join(releaseDir, PROJECT.RESOURCE_DIR);
|
|
210
210
|
const tmpResourceDir = path.join(projectDir, PROJECT.RESOURCE_DIR);
|
|
@@ -220,10 +220,6 @@ const createRelease = async ({
|
|
|
220
220
|
await fs.ensureDir(tmpResourceDir);
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
-
if (status === PROJECT.RELEASE_STATUS.draft) {
|
|
224
|
-
return release;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
223
|
const releaseExportDir = path.join(releaseDir, '.blocklet');
|
|
228
224
|
const releaseBundleDir = path.join(releaseExportDir, 'bundle');
|
|
229
225
|
const releaseReleaseDir = path.join(releaseExportDir, 'release');
|
|
@@ -231,6 +227,18 @@ const createRelease = async ({
|
|
|
231
227
|
const moniker = (urlPathFriendly(slugify(release.blockletTitle)) || 'blocklet').toLowerCase();
|
|
232
228
|
const releaseFileName = `${moniker}-${release.blockletVersion}.zip`;
|
|
233
229
|
const releaseFile = path.join(releaseDir, releaseFileName);
|
|
230
|
+
|
|
231
|
+
if (status !== PROJECT.RELEASE_STATUS.draft) {
|
|
232
|
+
params.lastReleaseId = _releaseId;
|
|
233
|
+
params.lastReleaseFiles = [releaseFileName];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const project = await projectState.updateProject(projectId, params);
|
|
237
|
+
|
|
238
|
+
if (status === PROJECT.RELEASE_STATUS.draft) {
|
|
239
|
+
return release;
|
|
240
|
+
}
|
|
241
|
+
|
|
234
242
|
if (fs.existsSync(releaseFile)) {
|
|
235
243
|
logger.error('release file already exists, remove it', releaseFile);
|
|
236
244
|
await fs.remove(releaseFile);
|
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/states/project.js
CHANGED
|
@@ -42,10 +42,12 @@ class Project extends BaseState {
|
|
|
42
42
|
'blockletDescription',
|
|
43
43
|
'blockletDid',
|
|
44
44
|
'blockletScreenshots',
|
|
45
|
+
'lastReleaseId',
|
|
46
|
+
'lastReleaseFiles',
|
|
45
47
|
]),
|
|
46
48
|
(x) => !isUndefinedOrNull(x)
|
|
47
49
|
);
|
|
48
|
-
|
|
50
|
+
_params.type = params.blockletComponents?.length ? 'pack' : 'resource';
|
|
49
51
|
const [, [updated]] = await this.update({ id }, { $set: _params });
|
|
50
52
|
return updated;
|
|
51
53
|
}
|
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 {
|
|
@@ -106,6 +104,7 @@ const { validate: validateEngine, get: getEngine } = require('../blocklet/manage
|
|
|
106
104
|
const isRequirementsSatisfied = require('./requirement');
|
|
107
105
|
const { getDidDomainForBlocklet } = require('./get-domain-for-blocklet');
|
|
108
106
|
const { expandBundle, findInterfacePortByName, prettyURL, templateReplace, getServerDidDomain } = require('./index');
|
|
107
|
+
const { installExternalDependencies } = require('./install-external-dependencies');
|
|
109
108
|
|
|
110
109
|
/**
|
|
111
110
|
* get blocklet engine info, default is node
|
|
@@ -564,6 +563,8 @@ const startBlockletProcess = async (
|
|
|
564
563
|
const env = getRuntimeEnvironments(b, nodeEnvironments, ancestors);
|
|
565
564
|
const startedAt = Date.now();
|
|
566
565
|
|
|
566
|
+
await installExternalDependencies({ appDir: env?.BLOCKLET_APP_DIR });
|
|
567
|
+
|
|
567
568
|
// run hook
|
|
568
569
|
await preFlight(b, { env });
|
|
569
570
|
|
|
@@ -1801,10 +1802,6 @@ const shouldSkipComponent = (componentDid, whiteList) => {
|
|
|
1801
1802
|
return !arr.includes(componentDid);
|
|
1802
1803
|
};
|
|
1803
1804
|
|
|
1804
|
-
const exceedRedemptionPeriod = (expirationDate) => {
|
|
1805
|
-
return dayjs().diff(dayjs(expirationDate), 'day') > EXPIRED_BLOCKLET_DATA_RETENTION_DAYS;
|
|
1806
|
-
};
|
|
1807
|
-
|
|
1808
1805
|
const ensureAppPortsNotOccupied = async ({
|
|
1809
1806
|
blocklet,
|
|
1810
1807
|
componentDids: inputDids,
|
|
@@ -1858,7 +1855,7 @@ const getComponentNamesWithVersion = (app = {}, componentDids = []) => {
|
|
|
1858
1855
|
};
|
|
1859
1856
|
|
|
1860
1857
|
const shouldEnableSlpDomain = ({ mode, launcher }) => {
|
|
1861
|
-
if (process.env.
|
|
1858
|
+
if (process.env.ABT_NODE_ENABLE_SLP_DOMAIN === 'true') {
|
|
1862
1859
|
return true;
|
|
1863
1860
|
}
|
|
1864
1861
|
|
|
@@ -2045,7 +2042,6 @@ module.exports = {
|
|
|
2045
2042
|
getBlockletStatus,
|
|
2046
2043
|
shouldSkipComponent,
|
|
2047
2044
|
getBlockletURLForLauncher,
|
|
2048
|
-
exceedRedemptionPeriod,
|
|
2049
2045
|
ensureAppPortsNotOccupied,
|
|
2050
2046
|
getComponentNamesWithVersion,
|
|
2051
2047
|
updateDidDocument,
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const { spawn } = require('child_process');
|
|
2
2
|
const fs = require('fs-extra');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
|
|
5
|
-
function
|
|
5
|
+
function isDependencyInstalled(appDir, dependency) {
|
|
6
|
+
return fs.existsSync(path.resolve(appDir, 'node_modules', dependency));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function installExternalDependencies({ appDir, forceInstall = false } = {}) {
|
|
6
10
|
if (!appDir) {
|
|
7
11
|
throw new Error('appDir is required');
|
|
8
12
|
}
|
|
@@ -22,25 +26,44 @@ function installExternalDependencies(appDir) {
|
|
|
22
26
|
// FIXME: 梁柱, 等未来所有的 blocklet 使用了正式版, 废弃 singleExternals
|
|
23
27
|
const { singleExternals, blockletExternalDependencies } = packageJson;
|
|
24
28
|
const externals = singleExternals || blockletExternalDependencies;
|
|
25
|
-
if (!externals || !externals
|
|
29
|
+
if (!Array.isArray(externals) || !externals.length) {
|
|
26
30
|
return;
|
|
27
31
|
}
|
|
28
32
|
|
|
29
|
-
const
|
|
30
|
-
cwd: appDir,
|
|
31
|
-
stdio: 'pipe',
|
|
32
|
-
shell: true,
|
|
33
|
-
env: {
|
|
34
|
-
...process.env,
|
|
35
|
-
NODE_ENV: 'production',
|
|
36
|
-
},
|
|
37
|
-
});
|
|
33
|
+
const isNeedInstall = forceInstall || externals.some((dependency) => !isDependencyInstalled(appDir, dependency));
|
|
38
34
|
|
|
39
|
-
if (
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
if (result.error) {
|
|
43
|
-
throw result.error;
|
|
35
|
+
if (!isNeedInstall) {
|
|
36
|
+
return;
|
|
44
37
|
}
|
|
38
|
+
|
|
39
|
+
// eslint-disable-next-line consistent-return
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
const child = spawn('npm', ['install'], {
|
|
42
|
+
cwd: appDir,
|
|
43
|
+
stdio: 'pipe',
|
|
44
|
+
shell: true,
|
|
45
|
+
env: {
|
|
46
|
+
...process.env,
|
|
47
|
+
NODE_ENV: 'production',
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
let errorOutput = '';
|
|
52
|
+
child.stderr.on('data', (data) => {
|
|
53
|
+
errorOutput += data.toString();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
child.on('close', (code) => {
|
|
57
|
+
if (code !== 0 && errorOutput.trim()) {
|
|
58
|
+
reject(new Error(errorOutput));
|
|
59
|
+
} else {
|
|
60
|
+
resolve();
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
child.on('error', (err) => {
|
|
65
|
+
reject(err);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
45
68
|
}
|
|
46
69
|
module.exports = { installExternalDependencies };
|
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-beta-
|
|
6
|
+
"version": "1.16.25-beta-bc165d9b",
|
|
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-beta-
|
|
23
|
-
"@abtnode/auth": "1.16.25-beta-
|
|
24
|
-
"@abtnode/certificate-manager": "1.16.25-beta-
|
|
25
|
-
"@abtnode/constant": "1.16.25-beta-
|
|
26
|
-
"@abtnode/cron": "1.16.25-beta-
|
|
27
|
-
"@abtnode/logger": "1.16.25-beta-
|
|
28
|
-
"@abtnode/models": "1.16.25-beta-
|
|
29
|
-
"@abtnode/queue": "1.16.25-beta-
|
|
30
|
-
"@abtnode/rbac": "1.16.25-beta-
|
|
31
|
-
"@abtnode/router-provider": "1.16.25-beta-
|
|
32
|
-
"@abtnode/static-server": "1.16.25-beta-
|
|
33
|
-
"@abtnode/timemachine": "1.16.25-beta-
|
|
34
|
-
"@abtnode/util": "1.16.25-beta-
|
|
22
|
+
"@abtnode/analytics": "1.16.25-beta-bc165d9b",
|
|
23
|
+
"@abtnode/auth": "1.16.25-beta-bc165d9b",
|
|
24
|
+
"@abtnode/certificate-manager": "1.16.25-beta-bc165d9b",
|
|
25
|
+
"@abtnode/constant": "1.16.25-beta-bc165d9b",
|
|
26
|
+
"@abtnode/cron": "1.16.25-beta-bc165d9b",
|
|
27
|
+
"@abtnode/logger": "1.16.25-beta-bc165d9b",
|
|
28
|
+
"@abtnode/models": "1.16.25-beta-bc165d9b",
|
|
29
|
+
"@abtnode/queue": "1.16.25-beta-bc165d9b",
|
|
30
|
+
"@abtnode/rbac": "1.16.25-beta-bc165d9b",
|
|
31
|
+
"@abtnode/router-provider": "1.16.25-beta-bc165d9b",
|
|
32
|
+
"@abtnode/static-server": "1.16.25-beta-bc165d9b",
|
|
33
|
+
"@abtnode/timemachine": "1.16.25-beta-bc165d9b",
|
|
34
|
+
"@abtnode/util": "1.16.25-beta-bc165d9b",
|
|
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-beta-
|
|
46
|
-
"@blocklet/env": "1.16.25-beta-
|
|
47
|
-
"@blocklet/meta": "1.16.25-beta-
|
|
48
|
-
"@blocklet/resolver": "1.16.25-beta-
|
|
49
|
-
"@blocklet/sdk": "1.16.25-beta-
|
|
50
|
-
"@did-space/client": "^0.3.
|
|
45
|
+
"@blocklet/constant": "1.16.25-beta-bc165d9b",
|
|
46
|
+
"@blocklet/env": "1.16.25-beta-bc165d9b",
|
|
47
|
+
"@blocklet/meta": "1.16.25-beta-bc165d9b",
|
|
48
|
+
"@blocklet/resolver": "1.16.25-beta-bc165d9b",
|
|
49
|
+
"@blocklet/sdk": "1.16.25-beta-bc165d9b",
|
|
50
|
+
"@did-space/client": "^0.3.73",
|
|
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": "a7895ed39e47c8c6bd10d32317804cc1904714ef"
|
|
106
106
|
}
|