@abtnode/core 1.8.31 → 1.8.32
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 +3 -3
- package/lib/api/team.js +22 -4
- package/lib/blocklet/manager/disk.js +265 -171
- package/lib/index.js +5 -7
- package/lib/states/README.md +3 -0
- package/lib/states/blocklet.js +29 -23
- package/lib/states/user.js +16 -0
- package/lib/util/requirement.js +26 -10
- package/lib/util/{registry.js → store.js} +51 -0
- package/lib/validators/blocklet.js +15 -0
- package/package.json +25 -25
- package/lib/blocklet/registry.js +0 -74
package/lib/api/node.js
CHANGED
|
@@ -13,8 +13,8 @@ const logger = require('@abtnode/logger')('@abtnode/core:api:node');
|
|
|
13
13
|
|
|
14
14
|
const IP = require('../util/ip');
|
|
15
15
|
const { validateNodeInfo, validateUpdateGateway } = require('../validators/node');
|
|
16
|
-
const BlockletRegistry = require('../blocklet/registry');
|
|
17
16
|
const { getAll } = require('../blocklet/manager/engine');
|
|
17
|
+
const StoreUtil = require('../util/store');
|
|
18
18
|
const { getDelegateState } = require('../util');
|
|
19
19
|
|
|
20
20
|
const sanitizeUrl = (url) => {
|
|
@@ -59,9 +59,9 @@ class NodeAPI {
|
|
|
59
59
|
throw new Error(`Blocklet registry already exist: ${sanitized}`);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
await
|
|
62
|
+
await StoreUtil.validateRegistryURL(sanitized);
|
|
63
63
|
|
|
64
|
-
const newBlockletRegistry = await
|
|
64
|
+
const newBlockletRegistry = await StoreUtil.getRegistryMeta(sanitized);
|
|
65
65
|
const newBlockletRegistryList = info.blockletRegistryList.map((x) => ({ ...x, selected: false }));
|
|
66
66
|
newBlockletRegistryList.push({ ...newBlockletRegistry, url: sanitized, selected: true, protected: false });
|
|
67
67
|
|
package/lib/api/team.js
CHANGED
|
@@ -10,6 +10,7 @@ const {
|
|
|
10
10
|
WHO_CAN_ACCESS,
|
|
11
11
|
PASSPORT_STATUS,
|
|
12
12
|
WELLKNOWN_SERVICE_PATH_PREFIX,
|
|
13
|
+
MAX_USER_PAGE_SIZE,
|
|
13
14
|
} = require('@abtnode/constant');
|
|
14
15
|
const { isValid: isValidDid } = require('@arcblock/did');
|
|
15
16
|
const { BlockletEvents } = require('@blocklet/constant');
|
|
@@ -30,8 +31,6 @@ const { validateCreatePermission, validateUpdatePermission } = require('../valid
|
|
|
30
31
|
|
|
31
32
|
const { getBlocklet } = require('../util/blocklet');
|
|
32
33
|
|
|
33
|
-
const MAX_USER_PAGE_SIZE = 100;
|
|
34
|
-
|
|
35
34
|
const validateReservedRole = (role) => {
|
|
36
35
|
if (Object.values(ROLES).includes(role)) {
|
|
37
36
|
throw new Error(`The role ${role} is reserved`);
|
|
@@ -126,14 +125,33 @@ class TeamAPI extends EventEmitter {
|
|
|
126
125
|
return doc;
|
|
127
126
|
}
|
|
128
127
|
|
|
129
|
-
async getUsers({ teamDid, query, paging: inputPaging, sort }) {
|
|
128
|
+
async getUsers({ teamDid, query, paging: inputPaging, sort, dids }) {
|
|
130
129
|
const state = await this.getUserState(teamDid);
|
|
131
130
|
|
|
132
131
|
if (inputPaging?.pageSize > MAX_USER_PAGE_SIZE) {
|
|
133
132
|
throw new Error(`Length of users should not exceed ${MAX_USER_PAGE_SIZE} per page`);
|
|
134
133
|
}
|
|
135
134
|
|
|
136
|
-
|
|
135
|
+
if (dids && dids.length > MAX_USER_PAGE_SIZE) {
|
|
136
|
+
throw new Error(`Length of users should not exceed ${MAX_USER_PAGE_SIZE}`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
let list;
|
|
140
|
+
let paging;
|
|
141
|
+
|
|
142
|
+
if (dids) {
|
|
143
|
+
list = await state.getUsersByDids({ query, dids });
|
|
144
|
+
paging = {
|
|
145
|
+
total: list.length,
|
|
146
|
+
pageSize: dids.length,
|
|
147
|
+
pageCount: 1,
|
|
148
|
+
page: 1,
|
|
149
|
+
};
|
|
150
|
+
} else {
|
|
151
|
+
const doc = await state.getUsers({ query, sort, paging: { pageSize: 20, ...inputPaging } });
|
|
152
|
+
list = doc.list;
|
|
153
|
+
paging = doc.paging;
|
|
154
|
+
}
|
|
137
155
|
|
|
138
156
|
return {
|
|
139
157
|
users: list.map(
|
|
@@ -23,7 +23,7 @@ const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager');
|
|
|
23
23
|
const downloadFile = require('@abtnode/util/lib/download-file');
|
|
24
24
|
const Lock = require('@abtnode/util/lib/lock');
|
|
25
25
|
const { getVcFromPresentation } = require('@abtnode/util/lib/vc');
|
|
26
|
-
const { VC_TYPE_BLOCKLET_PURCHASE, WHO_CAN_ACCESS } = require('@abtnode/constant');
|
|
26
|
+
const { VC_TYPE_BLOCKLET_PURCHASE, WHO_CAN_ACCESS, SERVER_ROLES } = require('@abtnode/constant');
|
|
27
27
|
|
|
28
28
|
const getBlockletEngine = require('@blocklet/meta/lib/engine');
|
|
29
29
|
const {
|
|
@@ -38,6 +38,7 @@ const {
|
|
|
38
38
|
forEachChild,
|
|
39
39
|
getComponentId,
|
|
40
40
|
getComponentBundleId,
|
|
41
|
+
isExternalBlocklet,
|
|
41
42
|
} = require('@blocklet/meta/lib/util');
|
|
42
43
|
const getComponentProcessId = require('@blocklet/meta/lib/get-component-process-id');
|
|
43
44
|
const validateBlockletEntry = require('@blocklet/meta/lib/entry');
|
|
@@ -47,6 +48,8 @@ const { update: updateMetaFile } = require('@blocklet/meta/lib/file');
|
|
|
47
48
|
const { titleSchema, descriptionSchema, mountPointSchema, logoSchema } = require('@blocklet/meta/lib/schema');
|
|
48
49
|
const hasReservedKey = require('@blocklet/meta/lib/has-reserved-key');
|
|
49
50
|
|
|
51
|
+
const { toExternalBlocklet } = toBlockletDid;
|
|
52
|
+
|
|
50
53
|
const {
|
|
51
54
|
BlockletStatus,
|
|
52
55
|
BlockletSource,
|
|
@@ -105,9 +108,8 @@ const {
|
|
|
105
108
|
getBlocklet,
|
|
106
109
|
ensureEnvDefault,
|
|
107
110
|
} = require('../../util/blocklet');
|
|
108
|
-
const
|
|
111
|
+
const StoreUtil = require('../../util/store');
|
|
109
112
|
const states = require('../../states');
|
|
110
|
-
const BlockletRegistry = require('../registry');
|
|
111
113
|
const BaseBlockletManager = require('./base');
|
|
112
114
|
const { get: getEngine } = require('./engine');
|
|
113
115
|
const blockletPm2Events = require('./pm2-events');
|
|
@@ -169,11 +171,14 @@ const getSkippedProcessIds = ({ newBlocklet, oldBlocklet, context = {} }) => {
|
|
|
169
171
|
return res;
|
|
170
172
|
};
|
|
171
173
|
|
|
174
|
+
const getBlockletIndex = (meta, controller) =>
|
|
175
|
+
controller ? toExternalBlocklet(meta.name, controller.id) : { did: meta.did, name: meta.name };
|
|
176
|
+
|
|
172
177
|
class BlockletManager extends BaseBlockletManager {
|
|
173
178
|
/**
|
|
174
179
|
* @param {*} dataDirs generate by ../../util:getDataDirs
|
|
175
180
|
*/
|
|
176
|
-
constructor({ dataDirs,
|
|
181
|
+
constructor({ dataDirs, startQueue, installQueue, daemon = false, teamManager }) {
|
|
177
182
|
super();
|
|
178
183
|
|
|
179
184
|
this.dataDirs = dataDirs;
|
|
@@ -190,7 +195,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
190
195
|
* { [download-did-version]: Lock }
|
|
191
196
|
*/
|
|
192
197
|
this.downloadLocks = {};
|
|
193
|
-
this.registry = registry;
|
|
194
198
|
|
|
195
199
|
// cached installed blocklets for performance
|
|
196
200
|
this.cachedBlocklets = null;
|
|
@@ -198,7 +202,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
198
202
|
// cached blocklet latest versions from each registries
|
|
199
203
|
this.cachedBlockletVersions = new LRU({
|
|
200
204
|
max: 40, // cache at most 40 blocklets
|
|
201
|
-
maxAge: process.env.NODE_ENV === 'test' ? 500 : 5 * 60 * 1000, // cache for 5 minute
|
|
205
|
+
maxAge: process.env.NODE_ENV === 'test' ? 500 : 0.5 * 60 * 1000, // cache for 0.5 minute
|
|
202
206
|
});
|
|
203
207
|
|
|
204
208
|
if (daemon) {
|
|
@@ -216,8 +220,17 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
216
220
|
*
|
|
217
221
|
* @param {{
|
|
218
222
|
* url: string;
|
|
219
|
-
*
|
|
220
|
-
*
|
|
223
|
+
* file: string;
|
|
224
|
+
* diffVersion: string;
|
|
225
|
+
* deleteSet: Array<string>;
|
|
226
|
+
* did: string;
|
|
227
|
+
* title: string;
|
|
228
|
+
* description: string;
|
|
229
|
+
* sync: boolean = false; // download synchronously, not use queue
|
|
230
|
+
* delay: number; // push download task to queue after a delay
|
|
231
|
+
* downloadTokenList: Array<{did: string, token: string}>;
|
|
232
|
+
* startImmediately: boolean;
|
|
233
|
+
* controller: Controller
|
|
221
234
|
* }} params
|
|
222
235
|
* @param {{
|
|
223
236
|
* [key: string]: any
|
|
@@ -245,7 +258,8 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
245
258
|
}
|
|
246
259
|
|
|
247
260
|
if (source === BlockletSource.url) {
|
|
248
|
-
|
|
261
|
+
const { url, controller, sync, delay } = params;
|
|
262
|
+
return this._installFromUrl({ url, controller, sync, delay }, context);
|
|
249
263
|
}
|
|
250
264
|
|
|
251
265
|
if (source === BlockletSource.upload) {
|
|
@@ -254,7 +268,8 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
254
268
|
}
|
|
255
269
|
|
|
256
270
|
if (source === BlockletSource.registry) {
|
|
257
|
-
|
|
271
|
+
const { did, controller, sync, delay } = params;
|
|
272
|
+
return this._installFromStore({ did, controller, sync, delay }, context);
|
|
258
273
|
}
|
|
259
274
|
|
|
260
275
|
if (source === BlockletSource.custom) {
|
|
@@ -343,7 +358,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
343
358
|
|
|
344
359
|
if (checkPrice && !isFree && meta.nftFactory) {
|
|
345
360
|
try {
|
|
346
|
-
const registryMeta = await
|
|
361
|
+
const registryMeta = await StoreUtil.getRegistryMeta(new URL(url).origin);
|
|
347
362
|
|
|
348
363
|
if (registryMeta.chainHost) {
|
|
349
364
|
const state = await getFactoryState(registryMeta.chainHost, meta.nftFactory);
|
|
@@ -356,12 +371,33 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
356
371
|
}
|
|
357
372
|
}
|
|
358
373
|
|
|
359
|
-
const { inStore, registryUrl } = await parseSourceUrl(url);
|
|
374
|
+
const { inStore, registryUrl } = await StoreUtil.parseSourceUrl(url);
|
|
375
|
+
|
|
376
|
+
return { meta, isFree, inStore, registryUrl };
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
async getBlockletByBundle({ did, name }, context) {
|
|
380
|
+
if (toBlockletDid(name) !== did) {
|
|
381
|
+
throw new Error('did and name does not match');
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (!context?.user) {
|
|
385
|
+
throw new Error('user does not exist');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
let blockletDid = did;
|
|
389
|
+
let isExternal = false;
|
|
390
|
+
|
|
391
|
+
if (context.user.role === SERVER_ROLES.EXTERNAL_BLOCKLET_CONTROLLER) {
|
|
392
|
+
blockletDid = toExternalBlocklet(name, context.user.did, { didOnly: true });
|
|
393
|
+
isExternal = true;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const blocklet = await states.blocklet.getBlocklet(blockletDid);
|
|
360
397
|
|
|
361
|
-
const blocklet = await states.blocklet.getBlocklet(meta.did);
|
|
362
398
|
const isRunning = blocklet ? blocklet.status === BlockletStatus.running : false;
|
|
363
399
|
|
|
364
|
-
return {
|
|
400
|
+
return { isInstalled: !!blocklet, isRunning, blockletDid, isExternal };
|
|
365
401
|
}
|
|
366
402
|
|
|
367
403
|
async installBlockletFromVc({ vcPresentation, challenge }, context) {
|
|
@@ -388,6 +424,15 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
388
424
|
const blocklet = await this.ensureBlocklet(did, { e2eMode });
|
|
389
425
|
|
|
390
426
|
try {
|
|
427
|
+
// blocklet may be manually stopped durning starting
|
|
428
|
+
// so error message would not be sent if blocklet is stopped
|
|
429
|
+
// so we need update status first
|
|
430
|
+
await states.blocklet.setBlockletStatus(did, BlockletStatus.starting);
|
|
431
|
+
blocklet.status = BlockletStatus.starting;
|
|
432
|
+
|
|
433
|
+
// validate requirement and engine
|
|
434
|
+
await validateBlocklet(blocklet);
|
|
435
|
+
|
|
391
436
|
if (!hasRunnableComponent(blocklet)) {
|
|
392
437
|
throw new Error('No runnable component found');
|
|
393
438
|
}
|
|
@@ -400,9 +445,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
400
445
|
);
|
|
401
446
|
}
|
|
402
447
|
|
|
403
|
-
// update status
|
|
404
|
-
await states.blocklet.setBlockletStatus(did, BlockletStatus.starting);
|
|
405
|
-
blocklet.status = BlockletStatus.starting;
|
|
406
448
|
this.emit(BlockletEvents.statusChange, blocklet);
|
|
407
449
|
|
|
408
450
|
if (blocklet.mode === BLOCKLET_MODES.DEVELOPMENT) {
|
|
@@ -811,36 +853,46 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
811
853
|
return this.attachRuntimeInfo({ did, nodeInfo, diskInfo: true, context });
|
|
812
854
|
}
|
|
813
855
|
|
|
814
|
-
async list({ includeRuntimeInfo = true, useCache = true } = {}, context) {
|
|
856
|
+
async list({ includeRuntimeInfo = true, useCache = true, filter } = {}, context) {
|
|
815
857
|
const blocklets = await states.blocklet.getBlocklets();
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
858
|
+
let list = blocklets;
|
|
859
|
+
|
|
860
|
+
if (includeRuntimeInfo) {
|
|
861
|
+
const nodeInfo = await states.node.read();
|
|
862
|
+
const updated = (
|
|
863
|
+
await Promise.all(
|
|
864
|
+
blocklets.map((x) => {
|
|
865
|
+
if (isBeforeInstalled(x.status)) {
|
|
866
|
+
return x;
|
|
867
|
+
}
|
|
819
868
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
await Promise.all(
|
|
823
|
-
blocklets.map((x) => {
|
|
824
|
-
if (isBeforeInstalled(x.status)) {
|
|
825
|
-
return x;
|
|
826
|
-
}
|
|
869
|
+
const cachedBlocklet =
|
|
870
|
+
useCache && this.cachedBlocklets ? this.cachedBlocklets.find((y) => y.meta.did === x.meta.did) : null;
|
|
827
871
|
|
|
828
|
-
|
|
829
|
-
|
|
872
|
+
return this.attachRuntimeInfo({
|
|
873
|
+
did: x.meta.did,
|
|
874
|
+
nodeInfo,
|
|
875
|
+
diskInfo: false,
|
|
876
|
+
context,
|
|
877
|
+
cachedBlocklet,
|
|
878
|
+
});
|
|
879
|
+
})
|
|
880
|
+
)
|
|
881
|
+
).filter(Boolean);
|
|
830
882
|
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
diskInfo: false,
|
|
835
|
-
context,
|
|
836
|
-
cachedBlocklet,
|
|
837
|
-
});
|
|
838
|
-
})
|
|
839
|
-
)
|
|
840
|
-
).filter(Boolean);
|
|
883
|
+
this.cachedBlocklets = cloneDeep(updated);
|
|
884
|
+
list = updated;
|
|
885
|
+
}
|
|
841
886
|
|
|
842
|
-
|
|
843
|
-
|
|
887
|
+
if (filter === 'external-excluded') {
|
|
888
|
+
return list.filter((x) => !isExternalBlocklet(x));
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
if (filter === 'external-only') {
|
|
892
|
+
return list.filter(isExternalBlocklet);
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
return list;
|
|
844
896
|
}
|
|
845
897
|
|
|
846
898
|
// eslint-disable-next-line no-unused-vars
|
|
@@ -1067,13 +1119,11 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1067
1119
|
|
|
1068
1120
|
const upgradeFromRegistry = registryUrl || blocklet.deployedFrom;
|
|
1069
1121
|
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
context
|
|
1076
|
-
);
|
|
1122
|
+
let newVersionMeta = await StoreUtil.getBlockletMeta({
|
|
1123
|
+
did: blocklet.meta.bundleDid,
|
|
1124
|
+
registryUrl: upgradeFromRegistry,
|
|
1125
|
+
});
|
|
1126
|
+
newVersionMeta = ensureMeta(newVersionMeta, { name: blocklet.meta.name, did: blocklet.meta.did });
|
|
1077
1127
|
|
|
1078
1128
|
function getSignature(signatures = [], oldSignatures = []) {
|
|
1079
1129
|
// if blocklet installed from local, upload, url, the signature is undefined, should return null
|
|
@@ -1359,13 +1409,24 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1359
1409
|
|
|
1360
1410
|
const oldBlocklet = { children: children.filter((x) => x.dynamic) }; // let downloader skip re-downloading dynamic blocklet
|
|
1361
1411
|
await this._downloadBlocklet(added, oldBlocklet);
|
|
1362
|
-
await states.blocklet.setBlockletStatus(did, BlockletStatus.installed);
|
|
1363
1412
|
|
|
1364
|
-
// Add
|
|
1413
|
+
// Add Config
|
|
1365
1414
|
await this._setConfigsFromMeta(did);
|
|
1415
|
+
let blocklet = await this.ensureBlocklet(did);
|
|
1416
|
+
|
|
1417
|
+
// pre install
|
|
1418
|
+
await this._runPreInstallHook(blocklet);
|
|
1419
|
+
|
|
1420
|
+
// Add environments
|
|
1366
1421
|
await this.updateBlockletEnvironment(did);
|
|
1422
|
+
blocklet = await this.ensureBlocklet(did);
|
|
1367
1423
|
|
|
1368
|
-
|
|
1424
|
+
// post install
|
|
1425
|
+
await this._runPostInstallHook(blocklet);
|
|
1426
|
+
|
|
1427
|
+
await states.blocklet.setBlockletStatus(did, BlockletStatus.installed);
|
|
1428
|
+
|
|
1429
|
+
blocklet = await this.ensureBlocklet(did);
|
|
1369
1430
|
|
|
1370
1431
|
await fs.writeFile(path.join(blocklet.env.dataDir, 'logo.svg'), createDidLogo(blocklet.meta.did));
|
|
1371
1432
|
|
|
@@ -1397,36 +1458,44 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1397
1458
|
}
|
|
1398
1459
|
|
|
1399
1460
|
const defaultPath = formatName(meta.name);
|
|
1400
|
-
const
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
];
|
|
1412
|
-
await validateBlocklet(children[0]);
|
|
1413
|
-
await states.blocklet.addChildren(rootDid, children);
|
|
1461
|
+
const component = {
|
|
1462
|
+
meta: ensureMeta(meta),
|
|
1463
|
+
mountPoint: mountPoint || `/${defaultPath}`,
|
|
1464
|
+
source: BlockletSource.local,
|
|
1465
|
+
deployedFrom: folder,
|
|
1466
|
+
status: BlockletStatus.installed,
|
|
1467
|
+
mode: BLOCKLET_MODES.DEVELOPMENT,
|
|
1468
|
+
dynamic: true,
|
|
1469
|
+
children: await parseChildrenFromMeta(meta, { ancestors: [{ mountPoint: mountPoint || `/${defaultPath}` }] }),
|
|
1470
|
+
};
|
|
1471
|
+
await validateBlocklet(component);
|
|
1472
|
+
await states.blocklet.addChildren(rootDid, [component]);
|
|
1414
1473
|
|
|
1415
1474
|
const newBlocklet = await states.blocklet.getBlocklet(rootDid);
|
|
1416
1475
|
await this._downloadBlocklet(newBlocklet, existRoot);
|
|
1417
1476
|
await states.blocklet.setBlockletStatus(rootDid, BlockletStatus.stopped);
|
|
1418
1477
|
|
|
1419
|
-
await this._upsertDynamicNavigation(existRoot.meta.did, { skipNavigation });
|
|
1478
|
+
await this._upsertDynamicNavigation(existRoot.meta.did, component, { skipNavigation });
|
|
1420
1479
|
|
|
1421
|
-
// Add
|
|
1480
|
+
// Add Config
|
|
1422
1481
|
await this._setConfigsFromMeta(rootDid);
|
|
1482
|
+
let blocklet = await this.ensureBlocklet(rootDid);
|
|
1483
|
+
|
|
1484
|
+
// pre install
|
|
1485
|
+
await this._runPreInstallHook(blocklet);
|
|
1486
|
+
|
|
1487
|
+
// Add environments
|
|
1423
1488
|
await this.updateBlockletEnvironment(rootDid);
|
|
1489
|
+
blocklet = await this.ensureBlocklet(rootDid);
|
|
1490
|
+
|
|
1491
|
+
// post install
|
|
1492
|
+
await this._runPostInstallHook(blocklet);
|
|
1424
1493
|
|
|
1425
1494
|
logger.info('add blocklet component for dev', { did, version, meta });
|
|
1426
1495
|
|
|
1427
|
-
|
|
1496
|
+
blocklet = await this.ensureBlocklet(rootDid);
|
|
1428
1497
|
|
|
1429
|
-
return
|
|
1498
|
+
return blocklet;
|
|
1430
1499
|
}
|
|
1431
1500
|
|
|
1432
1501
|
async ensureBlocklet(did, opts = {}) {
|
|
@@ -1881,13 +1950,15 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1881
1950
|
* did: string;
|
|
1882
1951
|
* registry: string;
|
|
1883
1952
|
* sync: boolean;
|
|
1953
|
+
* delay: number;
|
|
1954
|
+
* controller: Controller;
|
|
1884
1955
|
* }} params
|
|
1885
1956
|
* @param {*} context
|
|
1886
1957
|
* @return {*}
|
|
1887
1958
|
* @memberof BlockletManager
|
|
1888
1959
|
*/
|
|
1889
1960
|
async _installFromStore(params, context) {
|
|
1890
|
-
const { did, registry, sync } = params;
|
|
1961
|
+
const { did, registry, sync, delay, controller } = params;
|
|
1891
1962
|
|
|
1892
1963
|
logger.debug('start install blocklet', { did });
|
|
1893
1964
|
if (!isValidDid(did)) {
|
|
@@ -1895,67 +1966,96 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1895
1966
|
}
|
|
1896
1967
|
|
|
1897
1968
|
const registryUrl = registry || (await states.node.getBlockletRegistry());
|
|
1898
|
-
const info = await
|
|
1899
|
-
const meta = await
|
|
1969
|
+
const info = await StoreUtil.getRegistryMeta(registryUrl);
|
|
1970
|
+
const meta = await StoreUtil.getBlockletMeta({ did, registryUrl });
|
|
1900
1971
|
if (!meta) {
|
|
1901
1972
|
throw new Error('Can not install blocklet that not found in registry');
|
|
1902
1973
|
}
|
|
1903
1974
|
|
|
1904
|
-
const
|
|
1975
|
+
const { did: blockletDid, name: blockletName } = getBlockletIndex(meta, controller);
|
|
1976
|
+
|
|
1977
|
+
const state = await states.blocklet.getBlocklet(blockletDid);
|
|
1978
|
+
|
|
1905
1979
|
if (state) {
|
|
1906
1980
|
throw new Error('Can not install an already installed blocklet');
|
|
1907
1981
|
}
|
|
1908
1982
|
|
|
1983
|
+
if (controller?.vcId) {
|
|
1984
|
+
// sometimes nedb will throw error if use states.blocklet.count({ ['controller.vcId']: controller.vcId })
|
|
1985
|
+
const blocklets = await states.blocklet.find({}, { controller: 1 });
|
|
1986
|
+
const count = blocklets.filter((x) => x.controller?.vcId === controller.vcId).length;
|
|
1987
|
+
|
|
1988
|
+
if (count >= (controller.appMaxCount || 1)) {
|
|
1989
|
+
throw new Error(
|
|
1990
|
+
`You can only install ${controller.appMaxCount} blocklet with this credential: ${controller.vcId}`
|
|
1991
|
+
);
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1909
1995
|
// install
|
|
1910
1996
|
return this._install({
|
|
1911
|
-
meta,
|
|
1997
|
+
meta: ensureMeta(meta, { did: blockletDid, name: blockletName }),
|
|
1912
1998
|
source: BlockletSource.registry,
|
|
1913
1999
|
deployedFrom: info.cdnUrl || registryUrl,
|
|
1914
2000
|
sync,
|
|
2001
|
+
delay,
|
|
2002
|
+
controller,
|
|
1915
2003
|
context,
|
|
1916
2004
|
});
|
|
1917
2005
|
}
|
|
1918
2006
|
|
|
1919
2007
|
/**
|
|
1920
|
-
*
|
|
2008
|
+
* @type {{
|
|
2009
|
+
* id: string;
|
|
2010
|
+
* vcId: string;
|
|
2011
|
+
* expireDate: Date;
|
|
2012
|
+
* }} Controller
|
|
1921
2013
|
*
|
|
1922
2014
|
* @param {{
|
|
1923
2015
|
* url: string;
|
|
1924
2016
|
* sync: boolean;
|
|
2017
|
+
* delay: number;
|
|
2018
|
+
* controller: Controller;
|
|
1925
2019
|
* }} params
|
|
1926
2020
|
* @param {{}} context
|
|
1927
2021
|
* @return {*}
|
|
1928
2022
|
* @memberof BlockletManager
|
|
1929
2023
|
*/
|
|
1930
2024
|
async _installFromUrl(params, context) {
|
|
1931
|
-
const { url, sync } = params;
|
|
2025
|
+
const { url, sync, delay, controller } = params;
|
|
1932
2026
|
|
|
1933
2027
|
logger.debug('start install blocklet', { url });
|
|
1934
2028
|
|
|
1935
|
-
const
|
|
2029
|
+
const bundleMeta = await getBlockletMetaFromUrl(url);
|
|
2030
|
+
|
|
2031
|
+
if (!bundleMeta) {
|
|
2032
|
+
throw new Error(`Can not install blocklet that not found by url: ${url}`);
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
const { did: blockletDid, name: blockletName } = getBlockletIndex(bundleMeta, controller);
|
|
2036
|
+
|
|
2037
|
+
// install from store if url is a store url
|
|
2038
|
+
const { inStore, registryUrl, blockletDid: bundleDid } = await StoreUtil.parseSourceUrl(url, controller);
|
|
1936
2039
|
if (inStore) {
|
|
1937
2040
|
const exist = await states.blocklet.getBlocklet(blockletDid);
|
|
1938
2041
|
if (exist) {
|
|
1939
|
-
return this.upgrade({ did: blockletDid, registryUrl }, context);
|
|
2042
|
+
return this.upgrade({ did: blockletDid, registryUrl, sync, delay }, context);
|
|
1940
2043
|
}
|
|
1941
2044
|
|
|
1942
|
-
return this._installFromStore({ did:
|
|
2045
|
+
return this._installFromStore({ did: bundleDid, registry: registryUrl, controller, sync, delay }, context);
|
|
1943
2046
|
}
|
|
1944
2047
|
|
|
1945
|
-
const meta =
|
|
1946
|
-
|
|
1947
|
-
if (!meta) {
|
|
1948
|
-
throw new Error(`Can not install blocklet that not found by url: ${url}`);
|
|
1949
|
-
}
|
|
2048
|
+
const meta = ensureMeta(bundleMeta, { name: blockletName, did: blockletDid });
|
|
1950
2049
|
|
|
1951
2050
|
// upgrade
|
|
1952
|
-
const exist = await states.blocklet.getBlocklet(
|
|
2051
|
+
const exist = await states.blocklet.getBlocklet(blockletDid);
|
|
1953
2052
|
if (exist) {
|
|
1954
2053
|
return this._upgrade({
|
|
1955
2054
|
meta,
|
|
1956
2055
|
source: BlockletSource.url,
|
|
1957
2056
|
deployedFrom: url,
|
|
1958
2057
|
sync,
|
|
2058
|
+
delay,
|
|
1959
2059
|
context,
|
|
1960
2060
|
});
|
|
1961
2061
|
}
|
|
@@ -1966,6 +2066,8 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1966
2066
|
source: BlockletSource.url,
|
|
1967
2067
|
deployedFrom: url,
|
|
1968
2068
|
sync,
|
|
2069
|
+
delay,
|
|
2070
|
+
controller,
|
|
1969
2071
|
context,
|
|
1970
2072
|
});
|
|
1971
2073
|
}
|
|
@@ -1987,7 +2089,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1987
2089
|
throw new Error('Root blocklet does not exist');
|
|
1988
2090
|
}
|
|
1989
2091
|
|
|
1990
|
-
const { inStore } = await parseSourceUrl(url);
|
|
2092
|
+
const { inStore } = await StoreUtil.parseSourceUrl(url);
|
|
1991
2093
|
|
|
1992
2094
|
const meta = await getBlockletMetaFromUrl(url);
|
|
1993
2095
|
|
|
@@ -2125,7 +2227,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2125
2227
|
.pipe(writeStream);
|
|
2126
2228
|
readStream.on('error', (error) => {
|
|
2127
2229
|
logger.error('File upload read stream failed', { error });
|
|
2128
|
-
writeStream.destroy(new Error(
|
|
2230
|
+
writeStream.destroy(new Error(`File upload read stream failed, ${error.message}`));
|
|
2129
2231
|
});
|
|
2130
2232
|
writeStream.on('error', (error) => {
|
|
2131
2233
|
logger.error('File upload write stream failed', { error });
|
|
@@ -2437,12 +2539,14 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2437
2539
|
* deployedFrom: string;
|
|
2438
2540
|
* context: {}
|
|
2439
2541
|
* sync: boolean = false;
|
|
2542
|
+
* delay: number;
|
|
2543
|
+
* controller: Controller;
|
|
2440
2544
|
* }} params
|
|
2441
2545
|
* @return {*}
|
|
2442
2546
|
* @memberof BlockletManager
|
|
2443
2547
|
*/
|
|
2444
2548
|
async _install(params) {
|
|
2445
|
-
const { meta, source, deployedFrom, context, sync } = params;
|
|
2549
|
+
const { meta, source, deployedFrom, context, sync, delay, controller } = params;
|
|
2446
2550
|
|
|
2447
2551
|
validateBlockletMeta(meta, { ensureDist: true });
|
|
2448
2552
|
|
|
@@ -2458,6 +2562,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2458
2562
|
source,
|
|
2459
2563
|
deployedFrom,
|
|
2460
2564
|
children,
|
|
2565
|
+
controller,
|
|
2461
2566
|
});
|
|
2462
2567
|
|
|
2463
2568
|
await validateBlocklet(blocklet);
|
|
@@ -2493,31 +2598,33 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2493
2598
|
return states.blocklet.getBlocklet(did);
|
|
2494
2599
|
}
|
|
2495
2600
|
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2601
|
+
setTimeout(() => {
|
|
2602
|
+
const ticket = this.installQueue.push(
|
|
2603
|
+
{
|
|
2604
|
+
entity: 'blocklet',
|
|
2605
|
+
action: 'download',
|
|
2606
|
+
id: did,
|
|
2607
|
+
...downloadParams,
|
|
2608
|
+
},
|
|
2609
|
+
did
|
|
2610
|
+
);
|
|
2611
|
+
ticket.on('failed', async (err) => {
|
|
2612
|
+
logger.error('failed to install blocklet', { name, did, version, error: err });
|
|
2613
|
+
try {
|
|
2614
|
+
await this._rollback('install', did, { extraState: oldExtraState });
|
|
2615
|
+
} catch (e) {
|
|
2616
|
+
logger.error('failed to remove blocklet on install error', { did: meta.did, error: e });
|
|
2617
|
+
}
|
|
2512
2618
|
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2619
|
+
states.notification.create({
|
|
2620
|
+
title: 'Blocklet Install Failed',
|
|
2621
|
+
description: `Blocklet ${name}@${version} install failed with error: ${err.message || 'queue exception'}`,
|
|
2622
|
+
entityType: 'blocklet',
|
|
2623
|
+
entityId: did,
|
|
2624
|
+
severity: 'error',
|
|
2625
|
+
});
|
|
2519
2626
|
});
|
|
2520
|
-
});
|
|
2627
|
+
}, delay || 0);
|
|
2521
2628
|
|
|
2522
2629
|
return blocklet1;
|
|
2523
2630
|
} catch (err) {
|
|
@@ -2780,33 +2887,14 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2780
2887
|
}
|
|
2781
2888
|
|
|
2782
2889
|
// pre install
|
|
2783
|
-
|
|
2784
|
-
const preInstall = (b) =>
|
|
2785
|
-
hooks.preInstall(b.env.processId, {
|
|
2786
|
-
hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
|
|
2787
|
-
env: { ...nodeEnvironments },
|
|
2788
|
-
appDir: b.env.appDir,
|
|
2789
|
-
did, // root blocklet did
|
|
2790
|
-
notification: states.notification,
|
|
2791
|
-
context,
|
|
2792
|
-
});
|
|
2793
|
-
await forEachBlocklet(blocklet, preInstall, { parallel: true });
|
|
2890
|
+
await this._runPreInstallHook(blocklet, context);
|
|
2794
2891
|
|
|
2795
2892
|
// Add environments
|
|
2796
2893
|
await this.updateBlockletEnvironment(meta.did);
|
|
2797
2894
|
blocklet = await this.ensureBlocklet(did);
|
|
2798
2895
|
|
|
2799
2896
|
// post install
|
|
2800
|
-
|
|
2801
|
-
hooks.postInstall(b.env.processId, {
|
|
2802
|
-
hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
|
|
2803
|
-
env: getRuntimeEnvironments(b, nodeEnvironments, ancestors),
|
|
2804
|
-
appDir: b.env.appDir,
|
|
2805
|
-
did, // root blocklet did
|
|
2806
|
-
notification: states.notification,
|
|
2807
|
-
context,
|
|
2808
|
-
});
|
|
2809
|
-
await forEachBlocklet(blocklet, postInstall, { parallel: true });
|
|
2897
|
+
await this._runPostInstallHook(blocklet, context);
|
|
2810
2898
|
|
|
2811
2899
|
await states.blocklet.setBlockletStatus(did, BlockletStatus.installed);
|
|
2812
2900
|
blocklet = await this.ensureBlocklet(did);
|
|
@@ -2892,33 +2980,14 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2892
2980
|
let blocklet = await this.ensureBlocklet(did);
|
|
2893
2981
|
|
|
2894
2982
|
// pre install
|
|
2895
|
-
|
|
2896
|
-
const preInstall = (b) =>
|
|
2897
|
-
hooks.preInstall(b.env.processId, {
|
|
2898
|
-
hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
|
|
2899
|
-
env: { ...nodeEnvironments },
|
|
2900
|
-
appDir: b.env.appDir,
|
|
2901
|
-
did, // root blocklet did
|
|
2902
|
-
notification: states.notification,
|
|
2903
|
-
context,
|
|
2904
|
-
});
|
|
2905
|
-
await forEachBlocklet(blocklet, preInstall, { parallel: true });
|
|
2983
|
+
await this._runPreInstallHook(blocklet, context);
|
|
2906
2984
|
|
|
2907
2985
|
// Add environments
|
|
2908
2986
|
await this.updateBlockletEnvironment(did);
|
|
2909
2987
|
blocklet = await this.ensureBlocklet(did);
|
|
2910
2988
|
|
|
2911
2989
|
// post install
|
|
2912
|
-
|
|
2913
|
-
hooks.postInstall(b.env.processId, {
|
|
2914
|
-
hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
|
|
2915
|
-
env: getRuntimeEnvironments(b, nodeEnvironments, ancestors),
|
|
2916
|
-
appDir: b.env.appDir,
|
|
2917
|
-
did, // root blocklet did
|
|
2918
|
-
notification: states.notification,
|
|
2919
|
-
context,
|
|
2920
|
-
});
|
|
2921
|
-
await forEachBlocklet(blocklet, postInstall, { parallel: true });
|
|
2990
|
+
await this._runPostInstallHook(blocklet, context);
|
|
2922
2991
|
|
|
2923
2992
|
logger.info('start migration');
|
|
2924
2993
|
try {
|
|
@@ -2926,6 +2995,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2926
2995
|
forEachBlockletSync(oldBlocklet, (b, { id }) => {
|
|
2927
2996
|
oldVersions[id] = b.meta.version;
|
|
2928
2997
|
});
|
|
2998
|
+
const nodeEnvironments = await states.node.getEnvironments();
|
|
2929
2999
|
const runMigration = (b, { id, ancestors }) => {
|
|
2930
3000
|
return runMigrationScripts({
|
|
2931
3001
|
blocklet: b,
|
|
@@ -3275,11 +3345,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3275
3345
|
logger.info('Download Blocklet', { name, did, bundles: metas.map((x) => get(x, 'dist.tarball')) });
|
|
3276
3346
|
const tasks = [];
|
|
3277
3347
|
for (const meta of metas) {
|
|
3278
|
-
const url =
|
|
3279
|
-
did,
|
|
3280
|
-
tarball: get(meta, 'dist.tarball'),
|
|
3281
|
-
registryUrl: blocklet.source === BlockletSource.registry ? blocklet.deployedFrom : undefined,
|
|
3282
|
-
});
|
|
3348
|
+
const url = meta.dist.tarball;
|
|
3283
3349
|
tasks.push(this._downloadBundle(meta, did, url, context));
|
|
3284
3350
|
}
|
|
3285
3351
|
const results = await Promise.all(tasks);
|
|
@@ -3473,24 +3539,20 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3473
3539
|
}
|
|
3474
3540
|
|
|
3475
3541
|
async _getLatestBlockletVersionFromStore({ blocklet, version }) {
|
|
3542
|
+
const { deployedFrom: registryUrl } = blocklet;
|
|
3476
3543
|
const { did, bundleDid } = blocklet.meta;
|
|
3477
3544
|
|
|
3478
3545
|
let versions = this.cachedBlockletVersions.get(did);
|
|
3479
3546
|
|
|
3480
3547
|
if (!versions) {
|
|
3481
|
-
const
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
return;
|
|
3489
|
-
}
|
|
3490
|
-
logger.error('get blocklet meta from registry failed', { did, error });
|
|
3491
|
-
})); // prettier-ignore
|
|
3548
|
+
const item = await StoreUtil.getBlockletMeta({ did: bundleDid, registryUrl });
|
|
3549
|
+
|
|
3550
|
+
if (!item) {
|
|
3551
|
+
return null;
|
|
3552
|
+
}
|
|
3553
|
+
|
|
3554
|
+
versions = [{ did, version: item.version }];
|
|
3492
3555
|
|
|
3493
|
-
versions = await Promise.all(tasks);
|
|
3494
3556
|
this.cachedBlockletVersions.set(did, versions);
|
|
3495
3557
|
}
|
|
3496
3558
|
|
|
@@ -3543,6 +3605,38 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3543
3605
|
|
|
3544
3606
|
return versions[0];
|
|
3545
3607
|
}
|
|
3608
|
+
|
|
3609
|
+
async _runPreInstallHook(blocklet, context) {
|
|
3610
|
+
const nodeEnvironments = await states.node.getEnvironments();
|
|
3611
|
+
|
|
3612
|
+
const preInstall = (b) =>
|
|
3613
|
+
hooks.preInstall(b.env.processId, {
|
|
3614
|
+
hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
|
|
3615
|
+
env: { ...nodeEnvironments },
|
|
3616
|
+
appDir: b.env.appDir,
|
|
3617
|
+
did: blocklet.meta.did, // root blocklet did
|
|
3618
|
+
notification: states.notification,
|
|
3619
|
+
context,
|
|
3620
|
+
});
|
|
3621
|
+
|
|
3622
|
+
await forEachBlocklet(blocklet, preInstall, { parallel: true });
|
|
3623
|
+
}
|
|
3624
|
+
|
|
3625
|
+
async _runPostInstallHook(blocklet, context) {
|
|
3626
|
+
const nodeEnvironments = await states.node.getEnvironments();
|
|
3627
|
+
|
|
3628
|
+
const postInstall = (b, { ancestors }) =>
|
|
3629
|
+
hooks.postInstall(b.env.processId, {
|
|
3630
|
+
hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
|
|
3631
|
+
env: getRuntimeEnvironments(b, nodeEnvironments, ancestors),
|
|
3632
|
+
appDir: b.env.appDir,
|
|
3633
|
+
did: blocklet.meta.did, // root blocklet did
|
|
3634
|
+
notification: states.notification,
|
|
3635
|
+
context,
|
|
3636
|
+
});
|
|
3637
|
+
|
|
3638
|
+
await forEachBlocklet(blocklet, postInstall, { parallel: true });
|
|
3639
|
+
}
|
|
3546
3640
|
}
|
|
3547
3641
|
|
|
3548
3642
|
module.exports = BlockletManager;
|
package/lib/index.js
CHANGED
|
@@ -11,7 +11,6 @@ const { listProviders } = require('@abtnode/router-provider');
|
|
|
11
11
|
const { DEFAULT_CERTIFICATE_EMAIL, EVENTS } = require('@abtnode/constant');
|
|
12
12
|
|
|
13
13
|
const RoutingSnapshot = require('./states/routing-snapshot');
|
|
14
|
-
const BlockletRegistry = require('./blocklet/registry');
|
|
15
14
|
const BlockletManager = require('./blocklet/manager/disk');
|
|
16
15
|
const RouterManager = require('./router/manager');
|
|
17
16
|
const getRouterHelpers = require('./router/helper');
|
|
@@ -27,6 +26,7 @@ const DomainStatus = require('./util/domain-status');
|
|
|
27
26
|
const Upgrade = require('./util/upgrade');
|
|
28
27
|
const resetNode = require('./util/reset-node');
|
|
29
28
|
const DiskMonitor = require('./util/disk-monitor');
|
|
29
|
+
const StoreUtil = require('./util/store');
|
|
30
30
|
const createQueue = require('./queue');
|
|
31
31
|
const createEvents = require('./event');
|
|
32
32
|
const pm2Events = require('./blocklet/manager/pm2-events');
|
|
@@ -123,15 +123,12 @@ function ABTNode(options) {
|
|
|
123
123
|
},
|
|
124
124
|
});
|
|
125
125
|
|
|
126
|
-
const blockletRegistry = new BlockletRegistry();
|
|
127
|
-
|
|
128
126
|
const teamManager = new TeamManager({ nodeDid: options.nodeDid, dataDirs, states });
|
|
129
127
|
|
|
130
128
|
const blockletManager = new BlockletManager({
|
|
131
129
|
dataDirs,
|
|
132
130
|
startQueue,
|
|
133
131
|
installQueue,
|
|
134
|
-
registry: blockletRegistry,
|
|
135
132
|
daemon: options.daemon,
|
|
136
133
|
teamManager,
|
|
137
134
|
});
|
|
@@ -207,6 +204,7 @@ function ABTNode(options) {
|
|
|
207
204
|
upgradeComponents: blockletManager.updateChildren.bind(blockletManager),
|
|
208
205
|
getLatestBlockletVersion: blockletManager.getLatestBlockletVersion.bind(blockletManager),
|
|
209
206
|
getBlockletMetaFromUrl: blockletManager.getMetaFromUrl.bind(blockletManager),
|
|
207
|
+
getBlockletByBundle: blockletManager.getBlockletByBundle.bind(blockletManager),
|
|
210
208
|
resetBlocklet: blockletManager.reset.bind(blockletManager),
|
|
211
209
|
deleteBlockletProcess: blockletManager.deleteProcess.bind(blockletManager),
|
|
212
210
|
configPublicToStore: blockletManager.configPublicToStore.bind(blockletManager),
|
|
@@ -226,9 +224,9 @@ function ABTNode(options) {
|
|
|
226
224
|
updateAllBlockletEnvironment: blockletManager.updateAllBlockletEnvironment.bind(blockletManager),
|
|
227
225
|
setBlockletInitialized: blockletManager.setInitialized.bind(blockletManager),
|
|
228
226
|
|
|
229
|
-
//
|
|
230
|
-
getBlockletMeta:
|
|
231
|
-
getRegistryMeta:
|
|
227
|
+
// Store
|
|
228
|
+
getBlockletMeta: StoreUtil.getBlockletMeta,
|
|
229
|
+
getRegistryMeta: StoreUtil.getRegistryMeta,
|
|
232
230
|
|
|
233
231
|
// Node State
|
|
234
232
|
getNodeInfo: nodeAPI.getInfo.bind(nodeAPI),
|
package/lib/states/README.md
CHANGED
|
@@ -33,3 +33,6 @@ All Blocklet Server states are managed by files in this folder.
|
|
|
33
33
|
- `children`: component of app, same structure as parent, can be nested
|
|
34
34
|
- `mountPoint` mount path of web site. _component only_
|
|
35
35
|
- `dynamic` is dynamically added. _component only_
|
|
36
|
+
- `controller`
|
|
37
|
+
- `tokens`
|
|
38
|
+
- `downloadTokenList`
|
package/lib/states/blocklet.js
CHANGED
|
@@ -23,6 +23,7 @@ const logger = require('@abtnode/logger')('state-blocklet');
|
|
|
23
23
|
const BaseState = require('./base');
|
|
24
24
|
const { checkDuplicateComponents, ensureMeta } = require('../util/blocklet');
|
|
25
25
|
const { validateBlockletMeta } = require('../util');
|
|
26
|
+
const { validateBlockletController } = require('../validators/blocklet');
|
|
26
27
|
|
|
27
28
|
const lock = new Lock('blocklet-port-assign-lock');
|
|
28
29
|
|
|
@@ -180,11 +181,12 @@ class BlockletState extends BaseState {
|
|
|
180
181
|
deployedFrom = '',
|
|
181
182
|
mode = BLOCKLET_MODES.PRODUCTION,
|
|
182
183
|
children: rawChildren = [],
|
|
184
|
+
controller,
|
|
183
185
|
} = {}) {
|
|
184
186
|
return this.getBlocklet(meta.did).then(
|
|
185
|
-
(
|
|
187
|
+
(exist) =>
|
|
186
188
|
new Promise(async (resolve, reject) => {
|
|
187
|
-
if (
|
|
189
|
+
if (exist) {
|
|
188
190
|
reject(new Error('Blocklet already added'));
|
|
189
191
|
return;
|
|
190
192
|
}
|
|
@@ -208,29 +210,33 @@ class BlockletState extends BaseState {
|
|
|
208
210
|
|
|
209
211
|
fixChildren(children);
|
|
210
212
|
|
|
213
|
+
const data = {
|
|
214
|
+
appDid: null, // will updated later when updating blocklet environments
|
|
215
|
+
mode,
|
|
216
|
+
meta: sanitized,
|
|
217
|
+
status,
|
|
218
|
+
source,
|
|
219
|
+
deployedFrom,
|
|
220
|
+
ports,
|
|
221
|
+
environments: [],
|
|
222
|
+
children,
|
|
223
|
+
controller,
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
if (controller) {
|
|
227
|
+
data.controller = await validateBlockletController(controller);
|
|
228
|
+
}
|
|
229
|
+
|
|
211
230
|
// add to db
|
|
212
|
-
this.insert(
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
meta: sanitized,
|
|
217
|
-
status,
|
|
218
|
-
source,
|
|
219
|
-
deployedFrom,
|
|
220
|
-
ports,
|
|
221
|
-
environments: [],
|
|
222
|
-
children,
|
|
223
|
-
},
|
|
224
|
-
(err, newDoc) => {
|
|
225
|
-
lock.release();
|
|
226
|
-
if (err) {
|
|
227
|
-
return reject(err);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
this.emit('add', newDoc);
|
|
231
|
-
return resolve(newDoc);
|
|
231
|
+
this.insert(data, (err, doc) => {
|
|
232
|
+
lock.release();
|
|
233
|
+
if (err) {
|
|
234
|
+
return reject(err);
|
|
232
235
|
}
|
|
233
|
-
|
|
236
|
+
|
|
237
|
+
this.emit('add', doc);
|
|
238
|
+
return resolve(doc);
|
|
239
|
+
});
|
|
234
240
|
} catch (err) {
|
|
235
241
|
lock.release();
|
|
236
242
|
reject(err);
|
package/lib/states/user.js
CHANGED
|
@@ -124,6 +124,22 @@ class User extends BaseState {
|
|
|
124
124
|
};
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
+
async getUsersByDids({ dids, query } = {}) {
|
|
128
|
+
const { approved } = query || {};
|
|
129
|
+
const didList = dids || [];
|
|
130
|
+
|
|
131
|
+
const queryParam = {
|
|
132
|
+
did: { $in: didList },
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
if (!isNullOrUndefined(approved)) {
|
|
136
|
+
queryParam.approved = !!approved;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// get data
|
|
140
|
+
return this.find(queryParam);
|
|
141
|
+
}
|
|
142
|
+
|
|
127
143
|
async getUser(did) {
|
|
128
144
|
const doc = await this.findOne({ did });
|
|
129
145
|
return doc;
|
package/lib/util/requirement.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
const semver = require('semver');
|
|
2
2
|
|
|
3
|
-
let { version:
|
|
3
|
+
let { version: actualServerVersion } = require('../../package.json');
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
const { version: actualNodejsVersion } = process;
|
|
6
|
+
|
|
7
|
+
const defaults = { abtnode: '>= 1.1.0', os: '*', cpu: '*', nodejs: '*' };
|
|
6
8
|
|
|
7
9
|
const isSatisfied = (requirements, throwOnUnsatisfied = true) => {
|
|
8
10
|
if (!requirements || typeof requirements !== 'object') {
|
|
@@ -10,15 +12,23 @@ const isSatisfied = (requirements, throwOnUnsatisfied = true) => {
|
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
const { platform: actualOs, arch: actualCpu } = process;
|
|
13
|
-
const {
|
|
14
|
-
|
|
15
|
+
const {
|
|
16
|
+
os: expectedOs,
|
|
17
|
+
cpu: expectedCpu,
|
|
18
|
+
abtnode,
|
|
19
|
+
server,
|
|
20
|
+
nodejs: expectedNodejsVersion,
|
|
21
|
+
} = Object.assign(defaults, requirements);
|
|
22
|
+
const expectedServerVersion = server || abtnode;
|
|
15
23
|
|
|
16
24
|
const isOsSatisfied =
|
|
17
25
|
expectedOs === '*' || expectedOs === actualOs || (Array.isArray(expectedOs) && expectedOs.includes(actualOs));
|
|
18
26
|
const isCpuSatisfied =
|
|
19
27
|
expectedCpu === '*' || expectedCpu === actualCpu || (Array.isArray(expectedCpu) && expectedCpu.includes(actualCpu));
|
|
20
28
|
|
|
21
|
-
const
|
|
29
|
+
const isServerVersionSatisfied = semver.satisfies(semver.coerce(actualServerVersion), expectedServerVersion);
|
|
30
|
+
|
|
31
|
+
const isNodejsVersionSatisfied = semver.satisfies(semver.coerce(actualNodejsVersion), expectedNodejsVersion);
|
|
22
32
|
|
|
23
33
|
if (throwOnUnsatisfied) {
|
|
24
34
|
if (isOsSatisfied === false) {
|
|
@@ -27,19 +37,25 @@ const isSatisfied = (requirements, throwOnUnsatisfied = true) => {
|
|
|
27
37
|
if (isCpuSatisfied === false) {
|
|
28
38
|
throw new Error(`Your blocklet is not supported on architecture ${actualCpu}`);
|
|
29
39
|
}
|
|
30
|
-
if (
|
|
40
|
+
if (isServerVersionSatisfied === false) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
`Expected server version for the blocklet is ${expectedServerVersion}, but your server version is ${actualServerVersion}, please upgrade or downgrade your server to continue.`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
if (isNodejsVersionSatisfied === false) {
|
|
31
46
|
throw new Error(
|
|
32
|
-
`Expected
|
|
47
|
+
`Expected Nodejs version for the blocklet is ${expectedNodejsVersion}, but your Nodejs version is ${actualNodejsVersion}, please upgrade or downgrade Nodejs and restart your server.`
|
|
33
48
|
);
|
|
34
49
|
}
|
|
35
50
|
}
|
|
36
51
|
|
|
37
|
-
return isOsSatisfied && isCpuSatisfied &&
|
|
52
|
+
return isOsSatisfied && isCpuSatisfied && isServerVersionSatisfied;
|
|
38
53
|
};
|
|
39
54
|
|
|
40
55
|
// Just for test
|
|
41
|
-
isSatisfied.
|
|
42
|
-
|
|
56
|
+
isSatisfied._setActualServerVersion = (d) => {
|
|
57
|
+
actualServerVersion = d;
|
|
43
58
|
};
|
|
59
|
+
isSatisfied._actualNodejsVersion = actualNodejsVersion;
|
|
44
60
|
|
|
45
61
|
module.exports = isSatisfied;
|
|
@@ -8,6 +8,7 @@ const { name } = require('../../package.json');
|
|
|
8
8
|
const logger = require('@abtnode/logger')(`${name}:util:registry`); // eslint-disable-line
|
|
9
9
|
|
|
10
10
|
const request = require('./request');
|
|
11
|
+
const { fixAndVerifyMetaFromStore } = require('./blocklet');
|
|
11
12
|
|
|
12
13
|
const validateRegistryURL = async (registry) => {
|
|
13
14
|
const url = joinURL(registry, BLOCKLET_STORE_API_PREFIX, `/blocklets.json?__t__=${Date.now()}`);
|
|
@@ -83,8 +84,58 @@ const parseSourceUrl = async (url) => {
|
|
|
83
84
|
};
|
|
84
85
|
};
|
|
85
86
|
|
|
87
|
+
const resolveTarballURL = async ({ did, tarball = '', registryUrl = '' }) => {
|
|
88
|
+
if (!tarball) {
|
|
89
|
+
return '';
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (tarball.startsWith('file://')) {
|
|
93
|
+
return decodeURIComponent(tarball);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (tarball.startsWith('http://') || tarball.startsWith('https://')) {
|
|
97
|
+
return tarball;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!registryUrl) {
|
|
101
|
+
return '';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!did) {
|
|
105
|
+
return '';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return joinURL(registryUrl, 'api', 'blocklets', did, tarball);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const getBlockletMeta = async ({ did, registryUrl }) => {
|
|
112
|
+
const url = joinURL(registryUrl, BLOCKLET_STORE_API_PREFIX, `/blocklets/${did}/blocklet.json?__t__=${Date.now()}`);
|
|
113
|
+
|
|
114
|
+
const { data } = await request.get(url);
|
|
115
|
+
try {
|
|
116
|
+
if (data.did !== did) {
|
|
117
|
+
throw new Error('Invalid blocklet meta: did does not match');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const meta = fixAndVerifyMetaFromStore(data);
|
|
121
|
+
|
|
122
|
+
meta.dist.tarball = await resolveTarballURL({
|
|
123
|
+
did,
|
|
124
|
+
tarball: meta.dist.tarball,
|
|
125
|
+
registryUrl,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
return meta;
|
|
129
|
+
} catch (err) {
|
|
130
|
+
logger.error('failed to get blocklet meta', { did, data, error: err });
|
|
131
|
+
throw err;
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
86
135
|
module.exports = {
|
|
87
136
|
validateRegistryURL,
|
|
88
137
|
getRegistryMeta,
|
|
89
138
|
parseSourceUrl,
|
|
139
|
+
getBlockletMeta,
|
|
140
|
+
resolveTarballURL,
|
|
90
141
|
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const JOI = require('joi');
|
|
2
|
+
const { didExtension } = require('@blocklet/meta/lib/extension');
|
|
3
|
+
|
|
4
|
+
const Joi = JOI.extend(didExtension);
|
|
5
|
+
|
|
6
|
+
const blockletController = Joi.object({
|
|
7
|
+
id: Joi.DID().required(), // userDid
|
|
8
|
+
vcId: Joi.DID().required(),
|
|
9
|
+
appMaxCount: Joi.number().required().min(1),
|
|
10
|
+
expireDate: Joi.date(),
|
|
11
|
+
}).options({ stripUnknown: true });
|
|
12
|
+
|
|
13
|
+
module.exports = {
|
|
14
|
+
validateBlockletController: (entity) => blockletController.validateAsync(entity),
|
|
15
|
+
};
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.8.
|
|
6
|
+
"version": "1.8.32",
|
|
7
7
|
"description": "",
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"files": [
|
|
@@ -19,32 +19,32 @@
|
|
|
19
19
|
"author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@abtnode/auth": "1.8.
|
|
23
|
-
"@abtnode/certificate-manager": "1.8.
|
|
24
|
-
"@abtnode/constant": "1.8.
|
|
25
|
-
"@abtnode/cron": "1.8.
|
|
26
|
-
"@abtnode/db": "1.8.
|
|
27
|
-
"@abtnode/logger": "1.8.
|
|
28
|
-
"@abtnode/queue": "1.8.
|
|
29
|
-
"@abtnode/rbac": "1.8.
|
|
30
|
-
"@abtnode/router-provider": "1.8.
|
|
31
|
-
"@abtnode/static-server": "1.8.
|
|
32
|
-
"@abtnode/timemachine": "1.8.
|
|
33
|
-
"@abtnode/util": "1.8.
|
|
34
|
-
"@arcblock/did": "1.
|
|
22
|
+
"@abtnode/auth": "1.8.32",
|
|
23
|
+
"@abtnode/certificate-manager": "1.8.32",
|
|
24
|
+
"@abtnode/constant": "1.8.32",
|
|
25
|
+
"@abtnode/cron": "1.8.32",
|
|
26
|
+
"@abtnode/db": "1.8.32",
|
|
27
|
+
"@abtnode/logger": "1.8.32",
|
|
28
|
+
"@abtnode/queue": "1.8.32",
|
|
29
|
+
"@abtnode/rbac": "1.8.32",
|
|
30
|
+
"@abtnode/router-provider": "1.8.32",
|
|
31
|
+
"@abtnode/static-server": "1.8.32",
|
|
32
|
+
"@abtnode/timemachine": "1.8.32",
|
|
33
|
+
"@abtnode/util": "1.8.32",
|
|
34
|
+
"@arcblock/did": "1.18.1",
|
|
35
35
|
"@arcblock/did-motif": "^1.1.10",
|
|
36
|
-
"@arcblock/did-util": "1.
|
|
37
|
-
"@arcblock/event-hub": "1.
|
|
38
|
-
"@arcblock/jwt": "^1.
|
|
36
|
+
"@arcblock/did-util": "1.18.1",
|
|
37
|
+
"@arcblock/event-hub": "1.18.1",
|
|
38
|
+
"@arcblock/jwt": "^1.18.1",
|
|
39
39
|
"@arcblock/pm2-events": "^0.0.5",
|
|
40
|
-
"@arcblock/vc": "1.
|
|
41
|
-
"@blocklet/constant": "1.8.
|
|
42
|
-
"@blocklet/meta": "1.8.
|
|
43
|
-
"@blocklet/sdk": "1.8.
|
|
40
|
+
"@arcblock/vc": "1.18.1",
|
|
41
|
+
"@blocklet/constant": "1.8.32",
|
|
42
|
+
"@blocklet/meta": "1.8.32",
|
|
43
|
+
"@blocklet/sdk": "1.8.32",
|
|
44
44
|
"@fidm/x509": "^1.2.1",
|
|
45
|
-
"@ocap/mcrypto": "1.
|
|
46
|
-
"@ocap/util": "1.
|
|
47
|
-
"@ocap/wallet": "1.
|
|
45
|
+
"@ocap/mcrypto": "1.18.1",
|
|
46
|
+
"@ocap/util": "1.18.1",
|
|
47
|
+
"@ocap/wallet": "1.18.1",
|
|
48
48
|
"@slack/webhook": "^5.0.4",
|
|
49
49
|
"axios": "^0.27.2",
|
|
50
50
|
"axon": "^2.0.3",
|
|
@@ -81,5 +81,5 @@
|
|
|
81
81
|
"express": "^4.18.2",
|
|
82
82
|
"jest": "^27.5.1"
|
|
83
83
|
},
|
|
84
|
-
"gitHead": "
|
|
84
|
+
"gitHead": "8502244aeda5926b3433c1dd9142e63233e3c23c"
|
|
85
85
|
}
|
package/lib/blocklet/registry.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
const joinURL = require('url-join');
|
|
2
|
-
const { BLOCKLET_STORE_API_PREFIX } = require('@abtnode/constant');
|
|
3
|
-
|
|
4
|
-
const { name } = require('../../package.json');
|
|
5
|
-
|
|
6
|
-
const logger = require('@abtnode/logger')(`${name}:blocklet:registry`); // eslint-disable-line
|
|
7
|
-
|
|
8
|
-
const request = require('../util/request');
|
|
9
|
-
|
|
10
|
-
const states = require('../states');
|
|
11
|
-
const { fixAndVerifyMetaFromStore } = require('../util/blocklet');
|
|
12
|
-
const { validateRegistryURL, getRegistryMeta } = require('../util/registry');
|
|
13
|
-
|
|
14
|
-
class BlockletRegistry {
|
|
15
|
-
constructor() {
|
|
16
|
-
this.blocklets = [];
|
|
17
|
-
this.cacheId = '';
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
getBlocklet(id, registryUrl) {
|
|
21
|
-
return this.getBlockletMeta({ did: id, registryUrl });
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// eslint-disable-next-line no-unused-vars
|
|
25
|
-
async getBlockletMeta({ did, registryUrl }, context) {
|
|
26
|
-
const defaultRegistryUrl = await states.node.getBlockletRegistry();
|
|
27
|
-
|
|
28
|
-
const url = joinURL(
|
|
29
|
-
registryUrl || defaultRegistryUrl,
|
|
30
|
-
BLOCKLET_STORE_API_PREFIX,
|
|
31
|
-
`/blocklets/${did}/blocklet.json?__t__=${Date.now()}`
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
const { data } = await request.get(url);
|
|
35
|
-
try {
|
|
36
|
-
if (data.did !== did) {
|
|
37
|
-
throw new Error('Invalid blocklet meta: did does not match');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return fixAndVerifyMetaFromStore(data);
|
|
41
|
-
} catch (err) {
|
|
42
|
-
logger.error('failed to get blocklet meta', { did, data, error: err });
|
|
43
|
-
throw err;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async resolveTarballURL({ did, tarball = '', registryUrl = '' }) {
|
|
48
|
-
if (!tarball) {
|
|
49
|
-
return '';
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (tarball.startsWith('file://')) {
|
|
53
|
-
return decodeURIComponent(tarball);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (tarball.startsWith('http://') || tarball.startsWith('https://')) {
|
|
57
|
-
return tarball;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const defaultRegistryUrl = await states.node.getBlockletRegistry();
|
|
61
|
-
return joinURL(registryUrl || defaultRegistryUrl, 'api', 'blocklets', did, tarball);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
clearCache() {
|
|
65
|
-
this.cacheId = '';
|
|
66
|
-
this.blocklets = [];
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
BlockletRegistry.validateRegistryURL = validateRegistryURL;
|
|
71
|
-
|
|
72
|
-
BlockletRegistry.getRegistryMeta = getRegistryMeta;
|
|
73
|
-
|
|
74
|
-
module.exports = BlockletRegistry;
|