@abtnode/core 1.8.15 → 1.8.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/api/team.js CHANGED
@@ -1,13 +1,35 @@
1
1
  const { EventEmitter } = require('events');
2
2
  const pick = require('lodash/pick');
3
+ const joinUrl = require('url-join');
4
+
3
5
  const logger = require('@abtnode/logger')('@abtnode/core:api:team');
4
- const { ROLES, genPermissionName, EVENTS, WHO_CAN_ACCESS, PASSPORT_STATUS } = require('@abtnode/constant');
6
+ const {
7
+ ROLES,
8
+ genPermissionName,
9
+ EVENTS,
10
+ WHO_CAN_ACCESS,
11
+ PASSPORT_STATUS,
12
+ WELLKNOWN_SERVICE_PATH_PREFIX,
13
+ } = require('@abtnode/constant');
5
14
  const { isValid: isValidDid } = require('@arcblock/did');
6
15
  const { BlockletEvents } = require('@blocklet/meta/lib/constants');
16
+ const { sendToUser } = require('@blocklet/sdk/lib/util/send-notification');
17
+ const {
18
+ createPassportVC,
19
+ createPassport,
20
+ upsertToPassports,
21
+ createUserPassport,
22
+ } = require('@abtnode/auth/lib/passport');
23
+ const { getPassportStatusEndpoint } = require('@abtnode/auth/lib/auth');
24
+ const getBlockletInfo = require('@blocklet/meta/lib/info');
25
+ const { parseUserAvatar } = require('@abtnode/util/lib/user-avatar');
26
+
7
27
  const { validateTrustedPassportIssuers } = require('../validators/trusted-passport');
8
28
  const { validateCreateRole, validateUpdateRole } = require('../validators/role');
9
29
  const { validateCreatePermission, validateUpdatePermission } = require('../validators/permission');
10
30
 
31
+ const { getBlocklet } = require('../util/blocklet');
32
+
11
33
  const MAX_USER_PAGE_SIZE = 100;
12
34
 
