@abtnode/core 1.7.26 → 1.8.1

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/cert.js CHANGED
@@ -103,14 +103,16 @@ class Cert extends EventEmitter {
103
103
 
104
104
  async add(data) {
105
105
  if (!data.certificate || !data.privateKey) {
106
- throw new Error('certificate and privateKey is required');
106
+ throw new Error('certificate and privateKey are required');
107
107
  }
108
108
 
109
109
  Cert.fixCertificate(data);
110
110
 
111
- await this.manager.add(data);
112
- logger.info('add certificate result', { name: data.name });
113
- this.emit('cert.added', data);
111
+ const result = await this.manager.add(data);
112
+ logger.info('add certificate result', { name: result.name });
113
+ this.emit('cert.added', result);
114
+
115
+ return result;
114
116
  }
115
117
 
116
118
  async issue({ domain }) {
@@ -120,7 +122,10 @@ class Cert extends EventEmitter {
120
122
  }
121
123
 
122
124
  async upsertByDomain(data) {
123
- return this.manager.upsertByDomain(data);
125
+ const result = await this.manager.upsertByDomain(data);
126
+ this.emit('cert.updated', result);
127
+
128
+ return result;
124
129
  }
125
130
 
126
131
  async update(data) {
package/lib/event.js CHANGED
@@ -5,6 +5,7 @@ const { wipeSensitiveData } = require('@blocklet/meta/lib/util');
5
5
  const logger = require('@abtnode/logger')('@abtnode/core:event');
6
6
  const { BLOCKLET_MODES, BlockletStatus, BlockletSource, BlockletEvents } = require('@blocklet/meta/lib/constants');
7
7
  const { EVENTS } = require('@abtnode/constant');
8
+ const handleInstanceInStore = require('@abtnode/util/lib/public-to-store');
8
9
 
9
10
  const eventHub =
10
11
  process.env.NODE_ENV === 'test' ? require('@arcblock/event-hub/single') : require('@arcblock/event-hub');
@@ -160,6 +161,13 @@ module.exports = ({
160
161
  await handleBlockletUpgrade(eventName, payload);
161
162
  } else if ([BlockletEvents.removed].includes(eventName)) {
162
163
  await handleBlockletRemove(eventName, payload);
164
+ } else if ([BlockletEvents.started].includes(eventName)) {
165
+ try {
166
+ const blocklet = await blockletManager.ensureBlocklet(payload.meta.did);
167
+ await handleInstanceInStore(blocklet);
168
+ } catch (error) {
169
+ logger.error('handleInstanceInStore failed', { error });
170
+ }
163
171
  }
164
172
 
165
173
  if (payload.blocklet && !payload.meta) {
package/lib/index.js CHANGED
@@ -117,6 +117,7 @@ function ABTNode(options) {
117
117
  maintainerEmail: DEFAULT_CERTIFICATE_EMAIL,
118
118
  dataDir: dataDirs.certManagerModule,
119
119
  });
120
+
120
121
  const routerManager = new RouterManager({ certManager });
121
122
  const routingSnapshot = new RoutingSnapshot({
122
123
  baseDir: dataDirs.core,
@@ -126,13 +127,18 @@ function ABTNode(options) {
126
127
  return { sites };
127
128
  },
128
129
  });
130
+
129
131
  const blockletRegistry = new BlockletRegistry();
132
+
133
+ const teamManager = new TeamManager({ nodeDid: options.nodeDid, dataDirs, states });
134
+
130
135
  const blockletManager = new BlockletManager({
131
136
  dataDirs,
132
137
  startQueue,
133
138
  installQueue,
134
139
  registry: blockletRegistry,
135
140
  daemon: options.daemon,
141
+ teamManager,
136
142
  });
137
143
  blockletManager.setMaxListeners(0);
138
144
 
@@ -156,7 +162,6 @@ function ABTNode(options) {
156
162
  getRouterProvider,
157
163
  } = getRouterHelpers({ dataDirs, routingSnapshot, routerManager, blockletManager, certManager });
158
164
 
159
- const teamManager = new TeamManager({ nodeDid: options.nodeDid, dataDirs, states });
160
165
  const nodeAPI = new NodeAPI(states.node, blockletRegistry);
161
166
  const teamAPI = new TeamAPI({ states, teamManager });
162
167
 
@@ -221,6 +226,7 @@ function ABTNode(options) {
221
226
  getLatestBlockletVersion: blockletManager.getLatestBlockletVersion.bind(blockletManager),
222
227
  getBlockletMetaFromUrl: blockletManager.getMetaFromUrl.bind(blockletManager),
223
228
  resetBlocklet: blockletManager.reset.bind(blockletManager),
229
+ configPublicToStore: blockletManager.configPublicToStore.bind(blockletManager),
224
230
 
225
231
  deleteBlockletProcess: blockletManager.deleteProcess.bind(blockletManager),
226
232
 
@@ -272,6 +278,7 @@ function ABTNode(options) {
272
278
  // Account
273
279
  getUsers: teamAPI.getUsers.bind(teamAPI),
274
280
  getUsersCount: teamAPI.getUsersCount.bind(teamAPI),
281
+ getUsersCountPerRole: teamAPI.getUsersCountPerRole.bind(teamAPI),
275
282
  getUser: teamAPI.getUser.bind(teamAPI),
276
283
  getOwner: teamAPI.getOwner.bind(teamAPI),
277
284
  getNodeUsers: () => teamAPI.getUsers({ teamDid: options.nodeDid }),
@@ -434,7 +434,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
434
434
  isProtected: true,
435
435
  });
436
436
 
437
- logger.info('dashboard certificate updated');
437
+ logger.info('dashboard certificate updated', { domain });
438
438
  } catch (error) {
439
439
  logger.error('update dashboard certificate failed', { error });
440
440
  throw error;
@@ -1103,6 +1103,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1103
1103
  certManager.on('cert.added', () => providers[providerName].reload());
1104
1104
  certManager.on('cert.removed', () => providers[providerName].reload());
1105
1105
  certManager.on('cert.issued', () => providers[providerName].reload());
1106
+ certManager.on('cert.updated', () => providers[providerName].reload());
1106
1107
 
1107
1108
  await providers[providerName].start();
1108
1109
  }
@@ -95,19 +95,16 @@ class Router {
95
95
  }
96
96
 
97
97
  async updateRoutingTable() {
98
+ logger.info('update routing table');
99
+
98
100
  const { sites, certificates, headers = {}, services = [], nodeInfo = {} } = (await this.getRoutingParams()) || {};
99
101
  if (!Array.isArray(sites)) {
100
102
  logger.error('sites is not an array', { sites });
101
103
  return;
102
104
  }
103
105
 
104
- logger.info('updateRoutingTable sites:', { sites });
105
-
106
106
  this.routingTable = getRoutingTable({ sites, nodeInfo });
107
107
 
108
- logger.info('updateRoutingTable routingTable:', { routingTable: this.routingTable });
109
- logger.info('updateRoutingTable certificates:', { certificates: certificates.map((item) => item.domain) });
110
-
111
108
  const requestLimit = nodeInfo.routing.requestLimit || { enable: false, rate: GATEWAY_REQ_LIMIT.min };
112
109
  if (requestLimit.enabled) {
113
110
  requestLimit.maxInstantRate = requestLimit.rate >= 20 ? 20 : requestLimit.rate + 20;
@@ -8,7 +8,6 @@ const path = require('path');
8
8
  const os = require('os');
9
9
  const fse = require('fs-extra');
10
10
  const get = require('lodash/get');
11
- const { Certificate, PrivateKey } = require('@fidm/x509');
12
11
  const { EventEmitter } = require('events');
13
12
  const uuid = require('uuid');
14
13
  const isUrl = require('is-url');
@@ -564,39 +563,6 @@ class RouterManager extends EventEmitter {
564
563
  }
565
564
  }
566
565
 
567
- validateCertificate(cert, domain) {
568
- const certificate = Certificate.fromPEM(cert.certificate);
569
- const privateKey = PrivateKey.fromPEM(cert.privateKey);
570
-
571
- const data = Buffer.allocUnsafe(100);
572
- const signature = privateKey.sign(data, 'sha512');
573
- if (!certificate.publicKey.verify(data, signature, 'sha512')) {
574
- throw new Error('Invalid certificate: signature verify failed');
575
- }
576
-
577
- const certDomain = get(certificate, 'subject.commonName', '');
578
- if (domain && domain !== certDomain) {
579
- throw new Error('Invalid certificate: domain does not match');
580
- }
581
-
582
- const validFrom = get(certificate, 'validFrom', '');
583
- if (!validFrom || new Date(validFrom).getTime() > Date.now()) {
584
- throw new Error('Invalid certificate: not in valid period');
585
- }
586
- const validTo = get(certificate, 'validTo', '');
587
- if (!validTo || new Date(validTo).getTime() < Date.now()) {
588
- throw new Error('Invalid certificate: not in valid period');
589
- }
590
-
591
- return certificate;
592
- }
593
-
594
- fixCertificate(entity) {
595
- // Hack: this logic exists because gql does not allow line breaks in arg values
596
- entity.privateKey = entity.privateKey.split('|').join('\n');
597
- entity.certificate = entity.certificate.split('|').join('\n');
598
- }
599
-
600
566
  fixRootBlockletRule(rule) {
601
567
  if (!rule.id) {
602
568
  rule.id = uuid.v4();
@@ -114,7 +114,11 @@ const getLogContent = async (action, args, context, result, info, node) => {
114
114
  return `upgraded blocklet ${getBlockletInfo(result, info)} to v${result.meta.version}`;
115
115
  case 'updateChildBlocklets':
116
116
  return `upgraded components for blocklet ${getBlockletInfo(result, info)}`;
117
-
117
+ case 'configPublicToStore':
118
+ if (args.publicToStore) {
119
+ return `set publicToStore to true for blocklet ${getBlockletInfo(result, info)}`;
120
+ }
121
+ return `set publicToStore to false for blocklet ${getBlockletInfo(result, info)}`;
118
122
  // store
119
123
  case 'addBlockletStore':
120
124
  return `added blocklet store ${args.url}`;
@@ -237,6 +241,7 @@ const getLogCategory = (action) => {
237
241
  case 'configBlocklet':
238
242
  case 'upgradeBlocklet':
239
243
  case 'updateChildBlocklets':
244
+ case 'configPublicToStore':
240
245
  return 'blocklet';
241
246
 
242
247
  // store
@@ -10,7 +10,13 @@ class SessionState extends BaseState {
10
10
 
11
11
  this.db.ensureIndex({ fieldName: 'createdAt' }, (error) => {
12
12
  if (error) {
13
- logger.error('ensure index failed', { error });
13
+ logger.error('ensure createdAt index failed', { error });
14
+ }
15
+ });
16
+
17
+ this.db.ensureIndex({ fieldName: 'expireDate', expireAfterSeconds: 0 }, (error) => {
18
+ if (error) {
19
+ logger.error('ensure expireDate index failed', { error });
14
20
  }
15
21
  });
16
22
  }
@@ -1,3 +1,5 @@
1
+ const pickBy = require('lodash/pickBy');
2
+
1
3
  const logger = require('@abtnode/logger')('@abtnode/core:states:user');
2
4
  const { PASSPORT_STATUS } = require('@abtnode/constant');
3
5
  const BaseState = require('./base');
@@ -7,6 +9,8 @@ const fixPassports = (doc) => {
7
9
  doc.passports = (doc.passports || []).filter((x) => x.id);
8
10
  };
9
11
 
12
+ const isNullOrUndefined = (x) => x === undefined || x === null;
13
+
10
14
  class User extends BaseState {
11
15
  constructor(baseDir, options = {}) {
12
16
  super(baseDir, { filename: 'user.db', ...options });
@@ -85,13 +89,45 @@ class User extends BaseState {
85
89
  return doc;
86
90
  }
87
91
 
88
- async getUsers() {
89
- const docs = await super.find();
90
- if (docs) {
91
- docs.forEach(fixPassports); // backward compatible
92
+ async getUsers({ query, sort, paging: inputPaging } = {}) {
93
+ const { approved, role, search } = query || {};
94
+
95
+ // make query param
96
+ const queryParam = {};
97
+
98
+ if (!isNullOrUndefined(approved)) {
99
+ queryParam.approved = !!approved;
100
+ }
101
+
102
+ if (search) {
103
+ queryParam.$or = [{ fullName: search }, { did: search }];
104
+ }
105
+
106
+ if (role && role !== '$all') {
107
+ if (role === '$none') {
108
+ queryParam.passports = { $size: 0 };
109
+ } else {
110
+ queryParam.passports = { $elemMatch: { name: role, status: PASSPORT_STATUS.VALID } };
111
+ }
112
+ }
113
+
114
+ const sortParam = pickBy(sort, (x) => !isNullOrUndefined(x));
115
+
116
+ if (!Object.keys(sortParam).length) {
117
+ sortParam.createdAt = -1;
118
+ }
119
+
120
+ // get data
121
+ const { list, paging } = await this.paginate(queryParam, sortParam, inputPaging);
122
+
123
+ if (list) {
124
+ list.forEach(fixPassports); // backward compatible
92
125
  }
93
126
 
94
- return docs;
127
+ return {
128
+ list,
129
+ paging,
130
+ };
95
131
  }
96
132
 
97
133
  async getUser(did) {
@@ -17,6 +17,18 @@ const { isCLI } = require('../util');
17
17
 
18
18
  const rbacCreationLock = new Lock('rbac-creation-lock');
19
19
 
20
+ const closeDatabase = async (db) =>
21
+ new Promise((resolve, reject) => {
22
+ db.closeDatabase((err) => {
23
+ if (err) {
24
+ reject(err);
25
+ return;
26
+ }
27
+
28
+ resolve();
29
+ });
30
+ });
31
+
20
32
  class TeamManager extends EventEmitter {
21
33
  constructor({ nodeDid, dataDirs, states }) {
22
34
  super();
@@ -48,10 +60,6 @@ class TeamManager extends EventEmitter {
48
60
  });
49
61
  });
50
62
 
51
- this.states.blocklet.on('remove', ({ meta: { did } }) => {
52
- this.cache[did] = null;
53
- });
54
-
55
63
  // init blocklet
56
64
  this.states.blocklet
57
65
  .getBlocklets()
@@ -280,6 +288,28 @@ class TeamManager extends EventEmitter {
280
288
  return owner;
281
289
  }
282
290
 
291
+ async deleteTeam(did) {
292
+ if (this.cache[did]) {
293
+ try {
294
+ if (this.cache[did].rbac) {
295
+ await closeDatabase(this.cache[did].rbac.storage.db);
296
+ }
297
+
298
+ if (this.cache[did].user) {
299
+ await closeDatabase(this.cache[did].user.db);
300
+ }
301
+
302
+ if (this.cache[did].session) {
303
+ await closeDatabase(this.cache[did].session.db);
304
+ }
305
+ } catch (err) {
306
+ logger.error('Failed to close database', { did, err });
307
+ }
308
+ }
309
+
310
+ this.cache[did] = null;
311
+ }
312
+
283
313
  // =======
284
314
  // Private
285
315
  // =======
@@ -44,7 +44,6 @@ const getBlockletInfo = require('@blocklet/meta/lib/info');
44
44
  const { validateMeta, fixAndValidateService } = require('@blocklet/meta/lib/validate');
45
45
  const {
46
46
  forEachBlocklet,
47
- isFreeBlocklet,
48
47
  getDisplayName,
49
48
  findWebInterface,
50
49
  forEachBlockletSync,
@@ -338,7 +337,8 @@ const getRuntimeEnvironments = (blocklet, nodeEnvironments, ancestors) => {
338
337
  };
339
338
  };
340
339
 
341
- const isUsefulError = (err) => err && err.message !== 'process or namespace not found';
340
+ const isUsefulError = (err) =>
341
+ err && err.message !== 'process or namespace not found' && !/^Process \d+ not found$/.test(err.message);
342
342
 
343
343
  const getHealthyCheckTimeout = (blocklet, { checkHealthImmediately } = {}) => {
344
344
  let minConsecutiveTime = 5000;
@@ -724,8 +724,6 @@ const parseChildrenFromMeta = async (src, context = {}) => {
724
724
 
725
725
  validateBlockletMeta(m, { ensureDist: true });
726
726
 
727
- verifyPurchase(m, context);
728
-
729
727
  if (!isComponentBlocklet(m)) {
730
728
  throw new Error(`The blocklet cannot be a component: ${m.title}`);
731
729
  }
@@ -936,11 +934,6 @@ const pruneBlockletBundle = async ({ blocklets, installDir, blockletSettings })
936
934
 
937
935
  const fillAppDirs = async (dir, level = 'root') => {
938
936
  if (level === 'version') {
939
- if (!fs.existsSync(path.join(dir, 'blocklet.yml'))) {
940
- logger.error('blocklet.yml does not exist in blocklet bundle dir', { dir });
941
- return;
942
- }
943
-
944
937
  appDirs.push({
945
938
  key: path.relative(installDir, dir),
946
939
  dir,
@@ -1227,18 +1220,6 @@ const needBlockletDownload = (blocklet, oldBlocklet) => {
1227
1220
  return !(get(oldBlocklet, 'meta.dist.integrity') === get(blocklet, 'meta.dist.integrity'));
1228
1221
  };
1229
1222
 
1230
- const verifyPurchase = (meta, opts = {}) => {
1231
- if (opts.blockletPurchaseVerified) {
1232
- return true;
1233
- }
1234
-
1235
- if (isFreeBlocklet(meta)) {
1236
- return true;
1237
- }
1238
-
1239
- throw new Error('Can not install a non-free blocklet directly');
1240
- };
1241
-
1242
1223
  const findAvailableDid = (meta, siblings) => {
1243
1224
  const reg = /-(\d+)$/;
1244
1225
  const match = reg.exec(meta.name);
@@ -1316,7 +1297,6 @@ module.exports = {
1316
1297
  getDiffFiles,
1317
1298
  getBundleDir,
1318
1299
  needBlockletDownload,
1319
- verifyPurchase,
1320
1300
  findAvailableDid,
1321
1301
  ensureMeta,
1322
1302
  getSourceUrlsFromConfig,
package/lib/util/index.js CHANGED
@@ -398,7 +398,10 @@ const validateUrl = async (url, expectedHttpResTypes = ['application/json', 'tex
398
398
  throw new Error(`Cannot get content-type from ${url}: ${err.message}`);
399
399
  }
400
400
 
401
- if (expectedHttpResTypes.some((x) => res.headers['content-type'].includes(x)) === false) {
401
+ if (
402
+ res.headers['content-type'] &&
403
+ expectedHttpResTypes.some((x) => res.headers['content-type'].includes(x)) === false
404
+ ) {
402
405
  throw new Error(`Unexpected content-type from ${url}: ${res.headers['content-type']}`);
403
406
  }
404
407
 
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.7.26",
6
+ "version": "1.8.1",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,27 +19,28 @@
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
- "@abtnode/certificate-manager": "1.7.26",
23
- "@abtnode/constant": "1.7.26",
24
- "@abtnode/cron": "1.7.26",
25
- "@abtnode/db": "1.7.26",
26
- "@abtnode/logger": "1.7.26",
27
- "@abtnode/queue": "1.7.26",
28
- "@abtnode/rbac": "1.7.26",
29
- "@abtnode/router-provider": "1.7.26",
30
- "@abtnode/static-server": "1.7.26",
31
- "@abtnode/timemachine": "1.7.26",
32
- "@abtnode/util": "1.7.26",
22
+ "@abtnode/certificate-manager": "1.8.1",
23
+ "@abtnode/constant": "1.8.1",
24
+ "@abtnode/cron": "1.8.1",
25
+ "@abtnode/db": "1.8.1",
26
+ "@abtnode/logger": "1.8.1",
27
+ "@abtnode/queue": "1.8.1",
28
+ "@abtnode/rbac": "1.8.1",
29
+ "@abtnode/router-provider": "1.8.1",
30
+ "@abtnode/static-server": "1.8.1",
31
+ "@abtnode/timemachine": "1.8.1",
32
+ "@abtnode/util": "1.8.1",
33
33
  "@arcblock/did": "1.17.0",
34
34
  "@arcblock/did-motif": "^1.1.10",
35
35
  "@arcblock/did-util": "1.17.0",
36
36
  "@arcblock/event-hub": "1.17.0",
37
+ "@arcblock/jwt": "^1.17.0",
37
38
  "@arcblock/pm2-events": "^0.0.5",
38
39
  "@arcblock/vc": "1.17.0",
39
- "@blocklet/meta": "1.7.26",
40
+ "@blocklet/meta": "1.8.1",
40
41
  "@fidm/x509": "^1.2.1",
41
- "@nedb/core": "^1.2.2",
42
- "@nedb/multi": "^1.2.2",
42
+ "@nedb/core": "^1.3.1",
43
+ "@nedb/multi": "^1.3.1",
43
44
  "@ocap/mcrypto": "1.17.0",
44
45
  "@ocap/util": "1.17.0",
45
46
  "@ocap/wallet": "1.17.0",
@@ -80,5 +81,5 @@
80
81
  "express": "^4.17.1",
81
82
  "jest": "^27.4.5"
82
83
  },
83
- "gitHead": "b7ef9b4ddb18f7a0c3898177fe06d9cefe966566"
84
+ "gitHead": "c970b8a386bebd7fe6dbc8b8eedf8bd8328b4bb5"
84
85
  }