@abtnode/core 1.7.18 → 1.7.21
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 +20 -0
- package/lib/api/team.js +40 -5
- package/lib/blocklet/extras.js +1 -5
- package/lib/blocklet/hooks.js +9 -9
- package/lib/blocklet/manager/disk.js +284 -215
- package/lib/blocklet/migration.js +1 -1
- package/lib/index.js +5 -2
- package/lib/migrations/1.7.20-blocklet-component.js +41 -0
- package/lib/router/helper.js +1 -1
- package/lib/router/index.js +1 -1
- package/lib/router/manager.js +24 -21
- package/lib/states/audit-log.js +26 -4
- package/lib/states/blocklet-extras.js +66 -159
- package/lib/states/blocklet.js +65 -62
- package/lib/states/node.js +23 -13
- package/lib/util/blocklet.js +160 -127
- package/lib/util/index.js +29 -0
- package/lib/validators/router.js +1 -2
- package/package.json +23 -22
package/lib/util/blocklet.js
CHANGED
|
@@ -5,7 +5,6 @@ const path = require('path');
|
|
|
5
5
|
const os = require('os');
|
|
6
6
|
const tar = require('tar');
|
|
7
7
|
const get = require('lodash/get');
|
|
8
|
-
const intersection = require('lodash/intersection');
|
|
9
8
|
const streamToPromise = require('stream-to-promise');
|
|
10
9
|
const { Throttle } = require('stream-throttle');
|
|
11
10
|
const ssri = require('ssri');
|
|
@@ -43,8 +42,19 @@ const validateBlockletEntry = require('@blocklet/meta/lib/entry');
|
|
|
43
42
|
const getBlockletEngine = require('@blocklet/meta/lib/engine');
|
|
44
43
|
const getBlockletInfo = require('@blocklet/meta/lib/info');
|
|
45
44
|
const { validateMeta, fixAndValidateService } = require('@blocklet/meta/lib/validate');
|
|
46
|
-
const {
|
|
45
|
+
const {
|
|
46
|
+
forEachBlocklet,
|
|
47
|
+
isFreeBlocklet,
|
|
48
|
+
getDisplayName,
|
|
49
|
+
findWebInterface,
|
|
50
|
+
forEachBlockletSync,
|
|
51
|
+
forEachChildSync,
|
|
52
|
+
isComponentBlocklet,
|
|
53
|
+
getSharedConfigObj,
|
|
54
|
+
getComponentName,
|
|
55
|
+
} = require('@blocklet/meta/lib/util');
|
|
47
56
|
const toBlockletDid = require('@blocklet/meta/lib/did');
|
|
57
|
+
const { titleSchema, descriptionSchema } = require('@blocklet/meta/lib/schema');
|
|
48
58
|
|
|
49
59
|
const { validate: validateEngine, get: getEngine } = require('../blocklet/manager/engine');
|
|
50
60
|
|
|
@@ -97,44 +107,36 @@ const PRIVATE_NODE_ENVS = [
|
|
|
97
107
|
|
|
98
108
|
/**
|
|
99
109
|
* @returns { dataDir, logsDir, cacheDir, appMain, appDir, mainDir, appCwd }
|
|
100
|
-
* dataDir: dataDirs.data/name (root
|
|
110
|
+
* dataDir: dataDirs.data/name (root component) or dataDirs.data/name/childName (child component)
|
|
101
111
|
* logsDir: dataDirs.log/name
|
|
102
|
-
* cacheDir: dataDirs.cache/name (root
|
|
103
|
-
* appDir:
|
|
104
|
-
* mainDir: appDir (dapp) or appDir/main (static). Used for for static
|
|
105
|
-
* appMain: app entry file or script (run appMain to start
|
|
112
|
+
* cacheDir: dataDirs.cache/name (root component) or dataDirs.cache/name/childName (child component)
|
|
113
|
+
* appDir: component bundle dir
|
|
114
|
+
* mainDir: appDir (dapp) or appDir/main (static). Used for for static component
|
|
115
|
+
* appMain: app entry file or script (run appMain to start component process)
|
|
106
116
|
* appCwd: cwd of appMain
|
|
107
117
|
*/
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
{
|
|
118
|
+
const getComponentDirs = (
|
|
119
|
+
component,
|
|
120
|
+
{ dataDirs, ensure = false, e2eMode = false, validate = true, ancestors = [] } = {}
|
|
111
121
|
) => {
|
|
112
|
-
if (!rootBlocklet) {
|
|
113
|
-
// eslint-disable-next-line no-param-reassign
|
|
114
|
-
rootBlocklet = blocklet;
|
|
115
|
-
}
|
|
116
122
|
// get data dirs
|
|
117
123
|
|
|
118
|
-
const { name } =
|
|
124
|
+
const { name: appName } = ancestors.concat(component)[0].meta;
|
|
125
|
+
const componentName = getComponentName(component, ancestors);
|
|
119
126
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (rootBlocklet !== blocklet) {
|
|
124
|
-
dataDir = path.join(dataDirs.data, rootBlocklet.meta.name, name);
|
|
125
|
-
cacheDir = path.join(dataDirs.cache, rootBlocklet.meta.name, name);
|
|
126
|
-
logsDir = path.join(dataDirs.logs, rootBlocklet.meta.name);
|
|
127
|
-
}
|
|
127
|
+
const logsDir = path.join(dataDirs.logs, appName);
|
|
128
|
+
const dataDir = path.join(dataDirs.data, componentName);
|
|
129
|
+
const cacheDir = path.join(dataDirs.cache, componentName);
|
|
128
130
|
|
|
129
131
|
// get app dirs
|
|
130
132
|
|
|
131
|
-
const { main, group } =
|
|
133
|
+
const { main, group } = component.meta;
|
|
132
134
|
|
|
133
135
|
let startFromDevEntry = '';
|
|
134
|
-
if (
|
|
135
|
-
startFromDevEntry =
|
|
136
|
-
if (e2eMode &&
|
|
137
|
-
startFromDevEntry =
|
|
136
|
+
if (component.mode === BLOCKLET_MODES.DEVELOPMENT && component.meta.scripts) {
|
|
137
|
+
startFromDevEntry = component.meta.scripts.dev;
|
|
138
|
+
if (e2eMode && component.meta.scripts.e2eDev) {
|
|
139
|
+
startFromDevEntry = component.meta.scripts.e2eDev;
|
|
138
140
|
}
|
|
139
141
|
}
|
|
140
142
|
|
|
@@ -145,10 +147,10 @@ const getBlockletDirs = (
|
|
|
145
147
|
let appDir = null;
|
|
146
148
|
let mainDir = null;
|
|
147
149
|
|
|
148
|
-
if (
|
|
149
|
-
appDir =
|
|
150
|
+
if (component.source === BlockletSource.local) {
|
|
151
|
+
appDir = component.deployedFrom;
|
|
150
152
|
} else {
|
|
151
|
-
appDir = getBundleDir(dataDirs.blocklets,
|
|
153
|
+
appDir = getBundleDir(dataDirs.blocklets, component.meta);
|
|
152
154
|
}
|
|
153
155
|
|
|
154
156
|
if (!appDir) {
|
|
@@ -168,9 +170,9 @@ const getBlockletDirs = (
|
|
|
168
170
|
|
|
169
171
|
mainDir = appDir;
|
|
170
172
|
|
|
171
|
-
if (validate && !startFromDevEntry && !isBeforeInstalled(
|
|
173
|
+
if (validate && !startFromDevEntry && !isBeforeInstalled(component.status)) {
|
|
172
174
|
try {
|
|
173
|
-
validateBlockletEntry(appDir,
|
|
175
|
+
validateBlockletEntry(appDir, component.meta);
|
|
174
176
|
} catch (err) {
|
|
175
177
|
throw new CustomError('BLOCKLET_CORRUPTED', err.message);
|
|
176
178
|
}
|
|
@@ -186,7 +188,7 @@ const getBlockletDirs = (
|
|
|
186
188
|
appMain = startFromDevEntry;
|
|
187
189
|
appCwd = appDir;
|
|
188
190
|
} else if (group === 'dapp') {
|
|
189
|
-
appMain = getBlockletEngine(
|
|
191
|
+
appMain = getBlockletEngine(component.meta).script || BLOCKLET_ENTRY_FILE;
|
|
190
192
|
appCwd = appDir;
|
|
191
193
|
} else if (group === 'static') {
|
|
192
194
|
mainDir = path.join(appDir, main);
|
|
@@ -200,11 +202,11 @@ const getBlockletDirs = (
|
|
|
200
202
|
};
|
|
201
203
|
|
|
202
204
|
/**
|
|
203
|
-
* set 'configs', configObj', 'environmentObj' to blocklet
|
|
205
|
+
* set 'configs', configObj', 'environmentObj' to blocklet TODO
|
|
204
206
|
* @param {*} blocklet
|
|
205
207
|
* @param {*} configs
|
|
206
208
|
*/
|
|
207
|
-
const fillBlockletConfigs = (blocklet, configs
|
|
209
|
+
const fillBlockletConfigs = (blocklet, configs) => {
|
|
208
210
|
blocklet.configs = configs || [];
|
|
209
211
|
blocklet.configObj = blocklet.configs.reduce((acc, x) => {
|
|
210
212
|
acc[x.key] = x.value;
|
|
@@ -214,13 +216,6 @@ const fillBlockletConfigs = (blocklet, configs, parent) => {
|
|
|
214
216
|
acc[x.key] = x.value;
|
|
215
217
|
return acc;
|
|
216
218
|
}, {});
|
|
217
|
-
|
|
218
|
-
const sharedConfigs = getSharedConfigs(blocklet, parent);
|
|
219
|
-
Object.keys(sharedConfigs).forEach((key) => {
|
|
220
|
-
blocklet.configObj[key] = sharedConfigs[key];
|
|
221
|
-
const item = blocklet.configs.find((x) => x.key === key);
|
|
222
|
-
item.value = sharedConfigs[key];
|
|
223
|
-
});
|
|
224
219
|
};
|
|
225
220
|
|
|
226
221
|
const ensureBlockletExpanded = async (meta, appDir) => {
|
|
@@ -243,7 +238,7 @@ const ensureBlockletExpanded = async (meta, appDir) => {
|
|
|
243
238
|
}
|
|
244
239
|
};
|
|
245
240
|
|
|
246
|
-
const
|
|
241
|
+
const getAppSystemEnvironments = (blocklet, nodeInfo) => {
|
|
247
242
|
const { did, name, title, description } = blocklet.meta;
|
|
248
243
|
const keys = Object.keys(BLOCKLET_CONFIGURABLE_KEY);
|
|
249
244
|
const result = getBlockletInfo(
|
|
@@ -273,7 +268,7 @@ const getRootSystemEnvironments = (blocklet, nodeInfo) => {
|
|
|
273
268
|
};
|
|
274
269
|
};
|
|
275
270
|
|
|
276
|
-
const
|
|
271
|
+
const getAppOverwrittenEnvironments = (blocklet, nodeInfo) => {
|
|
277
272
|
const result = {};
|
|
278
273
|
if (!blocklet || !blocklet.configObj) {
|
|
279
274
|
return result;
|
|
@@ -306,7 +301,7 @@ const getOverwrittenEnvironments = (blocklet, nodeInfo) => {
|
|
|
306
301
|
return result;
|
|
307
302
|
};
|
|
308
303
|
|
|
309
|
-
const
|
|
304
|
+
const getComponentSystemEnvironments = (blocklet) => {
|
|
310
305
|
const { port, ports } = blocklet;
|
|
311
306
|
const portEnvironments = {};
|
|
312
307
|
if (port) {
|
|
@@ -316,17 +311,17 @@ const getSystemEnvironments = (blocklet) => {
|
|
|
316
311
|
Object.assign(portEnvironments, ports);
|
|
317
312
|
}
|
|
318
313
|
return {
|
|
314
|
+
BLOCKLET_REAL_DID: blocklet.env.id,
|
|
319
315
|
BLOCKLET_DATA_DIR: blocklet.env.dataDir,
|
|
320
316
|
BLOCKLET_LOG_DIR: blocklet.env.logsDir,
|
|
321
317
|
BLOCKLET_CACHE_DIR: blocklet.env.cacheDir,
|
|
322
|
-
BLOCKLET_REAL_DID: blocklet.meta.did,
|
|
323
318
|
BLOCKLET_APP_DIR: blocklet.env.appDir,
|
|
324
319
|
BLOCKLET_MAIN_DIR: blocklet.env.mainDir,
|
|
325
320
|
...portEnvironments,
|
|
326
321
|
};
|
|
327
322
|
};
|
|
328
323
|
|
|
329
|
-
const getRuntimeEnvironments = (blocklet, nodeEnvironments,
|
|
324
|
+
const getRuntimeEnvironments = (blocklet, nodeEnvironments, ancestors) => {
|
|
330
325
|
// pm2 will force inject env variables of daemon process to blocklet process
|
|
331
326
|
// we can only rewrite these private env variables to empty
|
|
332
327
|
const safeNodeEnvironments = PRIVATE_NODE_ENVS.reduce((o, x) => {
|
|
@@ -335,30 +330,14 @@ const getRuntimeEnvironments = (blocklet, nodeEnvironments, parent) => {
|
|
|
335
330
|
}, {});
|
|
336
331
|
|
|
337
332
|
return {
|
|
338
|
-
...blocklet.environmentObj,
|
|
339
333
|
...blocklet.configObj,
|
|
340
|
-
...
|
|
334
|
+
...getSharedConfigObj(blocklet, ancestors),
|
|
335
|
+
...blocklet.environmentObj,
|
|
341
336
|
...nodeEnvironments,
|
|
342
337
|
...safeNodeEnvironments,
|
|
343
338
|
};
|
|
344
339
|
};
|
|
345
340
|
|
|
346
|
-
// we support share insecure same configs between parent and child blocklets
|
|
347
|
-
const getSharedConfigs = (child, parent) => {
|
|
348
|
-
const sharedConfigs = {};
|
|
349
|
-
if (parent && Array.isArray(parent.configs) && child.meta.did !== parent.meta.did) {
|
|
350
|
-
const parentKeys = parent.configs.filter((x) => x.secure === false && x.shared !== false).map((x) => x.key);
|
|
351
|
-
const childKeys = child.configs.map((x) => x.key);
|
|
352
|
-
const sharedKeys = intersection(parentKeys, childKeys);
|
|
353
|
-
sharedKeys.forEach((key) => {
|
|
354
|
-
if (!child.configObj[key]) {
|
|
355
|
-
sharedConfigs[key] = parent.configObj[key];
|
|
356
|
-
}
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
return sharedConfigs;
|
|
360
|
-
};
|
|
361
|
-
|
|
362
341
|
const isUsefulError = (err) => err && err.message !== 'process or namespace not found';
|
|
363
342
|
|
|
364
343
|
const getHealthyCheckTimeout = (blocklet, { checkHealthImmediately } = {}) => {
|
|
@@ -435,32 +414,40 @@ const getBlockletMetaFromUrls = async (urls) => {
|
|
|
435
414
|
* Start all precesses of a blocklet
|
|
436
415
|
* @param {*} blocklet should contain env props
|
|
437
416
|
*/
|
|
438
|
-
const startBlockletProcess = async (
|
|
417
|
+
const startBlockletProcess = async (
|
|
418
|
+
blocklet,
|
|
419
|
+
{ preStart = noop, nodeEnvironments, nodeInfo, e2eMode, skippedProcessIds = [] } = {}
|
|
420
|
+
) => {
|
|
439
421
|
if (!blocklet) {
|
|
440
422
|
throw new Error('blocklet should not be empty');
|
|
441
423
|
}
|
|
442
424
|
|
|
443
425
|
await forEachBlocklet(
|
|
444
426
|
blocklet,
|
|
445
|
-
async (b) => {
|
|
427
|
+
async (b, { ancestors }) => {
|
|
446
428
|
if (b.meta.group === BlockletGroup.gateway) {
|
|
447
429
|
return;
|
|
448
430
|
}
|
|
449
431
|
|
|
450
|
-
const { appMain,
|
|
432
|
+
const { appMain, processId, appCwd, logsDir } = b.env;
|
|
433
|
+
|
|
434
|
+
if (skippedProcessIds.includes(processId)) {
|
|
435
|
+
logger.info(`skip start process ${processId}`);
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
451
438
|
|
|
452
439
|
// get env
|
|
453
|
-
const env = getRuntimeEnvironments(b, nodeEnvironments);
|
|
440
|
+
const env = getRuntimeEnvironments(b, nodeEnvironments, ancestors);
|
|
454
441
|
|
|
455
442
|
// run hook
|
|
456
|
-
await preStart(b);
|
|
443
|
+
await preStart(b, { env });
|
|
457
444
|
|
|
458
445
|
// start process
|
|
459
446
|
const maxMemoryRestart = get(nodeInfo, 'runtimeConfig.blockletMaxMemoryLimit', BLOCKLET_MAX_MEM_LIMIT_IN_MB);
|
|
460
447
|
|
|
461
448
|
const options = {
|
|
462
449
|
namespace: 'blocklets',
|
|
463
|
-
name:
|
|
450
|
+
name: processId,
|
|
464
451
|
cwd: appCwd,
|
|
465
452
|
time: true,
|
|
466
453
|
output: path.join(logsDir, 'output.log'),
|
|
@@ -500,12 +487,12 @@ const startBlockletProcess = async (blocklet, { preStart = noop, nodeEnvironment
|
|
|
500
487
|
|
|
501
488
|
await pm2.startAsync(options);
|
|
502
489
|
|
|
503
|
-
const status = await getProcessState(
|
|
490
|
+
const status = await getProcessState(processId);
|
|
504
491
|
if (status === BlockletStatus.error) {
|
|
505
|
-
throw new Error(`${
|
|
492
|
+
throw new Error(`${processId} is not running within 5 seconds`);
|
|
506
493
|
}
|
|
507
494
|
|
|
508
|
-
logger.info('blocklet started', {
|
|
495
|
+
logger.info('blocklet started', { processId, status });
|
|
509
496
|
},
|
|
510
497
|
{ parallel: true }
|
|
511
498
|
);
|
|
@@ -515,12 +502,17 @@ const startBlockletProcess = async (blocklet, { preStart = noop, nodeEnvironment
|
|
|
515
502
|
* Stop all precesses of a blocklet
|
|
516
503
|
* @param {*} blocklet should contain env props
|
|
517
504
|
*/
|
|
518
|
-
const stopBlockletProcess = async (blocklet, { preStop = noop } = {}) => {
|
|
505
|
+
const stopBlockletProcess = async (blocklet, { preStop = noop, skippedProcessIds = [] } = {}) => {
|
|
519
506
|
await forEachBlocklet(
|
|
520
507
|
blocklet,
|
|
521
|
-
async (b) => {
|
|
522
|
-
|
|
523
|
-
|
|
508
|
+
async (b, { ancestors }) => {
|
|
509
|
+
if (skippedProcessIds.includes(b.env.processId)) {
|
|
510
|
+
logger.info(`skip stop process ${b.env.processId}`);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
await preStop(b, { ancestors });
|
|
515
|
+
await deleteProcess(b.env.processId);
|
|
524
516
|
},
|
|
525
517
|
{ parallel: true }
|
|
526
518
|
);
|
|
@@ -530,12 +522,17 @@ const stopBlockletProcess = async (blocklet, { preStop = noop } = {}) => {
|
|
|
530
522
|
* Delete all precesses of a blocklet
|
|
531
523
|
* @param {*} blocklet should contain env props
|
|
532
524
|
*/
|
|
533
|
-
const deleteBlockletProcess = async (blocklet, { preDelete = noop } = {}) => {
|
|
525
|
+
const deleteBlockletProcess = async (blocklet, { preDelete = noop, skippedProcessIds = [] } = {}) => {
|
|
534
526
|
await forEachBlocklet(
|
|
535
527
|
blocklet,
|
|
536
|
-
async (b) => {
|
|
537
|
-
|
|
538
|
-
|
|
528
|
+
async (b, { ancestors }) => {
|
|
529
|
+
if (skippedProcessIds.includes(b.env.processId)) {
|
|
530
|
+
logger.info(`skip delete process ${b.env.processId}`);
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
await preDelete(b, { ancestors });
|
|
535
|
+
await deleteProcess(b.env.processId);
|
|
539
536
|
},
|
|
540
537
|
{ parallel: true }
|
|
541
538
|
);
|
|
@@ -552,8 +549,8 @@ const reloadBlockletProcess = async (blocklet) =>
|
|
|
552
549
|
if (b.meta.group === BlockletGroup.gateway) {
|
|
553
550
|
return;
|
|
554
551
|
}
|
|
555
|
-
logger.info('reload process', {
|
|
556
|
-
await reloadProcess(b.env.
|
|
552
|
+
logger.info('reload process', { processId: b.env.processId });
|
|
553
|
+
await reloadProcess(b.env.processId);
|
|
557
554
|
},
|
|
558
555
|
{ parallel: false }
|
|
559
556
|
);
|
|
@@ -562,7 +559,7 @@ const getBlockletStatusFromProcess = async (blocklet) => {
|
|
|
562
559
|
const tasks = [];
|
|
563
560
|
await forEachBlocklet(blocklet, (b) => {
|
|
564
561
|
if (b.meta.group !== BlockletGroup.gateway) {
|
|
565
|
-
tasks.push(getProcessState(b.env.
|
|
562
|
+
tasks.push(getProcessState(b.env.processId));
|
|
566
563
|
}
|
|
567
564
|
});
|
|
568
565
|
|
|
@@ -592,11 +589,11 @@ const getRootBlockletStatus = (statusList = []) => {
|
|
|
592
589
|
};
|
|
593
590
|
|
|
594
591
|
/**
|
|
595
|
-
* @param {*}
|
|
592
|
+
* @param {*} processId
|
|
596
593
|
* @returns {BlockletStatus}
|
|
597
594
|
*/
|
|
598
|
-
const getProcessState = async (
|
|
599
|
-
const info = await getProcessInfo(
|
|
595
|
+
const getProcessState = async (processId) => {
|
|
596
|
+
const info = await getProcessInfo(processId);
|
|
600
597
|
if (!statusMap[info.pm2_env.status]) {
|
|
601
598
|
logger.error('Cannot find the blocklet status for pm2 status mapping', {
|
|
602
599
|
pm2Status: info.pm2_env.status,
|
|
@@ -608,9 +605,9 @@ const getProcessState = async (appId) => {
|
|
|
608
605
|
return statusMap[info.pm2_env.status];
|
|
609
606
|
};
|
|
610
607
|
|
|
611
|
-
const getProcessInfo = (
|
|
608
|
+
const getProcessInfo = (processId) =>
|
|
612
609
|
new Promise((resolve, reject) => {
|
|
613
|
-
pm2.describe(
|
|
610
|
+
pm2.describe(processId, async (err, [info]) => {
|
|
614
611
|
if (err) {
|
|
615
612
|
logger.error('Failed to get blocklet status from pm2', { error: err });
|
|
616
613
|
return reject(err);
|
|
@@ -624,10 +621,10 @@ const getProcessInfo = (appId) =>
|
|
|
624
621
|
});
|
|
625
622
|
});
|
|
626
623
|
|
|
627
|
-
const deleteProcess = (
|
|
624
|
+
const deleteProcess = (processId) =>
|
|
628
625
|
new Promise((resolve, reject) => {
|
|
629
626
|
pm2.connect(() => {
|
|
630
|
-
pm2.delete(
|
|
627
|
+
pm2.delete(processId, async (err) => {
|
|
631
628
|
if (isUsefulError(err)) {
|
|
632
629
|
logger.error('blocklet process delete failed', { error: err });
|
|
633
630
|
return reject(err);
|
|
@@ -637,9 +634,9 @@ const deleteProcess = (appId) =>
|
|
|
637
634
|
});
|
|
638
635
|
});
|
|
639
636
|
|
|
640
|
-
const reloadProcess = (
|
|
637
|
+
const reloadProcess = (processId) =>
|
|
641
638
|
new Promise((resolve, reject) => {
|
|
642
|
-
pm2.reload(
|
|
639
|
+
pm2.reload(processId, async (err) => {
|
|
643
640
|
if (err) {
|
|
644
641
|
if (isUsefulError(err)) {
|
|
645
642
|
logger.error('blocklet reload failed', { error: err });
|
|
@@ -696,7 +693,12 @@ const getSourceUrlsFromConfig = (config) => {
|
|
|
696
693
|
/**
|
|
697
694
|
* this function has side effect on children
|
|
698
695
|
*/
|
|
699
|
-
const parseChildrenFromMeta = async (src,
|
|
696
|
+
const parseChildrenFromMeta = async (src, context = {}) => {
|
|
697
|
+
const { dynamic, ancestors = [] } = context;
|
|
698
|
+
if (ancestors.length > 40) {
|
|
699
|
+
throw new Error('The depth of component should not exceed 40');
|
|
700
|
+
}
|
|
701
|
+
|
|
700
702
|
const configs = Array.isArray(src) ? src : src.children || [];
|
|
701
703
|
|
|
702
704
|
if (!configs || !configs.length) {
|
|
@@ -722,22 +724,42 @@ const parseChildrenFromMeta = async (src, { dynamic } = {}) => {
|
|
|
722
724
|
|
|
723
725
|
validateBlockletMeta(m, { ensureDist: true });
|
|
724
726
|
|
|
727
|
+
verifyPurchase(m, context);
|
|
728
|
+
|
|
729
|
+
if (!isComponentBlocklet(m)) {
|
|
730
|
+
throw new Error(`The blocklet cannot be a component: ${m.title}`);
|
|
731
|
+
}
|
|
732
|
+
|
|
725
733
|
const webInterface = findWebInterface(m);
|
|
726
734
|
if (!webInterface) {
|
|
727
735
|
throw new Error(`Web interface does not found in child ${config.name}`);
|
|
728
736
|
}
|
|
729
737
|
|
|
738
|
+
// validate mountPoint
|
|
730
739
|
const rule = webInterface.prefix;
|
|
731
|
-
if (rule !== BLOCKLET_DYNAMIC_PATH_PREFIX
|
|
732
|
-
|
|
740
|
+
if (rule !== BLOCKLET_DYNAMIC_PATH_PREFIX) {
|
|
741
|
+
const fullMountPoint = path.join(
|
|
742
|
+
ancestors.map((x) => x.mountPoint || ''),
|
|
743
|
+
mountPoint
|
|
744
|
+
);
|
|
745
|
+
if (normalizePathPrefix(rule) !== normalizePathPrefix(fullMountPoint)) {
|
|
746
|
+
throw new Error(`Prefix does not match in child ${config.name}. expected: ${rule}, resolved: ${mountPoint}`);
|
|
747
|
+
}
|
|
733
748
|
}
|
|
734
749
|
|
|
735
750
|
const meta = ensureMeta(m, { name: config.name });
|
|
751
|
+
|
|
752
|
+
// check circular dependencies
|
|
753
|
+
if (ancestors.map((x) => x.meta?.bundleDid).indexOf(meta.bundleDid) > -1) {
|
|
754
|
+
throw new Error('Blocklet components have circular dependencies');
|
|
755
|
+
}
|
|
756
|
+
|
|
736
757
|
if (config.title) {
|
|
737
758
|
meta.title = config.title;
|
|
759
|
+
meta.title = await titleSchema.validateAsync(config.title);
|
|
738
760
|
}
|
|
739
761
|
if (config.description) {
|
|
740
|
-
meta.description = config.description;
|
|
762
|
+
meta.description = await descriptionSchema.validateAsync(config.description);
|
|
741
763
|
}
|
|
742
764
|
|
|
743
765
|
const child = {
|
|
@@ -746,6 +768,12 @@ const parseChildrenFromMeta = async (src, { dynamic } = {}) => {
|
|
|
746
768
|
bundleSource: config.source || { url: config.resolved },
|
|
747
769
|
};
|
|
748
770
|
|
|
771
|
+
child.children = await parseChildrenFromMeta(meta, {
|
|
772
|
+
...context,
|
|
773
|
+
ancestors: [...ancestors, { meta, mountPoint }],
|
|
774
|
+
dynamic: false,
|
|
775
|
+
});
|
|
776
|
+
|
|
749
777
|
children.push(child);
|
|
750
778
|
}
|
|
751
779
|
|
|
@@ -775,7 +803,7 @@ const checkBlockletProcessHealthy = async (blocklet, { minConsecutiveTime, timeo
|
|
|
775
803
|
const _checkProcessHealthy = async (blocklet, { minConsecutiveTime, timeout }) => {
|
|
776
804
|
const { meta, ports, env } = blocklet;
|
|
777
805
|
const { name } = meta;
|
|
778
|
-
const {
|
|
806
|
+
const { processId } = env;
|
|
779
807
|
|
|
780
808
|
const webInterface = (meta.interfaces || []).find((x) => x.type === BLOCKLET_INTERFACE_TYPE_WEB);
|
|
781
809
|
if (!webInterface) {
|
|
@@ -787,10 +815,10 @@ const _checkProcessHealthy = async (blocklet, { minConsecutiveTime, timeout }) =
|
|
|
787
815
|
// ensure pm2 status is 'online'
|
|
788
816
|
const getStatus = async () => {
|
|
789
817
|
try {
|
|
790
|
-
const info = await getProcessInfo(
|
|
818
|
+
const info = await getProcessInfo(processId);
|
|
791
819
|
return info.pm2_env.status;
|
|
792
820
|
} catch (err) {
|
|
793
|
-
logger.error('blocklet checkStart error', { error: err,
|
|
821
|
+
logger.error('blocklet checkStart error', { error: err, processId, name });
|
|
794
822
|
return '';
|
|
795
823
|
}
|
|
796
824
|
};
|
|
@@ -819,7 +847,7 @@ const _checkProcessHealthy = async (blocklet, { minConsecutiveTime, timeout }) =
|
|
|
819
847
|
throw error;
|
|
820
848
|
}
|
|
821
849
|
} catch (error) {
|
|
822
|
-
logger.error('start blocklet failed', {
|
|
850
|
+
logger.error('start blocklet failed', { processId, name });
|
|
823
851
|
throw error;
|
|
824
852
|
}
|
|
825
853
|
};
|
|
@@ -870,15 +898,16 @@ const pruneBlockletBundle = async ({ blocklets, installDir, blockletSettings })
|
|
|
870
898
|
// blockletMap: { <[scope/]name/version>: true }
|
|
871
899
|
const blockletMap = {};
|
|
872
900
|
for (const blocklet of blocklets) {
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
}
|
|
901
|
+
forEachBlockletSync(blocklet, (component) => {
|
|
902
|
+
blockletMap[`${component.meta.bundleName}/${component.meta.version}`] = true;
|
|
903
|
+
});
|
|
877
904
|
}
|
|
878
905
|
for (const setting of blockletSettings) {
|
|
879
906
|
for (const child of setting.children || []) {
|
|
880
907
|
if (child.status !== BlockletStatus.deleted) {
|
|
881
|
-
|
|
908
|
+
forEachBlockletSync(child, (component) => {
|
|
909
|
+
blockletMap[`${component.meta.bundleName}/${component.meta.version}`] = true;
|
|
910
|
+
});
|
|
882
911
|
}
|
|
883
912
|
}
|
|
884
913
|
}
|
|
@@ -993,8 +1022,8 @@ const getDiskInfo = async (blocklet, { useFakeDiskInfo } = {}) => {
|
|
|
993
1022
|
}
|
|
994
1023
|
};
|
|
995
1024
|
|
|
996
|
-
const getRuntimeInfo = async (
|
|
997
|
-
const proc = await getProcessInfo(
|
|
1025
|
+
const getRuntimeInfo = async (processId) => {
|
|
1026
|
+
const proc = await getProcessInfo(processId);
|
|
998
1027
|
return {
|
|
999
1028
|
pid: proc.pid,
|
|
1000
1029
|
uptime: proc.pm2_env ? +new Date() - Number(proc.pm2_env.pm_uptime) : 0,
|
|
@@ -1084,15 +1113,23 @@ const fixAndVerifyMetaFromStore = (meta) => {
|
|
|
1084
1113
|
return fixAndValidateService(meta);
|
|
1085
1114
|
};
|
|
1086
1115
|
|
|
1087
|
-
const getUpdateMetaList = (
|
|
1088
|
-
const oldMap =
|
|
1089
|
-
|
|
1090
|
-
|
|
1116
|
+
const getUpdateMetaList = (oldBlocklet = {}, newBlocklet = {}) => {
|
|
1117
|
+
const oldMap = {};
|
|
1118
|
+
forEachChildSync(oldBlocklet, (b, { id }) => {
|
|
1119
|
+
if (b.bundleSource) {
|
|
1120
|
+
oldMap[id] = b.meta.version;
|
|
1091
1121
|
}
|
|
1092
|
-
|
|
1093
|
-
|
|
1122
|
+
});
|
|
1123
|
+
|
|
1124
|
+
const res = [];
|
|
1094
1125
|
|
|
1095
|
-
|
|
1126
|
+
forEachChildSync(newBlocklet, (b, { id }) => {
|
|
1127
|
+
if (b.bundleSource && b.meta.version !== oldMap[id]) {
|
|
1128
|
+
res.push({ id, meta: b.meta });
|
|
1129
|
+
}
|
|
1130
|
+
});
|
|
1131
|
+
|
|
1132
|
+
return res;
|
|
1096
1133
|
};
|
|
1097
1134
|
|
|
1098
1135
|
const getSourceFromInstallParams = (params) => {
|
|
@@ -1131,7 +1168,7 @@ const checkDuplicateComponents = (components = []) => {
|
|
|
1131
1168
|
(item, index) => components.findIndex((x) => x.mountPoint === item.mountPoint) !== index
|
|
1132
1169
|
);
|
|
1133
1170
|
if (duplicateMountPoints.length) {
|
|
1134
|
-
throw new Error(`
|
|
1171
|
+
throw new Error(`mount point must be unique: ${duplicateMountPoints.map((x) => x.mountPoint).join(', ')}`);
|
|
1135
1172
|
}
|
|
1136
1173
|
};
|
|
1137
1174
|
|
|
@@ -1187,11 +1224,7 @@ const needBlockletDownload = (blocklet, oldBlocklet) => {
|
|
|
1187
1224
|
return true;
|
|
1188
1225
|
}
|
|
1189
1226
|
|
|
1190
|
-
|
|
1191
|
-
return false;
|
|
1192
|
-
}
|
|
1193
|
-
|
|
1194
|
-
return true;
|
|
1227
|
+
return !(get(oldBlocklet, 'meta.dist.integrity') === get(blocklet, 'meta.dist.integrity'));
|
|
1195
1228
|
};
|
|
1196
1229
|
|
|
1197
1230
|
const verifyPurchase = (meta, opts = {}) => {
|
|
@@ -1252,10 +1285,10 @@ module.exports = {
|
|
|
1252
1285
|
getBlockletMetaFromUrl,
|
|
1253
1286
|
parseChildrenFromMeta,
|
|
1254
1287
|
parseChildren,
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1288
|
+
getComponentDirs,
|
|
1289
|
+
getAppSystemEnvironments,
|
|
1290
|
+
getAppOverwrittenEnvironments,
|
|
1291
|
+
getComponentSystemEnvironments,
|
|
1259
1292
|
getRuntimeEnvironments,
|
|
1260
1293
|
validateBlocklet,
|
|
1261
1294
|
fillBlockletConfigs,
|
package/lib/util/index.js
CHANGED
|
@@ -14,6 +14,7 @@ const { Certificate } = require('@fidm/x509');
|
|
|
14
14
|
const getPortLib = require('get-port');
|
|
15
15
|
const v8 = require('v8');
|
|
16
16
|
const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
|
|
17
|
+
const axios = require('@abtnode/util/lib/axios');
|
|
17
18
|
const parseBlockletMeta = require('@blocklet/meta/lib/parse');
|
|
18
19
|
const { validateMeta, fixAndValidateService } = require('@blocklet/meta/lib/validate');
|
|
19
20
|
const { BlockletStatus, BLOCKLET_INTERFACE_WELLKNOWN } = require('@blocklet/meta/lib/constants');
|
|
@@ -503,6 +504,33 @@ const getStateCrons = (states) => [
|
|
|
503
504
|
},
|
|
504
505
|
];
|
|
505
506
|
|
|
507
|
+
const getDelegateState = async (chainHost, address) => {
|
|
508
|
+
const result = await axios.post(
|
|
509
|
+
joinUrl(chainHost, '/gql/'),
|
|
510
|
+
JSON.stringify({
|
|
511
|
+
query: `{
|
|
512
|
+
getDelegateState(address: "${address}") {
|
|
513
|
+
state {
|
|
514
|
+
address
|
|
515
|
+
ops {
|
|
516
|
+
key
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}`,
|
|
521
|
+
}),
|
|
522
|
+
{
|
|
523
|
+
headers: {
|
|
524
|
+
'Content-Type': 'application/json',
|
|
525
|
+
Accept: 'application/json',
|
|
526
|
+
},
|
|
527
|
+
timeout: 60 * 1000,
|
|
528
|
+
}
|
|
529
|
+
);
|
|
530
|
+
|
|
531
|
+
return get(result.data, 'data.getDelegateState.state');
|
|
532
|
+
};
|
|
533
|
+
|
|
506
534
|
const lib = {
|
|
507
535
|
validateOwner,
|
|
508
536
|
getProviderFromNodeInfo,
|
|
@@ -541,6 +569,7 @@ const lib = {
|
|
|
541
569
|
getSafeEnv,
|
|
542
570
|
memoizeAsync,
|
|
543
571
|
getStateCrons,
|
|
572
|
+
getDelegateState,
|
|
544
573
|
};
|
|
545
574
|
|
|
546
575
|
module.exports = lib;
|
package/lib/validators/router.js
CHANGED
|
@@ -44,7 +44,6 @@ const ruleSchema = {
|
|
|
44
44
|
)
|
|
45
45
|
.required(),
|
|
46
46
|
did: Joi.string().label('did').when('type', { is: ROUTING_RULE_TYPES.BLOCKLET, then: Joi.required() }), // root blocklet did
|
|
47
|
-
realDid: Joi.string().label('real did'), // child blocklet did
|
|
48
47
|
port: Joi.number().label('port').port().when('type', { is: ROUTING_RULE_TYPES.BLOCKLET, then: Joi.required() }),
|
|
49
48
|
url: Joi.string().label('url').when('type', { is: ROUTING_RULE_TYPES.REDIRECT, then: Joi.required() }),
|
|
50
49
|
redirectCode: Joi.alternatives()
|
|
@@ -54,13 +53,13 @@ const ruleSchema = {
|
|
|
54
53
|
interfaceName: Joi.string() // root interface
|
|
55
54
|
.label('interface name')
|
|
56
55
|
.when('type', { is: ROUTING_RULE_TYPES.BLOCKLET, then: Joi.required() }),
|
|
57
|
-
realInterfaceName: Joi.string().label('real interface name'), // child blocklet interface
|
|
58
56
|
response: Joi.object({ status: Joi.number().required(), contentType: Joi.string(), body: Joi.string().required() })
|
|
59
57
|
.label('response')
|
|
60
58
|
.when('type', {
|
|
61
59
|
is: ROUTING_RULE_TYPES.DIRECT_RESPONSE,
|
|
62
60
|
then: Joi.required(),
|
|
63
61
|
}),
|
|
62
|
+
componentId: Joi.string().label('component id'), // component global id
|
|
64
63
|
},
|
|
65
64
|
|
|
66
65
|
// List of services that manipulate the request before the upstream blocklet
|