13
35
  const validateReservedRole = (role) => {
@@ -17,19 +39,53 @@ const validateReservedRole = (role) => {
17
39
  return true;
18
40
  };
19
41
 
42
+ const sendPassportVcNotification = ({ userDid, appWallet: wallet, locale, vc }) => {
43
+ try {
44
+ const receiver = userDid;
45
+ const sender = { appDid: wallet.address, appSk: wallet.secretKey };
46
+ const message = {
47
+ title: {
48
+ en: 'Receive a Passport',
49
+ zh: '获得通行证',
50
+ }[locale],
51
+ body: {
52
+ en: 'You got a passport',
53
+ zh: '你获得了一张通行证',
54
+ }[locale],
55
+ attachments: [
56
+ {
57
+ type: 'vc',
58
+ data: {
59
+ credential: vc,
60
+ tag: vc.credentialSubject.passport.name,
61
+ },
62
+ },
63
+ ],
64
+ };
65
+
66
+ sendToUser(receiver, message, sender, process.env.ABT_NODE_SERVICE_PORT).catch((error) => {
67
+ logger.error('Failed send passport vc to wallet', { error });
68
+ });
69
+ } catch (error) {
70
+ logger.error('Failed send passport vc to wallet', { error });
71
+ }
72
+ };
73
+
20
74
  class TeamAPI extends EventEmitter {
21
75
  /**
22
76
  *
23
77
  * @param {object} states abtnode core StateDB
24
78
  */
25
- constructor({ teamManager, states }) {
79
+ constructor({ teamManager, states, dataDirs }) {
26
80
  super();
27
81
 
28
82
  this.notification = states.notification;
29
83
  this.node = states.node;
84
+ this.states = states;
30
85
  this.memberInviteExpireTime = 1000 * 3600 * 24 * 30; // 30 days
31
86
  this.serverInviteExpireTime = 1000 * 3600; // 1 hour
32
87
  this.teamManager = teamManager;
88
+ this.dataDirs = dataDirs;
33
89
  }
34
90
 
35
91
  // User && Invitation
@@ -223,6 +279,72 @@ class TeamAPI extends EventEmitter {
223
279
  return doc;
224
280
  }
225
281
 
282
+ async issuePassportToUser({ teamDid, userDid, role: roleName, notify = true }, context = {}) {
283
+ const { locale = 'en' } = context;
284
+
285
+ const userState = await this.getUserState(teamDid);
286
+ const user = await userState.getUser(userDid);
287
+
288
+ if (!user) {
289
+ throw new Error(`user does not exist: ${userDid}`);
290
+ }
291
+
292
+ if (!user.approved) {
293
+ throw new Error(`the user is revoked: ${userDid}`);
294
+ }
295
+
296
+ const rbac = await this.getRBAC(teamDid);
297
+ const role = await rbac.getRole(roleName);
298
+
299
+ if (!role) {
300
+ throw new Error(`passport does not exist: ${roleName}`);
301
+ }
302
+
303
+ // create vc
304
+ const blocklet = await getBlocklet({ did: teamDid, states: this.states, dataDirs: this.dataDirs });
305
+ const nodeInfo = await this.node.read();
306
+ const blockletInfo = getBlockletInfo(blocklet, nodeInfo.sk);
307
+ const { wallet, passportColor, appUrl, name: issuerName } = blockletInfo;
308
+
309
+ const vc = createPassportVC({
310
+ issuerName,
311
+ issuerWallet: wallet,
312
+ ownerDid: userDid,
313
+ passport: await createPassport({
314
+ role,
315
+ }),
316
+ endpoint: getPassportStatusEndpoint({
317
+ baseUrl: joinUrl(appUrl, WELLKNOWN_SERVICE_PATH_PREFIX),
318
+ userDid,
319
+ teamDid,
320
+ }),
321
+ ownerProfile: {
322
+ ...user,
323
+ avatar: await parseUserAvatar(user.avatar, { dataDir: blocklet.env.dataDir }),
324
+ },
325
+ preferredColor: passportColor,
326
+ });
327
+
328
+ // write passport to db
329
+ const passport = createUserPassport(vc, { role: role.name });
330
+ user.passports = upsertToPassports(user.passports || [], passport);
331
+ await this.updateUser({
332
+ teamDid,
333
+ user: {
334
+ did: user.did,
335
+ pk: user.pk,
336
+ passports: user.passports,
337
+ },
338
+ });
339
+
340
+ // send vc to wallet
341
+ if (notify) {
342
+ sendPassportVcNotification({ userDid, appWallet: wallet, locale, vc });
343
+ }
344
+
345
+ return user;
346
+ }
347
+
226
348
  async revokeUserPassport({ teamDid, userDid, passportId } = {}) {
227
349
  if (!passportId) {
228
350
  throw new Error('Revoked passport should not be empty');
@@ -56,7 +56,8 @@ const preStart = async (blocklet, options) => {
56
56
  return runUserHook(blocklet.env.processId, 'pre-start', options);
57
57
  };
58
58
 
59
+ const postStart = (processId, ...args) => runUserHook(processId, 'post-start', ...args);
59
60
  const preUninstall = (processId, ...args) => runUserHook(processId, 'pre-uninstall', ...args);
60
61
  const preStop = (processId, ...args) => runUserHook(processId, 'pre-stop', ...args);
61
62
 
62
- module.exports = { preDeploy, preInstall, postInstall, preStart, preUninstall, preStop, preConfig };
63
+ module.exports = { preDeploy, preInstall, postInstall, preStart, postStart, preUninstall, preStop, preConfig };
@@ -8,6 +8,8 @@ class BaseBlockletManager extends EventEmitter {
8
8
  constructor() {
9
9
  super();
10
10
 
11
+ this.setMaxListeners(100);
12
+
11
13
  // HACK: do not emit any events from CLI
12
14
  if (isCLI() && process.env.NODE_ENV !== 'test') {
13
15
  this.emit = (name) => logger.debug('stopped blocklet manager event in CLI', name);
@@ -36,7 +36,6 @@ const {
36
36
  forEachBlocklet,
37
37
  forEachChild,
38
38
  getComponentId,
39
- getComponentName,
40
39
  getComponentBundleId,
41
40
  } = require('@blocklet/meta/lib/util');
42
41
  const getComponentProcessId = require('@blocklet/meta/lib/get-component-process-id');
@@ -73,9 +72,7 @@ const {
73
72
  getFromCache: getAccessibleExternalNodeIp,
74
73
  } = require('../../util/get-accessible-external-node-ip');
75
74
  const {
76
- getComponentDirs,
77
75
  getBlockletMetaFromUrl,
78
- fillBlockletConfigs,
79
76
  ensureBlockletExpanded,
80
77
  getAppSystemEnvironments,
81
78
  getComponentSystemEnvironments,
@@ -104,6 +101,7 @@ const {
104
101
  needBlockletDownload,
105
102
  findAvailableDid,
106
103
  ensureMeta,
104
+ getBlocklet,
107
105
  } = require('../../util/blocklet');
108
106
  const { parseSourceUrl } = require('../../util/registry');
109
107
  const states = require('../../states');
@@ -387,17 +385,22 @@ class BlockletManager extends BaseBlockletManager {
387
385
  fs.mkdirSync(logsDir, { recursive: true });
388
386
  }
389
387
 
390
- // start process
391
- const nodeEnvironments = await states.node.getEnvironments();
392
- await startBlockletProcess(blocklet, {
393
- ...context,
394
- preStart: (b, { env }) =>
395
- hooks.preStart(b, {
388
+ const getHookFn =
389
+ (hookName) =>
390
+ (b, { env }) =>
391
+ hooks[hookName](b, {
396
392
  appDir: b.env.appDir,
397
393
  hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
398
394
  env,
399
395
  did, // root blocklet did,
400
- }),
396
+ });
397
+
398
+ // start process
399
+ const nodeEnvironments = await states.node.getEnvironments();
400
+ await startBlockletProcess(blocklet, {
401
+ ...context,
402
+ preStart: getHookFn('preStart'),
403
+ postStart: getHookFn('postStart'),
401
404
  nodeEnvironments,
402
405
  nodeInfo: await states.node.read(),
403
406
  e2eMode,
@@ -1294,49 +1297,8 @@ class BlockletManager extends BaseBlockletManager {
1294
1297
  return rootBlocklet;
1295
1298
  }
1296
1299
 
1297
- async ensureBlocklet(did, { e2eMode = false, validateEnv = true, throwOnNotExist = true } = {}) {
1298
- if (!isValidDid(did)) {
1299
- throw new Error(`Blocklet did is invalid: ${did}`);
1300
- }
1301
-
1302
- const blocklet = await states.blocklet.getBlocklet(did);
1303
- if (!blocklet) {
1304
- if (throwOnNotExist) {
1305
- throw new Error(`Can not find blocklet in database by did ${did}`);
1306
- }
1307
- return null;
1308
- }
1309
-
1310
- // app settings
1311
- const settings = await states.blockletExtras.getSettings(blocklet.meta.did);
1312
- blocklet.trustedPassports = get(settings, 'trustedPassports') || [];
1313
- blocklet.enablePassportIssuance = get(settings, 'enablePassportIssuance', true);
1314
- blocklet.settings = settings || {};
1315
-
1316
- // app site
1317
- blocklet.site = await this.getSiteByDid(blocklet.meta.did);
1318
-
1319
- await forEachBlocklet(blocklet, async (component, { id, level, ancestors }) => {
1320
- // component env
1321
- component.env = {
1322
- id,
1323
- name: getComponentName(component, ancestors),
1324
- processId: getComponentProcessId(component, ancestors),
1325
- ...getComponentDirs(component, {
1326
- dataDirs: this.dataDirs,
1327
- ensure: true,
1328
- validate: validateEnv,
1329
- ancestors,
1330
- e2eMode: level === 0 ? e2eMode : false,
1331
- }),
1332
- };
1333
-
1334
- // component config
1335
- const configs = await states.blockletExtras.getConfigs([...ancestors.map((x) => x.meta.did), component.meta.did]);
1336
- fillBlockletConfigs(component, configs);
1337
- });
1338
-
1339
- return blocklet;
1300
+ async ensureBlocklet(did, opts = {}) {
1301
+ return getBlocklet({ ...opts, states, dataDirs: this.dataDirs, did });
1340
1302
  }
1341
1303
 
1342
1304
  async hasBlocklet({ did }) {
package/lib/index.js CHANGED
@@ -163,7 +163,7 @@ function ABTNode(options) {
163
163
  } = getRouterHelpers({ dataDirs, routingSnapshot, routerManager, blockletManager, certManager });
164
164
 
165
165
  const nodeAPI = new NodeAPI(states.node);
166
- const teamAPI = new TeamAPI({ states, teamManager });
166
+ const teamAPI = new TeamAPI({ states, teamManager, dataDirs });
167
167
 
168
168
  blockletManager.getRoutingRulesByDid = getRoutingRulesByDid;
169
169
  blockletManager.getSiteByDid = getSiteByDid;
@@ -298,6 +298,8 @@ function ABTNode(options) {
298
298
  // Passport
299
299
  revokeUserPassport: teamAPI.revokeUserPassport.bind(teamAPI),
300
300
  enableUserPassport: teamAPI.enableUserPassport.bind(teamAPI),
301
+ issuePassportToUser: teamAPI.issuePassportToUser.bind(teamAPI),
302
+
301
303
  createPassportIssuance: teamAPI.createPassportIssuance.bind(teamAPI),
302
304
  getPassportIssuances: teamAPI.getPassportIssuances.bind(teamAPI),
303
305
  getPassportIssuance: teamAPI.getPassportIssuance.bind(teamAPI),
@@ -311,13 +313,13 @@ function ABTNode(options) {
311
313
  verifyChallenge: states.challenge.verify.bind(states.challenge),
312
314
 
313
315
  // Notifications
314
- getNotifications: states.notification.find.bind(states.notification),
316
+ getNotifications: states.notification.findPaginated.bind(states.notification),
315
317
  readNotifications: states.notification.read.bind(states.notification),
316
318
  unreadNotifications: states.notification.unread.bind(states.notification),
317
319
 
318
320
  // AuditLog
319
321
  createAuditLog: (params) => states.auditLog.create(params, instance),
320
- getAuditLogs: states.auditLog.find.bind(states.auditLog),
322
+ getAuditLogs: states.auditLog.findPaginated.bind(states.auditLog),
321
323
 
322
324
  // Routing
323
325
  routerManager,
@@ -392,7 +394,6 @@ function ABTNode(options) {
392
394
 
393
395
  const events = createEvents({
394
396
  blockletManager,
395
- blockletRegistry,
396
397
  ensureBlockletRouting,
397
398
  ensureBlockletRoutingForUpgrade,
398
399
  removeBlockletRouting,
@@ -19,13 +19,15 @@ const validatePassport = (passport) => {
19
19
  const getUserName = (context) => get(context, 'user.fullName', '');
20
20
 
21
21
  class AccessKeyState extends BaseState {
22
- constructor(baseDir, options = {}) {
23
- super(baseDir, { filename: 'access_key.db', ...options });
24
-
25
- this.db.ensureIndex({ fieldName: 'accessKeyId', unique: true }, (error) => {
26
- if (error) {
27
- logger.error('ensure unique index failed', { error });
28
- }
22
+ constructor(baseDir, config = {}) {
23
+ super(baseDir, { filename: 'access_key.db', ...config });
24
+
25
+ this.onReady(() => {
26
+ this.ensureIndex({ fieldName: 'accessKeyId', unique: true }, (error) => {
27
+ if (error) {
28
+ logger.error('ensure unique index failed', { error });
29
+ }
30
+ });
29
31
  });
30
32
  }
31
33
 
@@ -48,7 +50,7 @@ class AccessKeyState extends BaseState {
48
50
  data.createdBy = getUserName(context);
49
51
  data.updatedBy = getUserName(context);
50
52
 
51
- const doc = await this.asyncDB.insert(data);
53
+ const doc = await this.insert(data);
52
54
  return {
53
55
  ...doc,
54
56
  accessKeySecret: toBase58(wallet.secretKey),
@@ -67,7 +69,7 @@ class AccessKeyState extends BaseState {
67
69
  if (!accessKeyId) {
68
70
  throw new Error('accessKeyId should not be empty');
69
71
  }
70
- const doc = await this.asyncDB.findOne({ accessKeyId });
72
+ const doc = await this.findOne({ accessKeyId });
71
73
  return doc;
72
74
  }
73
75
 
@@ -82,7 +84,7 @@ class AccessKeyState extends BaseState {
82
84
  if (!accessKeyId) {
83
85
  throw new Error('accessKeyId should not be empty');
84
86
  }
85
- const doc = await this.asyncDB.findOne({ accessKeyId });
87
+ const doc = await this.findOne({ accessKeyId });
86
88
  if (!doc) {
87
89
  throw new Error(`Access Key Id ${accessKeyId} does not exist`);
88
90
  }
@@ -92,7 +94,7 @@ class AccessKeyState extends BaseState {
92
94
  doc.passport = passport;
93
95
  doc.updatedBy = getUserName(context);
94
96
 
95
- await this.asyncDB.update({ accessKeyId }, { $set: doc });
97
+ await super.update({ accessKeyId }, { $set: doc });
96
98
  return doc;
97
99
  }
98
100
 
@@ -101,12 +103,12 @@ class AccessKeyState extends BaseState {
101
103
  if (!accessKeyId) {
102
104
  throw new Error('accessKeyId should not be empty');
103
105
  }
104
- const doc = await this.asyncDB.findOne({ accessKeyId });
106
+ const doc = await this.findOne({ accessKeyId });
105
107
  if (!doc) {
106
108
  throw new Error(`Access Key Id ${accessKeyId} does not exist`);
107
109
  }
108
110
  doc.lastUsedAt = new Date();
109
- await this.asyncDB.update({ accessKeyId }, { $set: doc });
111
+ await super.update({ accessKeyId }, { $set: doc });
110
112
  return doc;
111
113
  }
112
114
 
@@ -116,7 +118,7 @@ class AccessKeyState extends BaseState {
116
118
  if (!accessKeyId) {
117
119
  throw new Error('accessKeyId should not be empty');
118
120
  }
119
- const num = await this.asyncDB.remove({ accessKeyId });
121
+ const num = await super.remove({ accessKeyId });
120
122
  if (num <= 0) {
121
123
  throw new Error(`Access Key Id ${accessKeyId} does not exist`);
122
124
  }
@@ -3,7 +3,7 @@ const pick = require('lodash/pick');
3
3
  const get = require('lodash/get');
4
4
  const joinUrl = require('url-join');
5
5
  const { getDisplayName } = require('@blocklet/meta/lib/util');
6
- const { BLOCKLET_SITE_GROUP_SUFFIX } = require('@abtnode/constant');
6
+ const { BLOCKLET_SITE_GROUP_SUFFIX, NODE_SERVICES } = require('@abtnode/constant');
7
7
  const logger = require('@abtnode/logger')('@abtnode/core:states:audit-log');
8
8
 
9
9
  const BaseState = require('./base');
@@ -177,6 +177,8 @@ const getLogContent = async (action, args, context, result, info, node) => {
177
177
  return `updated trusted passport issuers to following for ${team}: \n${args.trustedPassports.map(x => `- ${x.remark}: ${x.issuerDid}`).join('\n')}`; // prettier-ignore
178
178
  case 'delegateTransferNFT':
179
179
  return `${args.owner} ${args.reason}`;
180
+ case 'issuePassportToUser':
181
+ return `issued **${args.role}** passport to ${user}`;
180
182
 
181
183
  // accessKeys
182
184
  case 'createAccessKey':
@@ -344,9 +346,15 @@ const getScope = (args = {}) => {
344
346
  return null;
345
347
  };
346
348
 
349
+ const fixActor = (actor) => {
350
+ if ([NODE_SERVICES.AUTH_SERVICE, 'blocklet'].includes(actor?.role)) {
351
+ actor.role = '';
352
+ }
353
+ };
354
+
347
355
  class AuditLogState extends BaseState {
348
- constructor(baseDir, options = {}) {
349
- super(baseDir, { filename: 'audit-log.db', ...options });
356
+ constructor(baseDir, config = {}) {
357
+ super(baseDir, { filename: 'audit-log.db', ...config });
350
358
  }
351
359
 
352
360
  /**
@@ -399,7 +407,9 @@ class AuditLogState extends BaseState {
399
407
  const { ip, ua, user } = context;
400
408
  const [info, uaInfo] = await Promise.all([node.states.node.read(), parse(ua)]);
401
409
 
402
- const data = await this.asyncDB.insert({
410
+ fixActor(user);
411
+
412
+ const data = await this.insert({
403
413
  scope: getScope(args) || info.did, // server or blocklet did
404
414
  action,
405
415
  category: await getLogCategory(action, args, context, result, info, node),
@@ -420,7 +430,7 @@ class AuditLogState extends BaseState {
420
430
  });
421
431
  }
422
432
 
423
- async find({ scope, category, paging } = {}) {
433
+ async findPaginated({ scope, category, paging } = {}) {
424
434
  const conditions = {};
425
435
  if (scope) {
426
436
  conditions.scope = scope;
@@ -429,7 +439,7 @@ class AuditLogState extends BaseState {
429
439
  conditions.category = category;
430
440
  }
431
441
 
432
- return this.paginate(conditions, { createdAt: -1 }, { pageSize: 20, ...paging });
442
+ return super.paginate(conditions, { createdAt: -1 }, { pageSize: 20, ...paging });
433
443
  }
434
444
  }
435
445
 
@@ -4,8 +4,8 @@ const logger = require('@abtnode/logger')('@abtnode/core:states');
4
4
  const { isCLI } = require('../util');
5
5
 
6
6
  class BaseState extends DB {
7
- constructor(baseDir, options) {
8
- super(baseDir, options);
7
+ constructor(baseDir, config) {
8
+ super(baseDir, config);
9
9
 
10
10
  // HACK: do not emit any events from CLI
11
11
  if (isCLI() && process.env.NODE_ENV !== 'test') {
@@ -12,8 +12,8 @@ const { mergeConfigs, parseConfigs } = require('../blocklet/extras');
12
12
  const noop = (k) => (v) => v[k];
13
13
 
14
14
  class BlockletExtrasState extends BaseState {
15
- constructor(baseDir, options = {}) {
16
- super(baseDir, { filename: 'blocklet_extras.db', ...options });
15
+ constructor(baseDir, config = {}) {
16
+ super(baseDir, { filename: 'blocklet_extras.db', ...config });
17
17
 
18
18
  this.extras = [
19
19
  // environment
@@ -82,10 +82,10 @@ class BlockletExtrasState extends BaseState {
82
82
  // eslint-disable-next-line no-param-reassign
83
83
  dids = [].concat(dids);
84
84
  const [rootDid, ...childDids] = dids;
85
- const { dek } = this.options;
85
+ const { dek } = this.config;
86
86
  const { name, afterGet = noop('data') } = extra;
87
87
 
88
- let item = await this.asyncDB.findOne({ did: rootDid });
88
+ let item = await this.findOne({ did: rootDid });
89
89
  while (item && childDids.length) {
90
90
  const did = childDids.shift();
91
91
  item = (item.children || []).find((x) => x.did === did);
@@ -104,9 +104,9 @@ class BlockletExtrasState extends BaseState {
104
104
  // eslint-disable-next-line no-param-reassign
105
105
  dids = [].concat(dids);
106
106
  const [rootDid, ...childDids] = dids;
107
- const { dek } = this.options;
107
+ const { dek } = this.config;
108
108
  const { name, beforeSet = noop('cur') } = extra;
109
- const exist = await this.asyncDB.findOne({ did: rootDid });
109
+ const exist = await this.findOne({ did: rootDid });
110
110
 
111
111
  const item = exist || { did: rootDid };
112
112
  let component = item;
@@ -126,7 +126,7 @@ class BlockletExtrasState extends BaseState {
126
126
  component[name] = newData;
127
127
 
128
128
  if (!exist) {
129
- await this.asyncDB.insert(item);
129
+ await this.insert(item);
130
130
  logger.info('create extra success', { name, dids });
131
131
  } else {
132
132
  await this.update(item._id, item);
@@ -143,7 +143,7 @@ class BlockletExtrasState extends BaseState {
143
143
  dids = [].concat(dids);
144
144
  const [rootDid, ...childDids] = dids;
145
145
  const { name } = extra;
146
- const item = await this.asyncDB.findOne({ did: rootDid });
146
+ const item = await this.findOne({ did: rootDid });
147
147
 
148
148
  if (!item) {
149
149
  return null;
@@ -170,9 +170,9 @@ class BlockletExtrasState extends BaseState {
170
170
 
171
171
  generateListFn(extra) {
172
172
  return async () => {
173
- const { dek } = this.options;
173
+ const { dek } = this.config;
174
174
  const { name, afterGet = noop('data') } = extra;
175
- const docs = await this.asyncDB.find({});
175
+ const docs = await this.find({});
176
176
  const list = docs
177
177
  .filter((x) => x[name])
178
178
  .map((x) => ({
@@ -41,6 +41,7 @@ const formatBlocklet = (blocklet, phase, dek) => {
41
41
 
42
42
  if (phase === 'onRead') {
43
43
  b.children = b.children || [];
44
+ b.environments = b.environments || [];
44
45
  }
45
46
 
46
47
  if (!b.environments || !b.meta || !dek) {
@@ -78,14 +79,14 @@ class BlockletState extends BaseState {
78
79
  /**
79
80
  * Creates an instance of BlockletState
80
81
  * @param {string} baseDir
81
- * @param {object} options
82
- * @param {string} options.blockletPort - from which port to start new blocklets
82
+ * @param {object} config
83
+ * @param {string} config.blockletPort - from which port to start new blocklets
83
84
  * @memberof BlockletState
84
85
  */
85
- constructor(baseDir, options = {}) {
86
- super(baseDir, { filename: 'blocklet.db', ...options });
86
+ constructor(baseDir, config = {}) {
87
+ super(baseDir, { filename: 'blocklet.db', ...config });
87
88
 
88
- this.defaultPort = options.blockletPort || 5555;
89
+ this.defaultPort = config.blockletPort || 5555;
89
90
  }
90
91
 
91
92
  getBlocklet(did) {
@@ -94,12 +95,12 @@ class BlockletState extends BaseState {
94
95
  resolve(null);
95
96
  }
96
97
 
97
- this.db.findOne({ $or: [{ 'meta.did': did }, { appDid: did }] }, (err, doc) => {
98
+ this.findOne({ $or: [{ 'meta.did': did }, { appDid: did }] }, (err, doc) => {
98
99
  if (err) {
99
100
  return reject(err);
100
101
  }
101
102
 
102
- return resolve(doc ? formatBlocklet(doc, 'onRead', this.options.dek) : null);
103
+ return resolve(doc ? formatBlocklet(doc, 'onRead', this.config.dek) : null);
103
104
  });
104
105
  });
105
106
  }
@@ -110,7 +111,7 @@ class BlockletState extends BaseState {
110
111
  resolve(null);
111
112
  }
112
113
 
113
- this.db.findOne({ $or: [{ 'meta.did': did }, { appDid: did }] }, { status: 1 }, (err, doc) => {
114
+ this.findOne({ $or: [{ 'meta.did': did }, { appDid: did }] }, { status: 1 }, (err, doc) => {
114
115
  if (err) {
115
116
  return reject(err);
116
117
  }
@@ -126,7 +127,7 @@ class BlockletState extends BaseState {
126
127
  resolve(false);
127
128
  }
128
129
 
129
- this.db.count({ $or: [{ 'meta.did': did }, { appDid: did }] }, (err, count) => {
130
+ this.count({ $or: [{ 'meta.did': did }, { appDid: did }] }, (err, count) => {
130
131
  if (err) {
131
132
  return reject(err);
132
133
  }
@@ -138,15 +139,15 @@ class BlockletState extends BaseState {
138
139
 
139
140
  getBlocklets(query = {}, projection) {
140
141
  return new Promise((resolve, reject) => {
141
- this.db
142
- .find(query, projection)
143
- .sort('-createdAt')
144
- .exec((err, docs) => {
142
+ this.cursor(query)
143
+ .projection(projection)
144
+ .sort({ createdAt: -1 })
145
+ .exec((err, docs = []) => {
145
146
  if (err) {
146
147
  return reject(err);
147
148
  }
148
149
 
149
- return resolve(docs.filter(Boolean).map((doc) => formatBlocklet(doc, 'onRead', this.options.dek)));
150
+ return resolve(docs.filter(Boolean).map((doc) => formatBlocklet(doc, 'onRead', this.config.dek)));
150
151
  });
151
152
  });
152
153
  }
@@ -160,13 +161,13 @@ class BlockletState extends BaseState {
160
161
  return reject(new Error(`Try to remove non-existing blocklet ${did}`));
161
162
  }
162
163
 
163
- this.db.remove({ _id: doc._id }, (err) => {
164
+ this.remove({ _id: doc._id }, (err) => {
164
165
  if (err) {
165
166
  return reject(err);
166
167
  }
167
168
 
168
169
  this.emit('remove', doc);
169
- return resolve(formatBlocklet(doc, 'onRead', this.options.dek));
170
+ return resolve(formatBlocklet(doc, 'onRead', this.config.dek));
170
171
  });
171
172
  })
172
173
  );
@@ -208,7 +209,7 @@ class BlockletState extends BaseState {
208
209
  fixChildren(children);
209
210
 
210
211
  // add to db
211
- this.db.insert(
212
+ this.insert(
212
213
  {
213
214
  appDid: null, // will updated later when updating blocklet environments
214
215
  mode,
@@ -248,7 +249,7 @@ class BlockletState extends BaseState {
248
249
  }
249
250
 
250
251
  try {
251
- const formatted = formatBlocklet(cloneDeep(updates), 'onUpdate', this.options.dek);
252
+ const formatted = formatBlocklet(cloneDeep(updates), 'onUpdate', this.config.dek);
252
253
  const newDoc = await this.updateById(doc._id, { $set: formatted });
253
254
  resolve(newDoc);
254
255
  } catch (err) {
@@ -455,7 +456,7 @@ class BlockletState extends BaseState {
455
456
 
456
457
  const doc = await this.getBlocklet(did);
457
458
  if (doc.status === status && !children) {
458
- return formatBlocklet(doc, 'onRead', this.options.dek);
459
+ return formatBlocklet(doc, 'onRead', this.config.dek);
459
460
  }
460
461
 
461
462
  const updates = { status, startedAt: undefined, stoppedAt: undefined };