@abtnode/core 1.6.5 → 1.6.9

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.
@@ -1,234 +1,17 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const util = require('util');
4
- const { EventEmitter } = require('events');
5
- const cloneDeep = require('lodash/cloneDeep');
6
- const DataStore =
7
- process.env.NODE_ENV === 'test' ? require('@nedb/core') : require('@nedb/multi')(Number(process.env.NEDB_MULTI_PORT));
1
+ const DB = require('@abtnode/db');
8
2
  const logger = require('@abtnode/logger')('@abtnode/core:states');
9
3
 
10
4
  const { isCLI } = require('../util');
11
5
 
12
- class BaseState extends EventEmitter {
6
+ class BaseState extends DB {
13
7
  constructor(baseDir, options) {
14
- super();
8
+ super(baseDir, options);
15
9
 
16
10
  // HACK: do not emit any events from CLI
17
11
  if (isCLI() && process.env.NODE_ENV !== 'test') {
18
12
  this.emit = (name) => logger.debug('stopped state db event in CLI', name);
19
13
  }
20
-
21
- const dbOptions = options.db || {};
22
- this.filename = path.join(baseDir, options.filename);
23
- this.options = Object.freeze(cloneDeep(options));
24
- this.db = new DataStore({
25
- filename: this.filename,
26
- timestampData: true,
27
- ...dbOptions,
28
- });
29
-
30
- logger.info('initialized', { filename: this.filename });
31
-
32
- this.ready = false;
33
- this.readyCallbacks = [];
34
- this.db.loadDatabase((err) => {
35
- if (err) {
36
- logger.error(`failed to load disk database ${this.filename}`, { error: err });
37
- console.error(err);
38
- } else {
39
- this.ready = true;
40
- if (this.readyCallbacks.length) {
41
- this.readyCallbacks.forEach((x) => x());
42
- }
43
- }
44
- });
45
-
46
- this.asyncDB = new Proxy(this.db, {
47
- get(target, property) {
48
- if (typeof target[property] === 'function') {
49
- return util
50
- .promisify((...args) => {
51
- const cb = args[args.length - 1];
52
- const rest = args.slice(0, args.length - 1);
53
-
54
- target[property](...rest, (err, ...result) => {
55
- if (err) {
56
- return cb(err);
57
- }
58
-
59
- if (result.length === 1) {
60
- return cb(null, result[0]);
61
- }
62
-
63
- return cb(null, result);
64
- });
65
- })
66
- .bind(target);
67
- }
68
-
69
- return target[property];
70
- },
71
- });
72
- }
73
-
74
- onReady(cb) {
75
- if (this.ready) {
76
- cb();
77
- } else {
78
- this.readyCallbacks.push(cb);
79
- }
80
- }
81
-
82
- createNotification(payload) {
83
- if (this.notification && typeof this.notification.create === 'function') {
84
- this.notification.create(payload);
85
- }
86
- }
87
-
88
- paginate(conditions, sort, paging) {
89
- const { pageSize: size = 20, page = 1 } = paging || {};
90
- const pageSize = Math.min(100, size);
91
-
92
- return new Promise((resolve, reject) => {
93
- this.db
94
- .find(conditions)
95
- .sort(sort)
96
- .exec((err, docs) => {
97
- if (err) {
98
- return reject(err);
99
- }
100
-
101
- const pageCount = Math.ceil(docs.length / pageSize);
102
- const total = docs.length;
103
- const skip = (page - 1) * pageSize;
104
- const list = docs.slice(skip, skip + pageSize);
105
-
106
- list.forEach((doc) => {
107
- // eslint-disable-next-line no-underscore-dangle
108
- doc.id = doc._id;
109
- });
110
-
111
- return resolve({
112
- list,
113
- paging: {
114
- total,
115
- pageSize,
116
- pageCount,
117
- page,
118
- },
119
- });
120
- });
121
- });
122
- }
123
-
124
- async updateById(id, updates, options = {}) {
125
- const [, doc] = await this.asyncDB.update({ _id: id }, updates, {
126
- multi: false,
127
- upsert: false,
128
- returnUpdatedDocs: true,
129
- ...options,
130
- });
131
-
132
- return doc;
133
- }
134
-
135
- update(...args) {
136
- if (args.length === 0) {
137
- throw new Error('param is required by update method');
138
- }
139
-
140
- if (typeof args[0] === 'string') {
141
- return this.updateById(...args);
142
- }
143
-
144
- return this.asyncDB.update(args[0], args[1], { returnUpdatedDocs: true, ...(args[2] || {}) });
145
- }
146
-
147
- count(conditions = {}) {
148
- return new Promise((resolve, reject) => {
149
- this.db.count(conditions, (err, num) => {
150
- if (err) {
151
- return reject(err);
152
- }
153
-
154
- return resolve(num);
155
- });
156
- });
157
- }
158
-
159
- remove(conditions = {}, options = { multi: false }) {
160
- return new Promise((resolve, reject) => {
161
- this.db.remove(conditions, options, (err, num) => {
162
- if (err) {
163
- return reject(err);
164
- }
165
-
166
- return resolve(num);
167
- });
168
- });
169
- }
170
-
171
- reset() {
172
- fs.unlinkSync(this.filename);
173
- }
174
-
175
- find(...args) {
176
- if (args.length === 0) {
177
- return this.asyncDB.find({});
178
- }
179
-
180
- return this.asyncDB.find(...args);
181
- }
182
-
183
- findOne(...args) {
184
- if (args.length === 0) {
185
- return this.asyncDB.findOne({});
186
- }
187
-
188
- return this.asyncDB.findOne(...args);
189
- }
190
-
191
- insert(...args) {
192
- return this.asyncDB.insert(...args);
193
- }
194
-
195
- compactDatafile() {
196
- this.db.persistence.compactDatafile((err) => {
197
- if (err) {
198
- console.error(`failed to compact datafile: ${this.filename}`, err);
199
- }
200
- });
201
14
  }
202
15
  }
203
16
 
204
- /**
205
- * Rename _id field name to id, this method has side effects
206
- * @param {object} entities
207
- */
208
- const renameIdFiledName = (entities, from = '_id', to = 'id') => {
209
- /* eslint-disable no-underscore-dangle, no-param-reassign */
210
-
211
- if (!entities) {
212
- return entities;
213
- }
214
-
215
- const mapEntity = (entity) => {
216
- if (entity[from]) {
217
- entity[to] = entity[from];
218
- delete entity[from];
219
- }
220
- };
221
-
222
- if (!Array.isArray(entities)) {
223
- mapEntity(entities);
224
- return entities;
225
- }
226
-
227
- entities.forEach(mapEntity);
228
-
229
- return entities;
230
- };
231
-
232
- BaseState.renameIdFiledName = renameIdFiledName;
233
-
234
17
  module.exports = BaseState;
@@ -1,4 +1,4 @@
1
- const BaseState = require('./base');
1
+ const stateFactory = require('@abtnode/db/lib/factory');
2
2
  const NodeState = require('./node');
3
3
  const ChallengeState = require('./challenge');
4
4
  const BlockletState = require('./blocklet');
@@ -12,8 +12,6 @@ const SessionState = require('./session');
12
12
  const ExtrasState = require('./blocklet-extras');
13
13
  const CacheState = require('./cache');
14
14
 
15
- const states = {};
16
-
17
15
  const init = (dataDirs, options) => {
18
16
  const notificationState = new NotificationState(dataDirs.core, options);
19
17
  const nodeState = new NodeState(dataDirs.core, options, dataDirs, notificationState);
@@ -28,7 +26,7 @@ const init = (dataDirs, options) => {
28
26
  const extrasState = new ExtrasState(dataDirs.core, options);
29
27
  const cacheState = new CacheState(dataDirs.core, options);
30
28
 
31
- Object.assign(states, {
29
+ return {
32
30
  node: nodeState,
33
31
  blocklet: blockletState,
34
32
  notification: notificationState,
@@ -41,22 +39,7 @@ const init = (dataDirs, options) => {
41
39
  session: sessionState,
42
40
  blockletExtras: extrasState,
43
41
  cache: cacheState,
44
- });
42
+ };
45
43
  };
46
44
 
47
- module.exports = new Proxy(
48
- {},
49
- {
50
- get(target, prop) {
51
- if (prop === 'init') {
52
- return init;
53
- }
54
-
55
- if (states[prop] instanceof BaseState) {
56
- return states[prop];
57
- }
58
-
59
- throw new Error(`State ${String(prop)} may not be initialized`);
60
- },
61
- }
62
- );
45
+ module.exports = stateFactory(init);
@@ -1,4 +1,5 @@
1
1
  /* eslint-disable no-underscore-dangle */
2
+ const semver = require('semver');
2
3
  const omit = require('lodash/omit');
3
4
  const isEqual = require('lodash/isEqual');
4
5
  const isEmpty = require('lodash/isEmpty');
@@ -89,6 +90,8 @@ class NodeState extends BaseState {
89
90
  runtimeConfig,
90
91
  ownerNft,
91
92
  launcherInfo,
93
+ didRegistry,
94
+ didDomain,
92
95
  } = this.options;
93
96
 
94
97
  if (nodeOwner && !validateOwner(nodeOwner)) {
@@ -121,6 +124,8 @@ class NodeState extends BaseState {
121
124
  ownerNft,
122
125
  diskAlertThreshold: DISK_ALERT_THRESHOLD_PERCENT,
123
126
  launcherInfo: launcherInfo || undefined,
127
+ didRegistry,
128
+ didDomain,
124
129
  },
125
130
  async (e, data) => {
126
131
  if (e) {
@@ -133,6 +138,10 @@ class NodeState extends BaseState {
133
138
  }
134
139
 
135
140
  logger.info('create', { data });
141
+ if (dek) {
142
+ data.sk = security.decrypt(data.sk, data.did, dek);
143
+ }
144
+
136
145
  return resolve(data);
137
146
  }
138
147
  )
@@ -163,7 +172,7 @@ class NodeState extends BaseState {
163
172
 
164
173
  cleanupDirtyUpgradeState() {
165
174
  return this.read().then((doc) => {
166
- if (doc.nextVersion === doc.version) {
175
+ if (doc.nextVersion && semver.lte(doc.nextVersion, doc.version)) {
167
176
  const updates = { nextVersion: '', upgradeSessionId: '' };
168
177
 
169
178
  // FIXME: this may cause the node exit some mode unexpectedly if it is not being upgraded
@@ -5,6 +5,7 @@ const path = require('path');
5
5
  const os = require('os');
6
6
  const tar = require('tar');
7
7
  const get = require('lodash/get');
8
+ const intersection = require('lodash/intersection');
8
9
  const streamToPromise = require('stream-to-promise');
9
10
  const { Throttle } = require('stream-throttle');
10
11
  const ssri = require('ssri');
@@ -69,7 +70,7 @@ const statusMap = {
69
70
  stopped: BlockletStatus.stopped,
70
71
  };
71
72
 
72
- const nodePrivateEnvs = [
73
+ const PRIVATE_NODE_ENVS = [
73
74
  // 'NEDB_MULTI_PORT', // FIXME: 排查 abtnode 对外提供的 SDK(比如 @abtnode/queue), SDK 中不要自动使用 NEDB_MULTI_PORT 环境变量
74
75
  'ABT_NODE_UPDATER_PORT',
75
76
  'ABT_NODE_SESSION_TTL',
@@ -186,7 +187,7 @@ const getBlockletDirs = (blocklet, { rootBlocklet, dataDirs, ensure = false } =
186
187
  * @param {*} blocklet
187
188
  * @param {*} configs
188
189
  */
189
- const fillBlockletConfigs = (blocklet, configs) => {
190
+ const fillBlockletConfigs = (blocklet, configs, parent) => {
190
191
  blocklet.configs = configs || [];
191
192
  blocklet.configObj = blocklet.configs.reduce((acc, x) => {
192
193
  acc[x.key] = x.value;
@@ -196,6 +197,13 @@ const fillBlockletConfigs = (blocklet, configs) => {
196
197
  acc[x.key] = x.value;
197
198
  return acc;
198
199
  }, {});
200
+
201
+ const sharedConfigs = getSharedConfigs(blocklet, parent);
202
+ Object.keys(sharedConfigs).forEach((key) => {
203
+ blocklet.configObj[key] = sharedConfigs[key];
204
+ const item = blocklet.configs.find((x) => x.key === key);
205
+ item.value = sharedConfigs[key];
206
+ });
199
207
  };
200
208
 
201
209
  const ensureBlockletExpanded = async (meta, appDir) => {
@@ -297,10 +305,10 @@ const getSystemEnvironments = (blocklet) => {
297
305
  };
298
306
  };
299
307
 
300
- const getRuntimeEnvironments = (blocklet, nodeEnvironments) => {
308
+ const getRuntimeEnvironments = (blocklet, nodeEnvironments, parent) => {
301
309
  // pm2 will force inject env variables of daemon process to blocklet process
302
310
  // we can only rewrite these private env variables to empty
303
- const safeNodeEnvironments = nodePrivateEnvs.reduce((o, x) => {
311
+ const safeNodeEnvironments = PRIVATE_NODE_ENVS.reduce((o, x) => {
304
312
  o[x] = '';
305
313
  return o;
306
314
  }, {});
@@ -308,11 +316,28 @@ const getRuntimeEnvironments = (blocklet, nodeEnvironments) => {
308
316
  return {
309
317
  ...blocklet.environmentObj,
310
318
  ...blocklet.configObj,
319
+ ...getSharedConfigs(blocklet, parent),
311
320
  ...nodeEnvironments,
312
321
  ...safeNodeEnvironments,
313
322
  };
314
323
  };
315
324
 
325
+ // we support share insecure same configs between parent and child blocklets
326
+ const getSharedConfigs = (child, parent) => {
327
+ const sharedConfigs = {};
328
+ if (parent && Array.isArray(parent.configs) && child.meta.did !== parent.meta.did) {
329
+ const parentKeys = parent.configs.filter((x) => x.secure === false).map((x) => x.key);
330
+ const childKeys = child.configs.map((x) => x.key);
331
+ const sharedKeys = intersection(parentKeys, childKeys);
332
+ sharedKeys.forEach((key) => {
333
+ if (!child.configObj[key]) {
334
+ sharedConfigs[key] = parent.configObj[key];
335
+ }
336
+ });
337
+ }
338
+ return sharedConfigs;
339
+ };
340
+
316
341
  const isUsefulError = (err) => err && err.message !== 'process or namespace not found';
317
342
 
318
343
  const getHealthyCheckTimeout = (blocklet, { checkHealthImmediately } = {}) => {
@@ -16,4 +16,8 @@ const getIpDnsDomainForBlocklet = (blocklet, blockletInterface) => {
16
16
  }${iName}-${SLOT_FOR_IP_DNS_SITE}.${DEFAULT_IP_DNS_DOMAIN_SUFFIX}`;
17
17
  };
18
18
 
19
- module.exports = getIpDnsDomainForBlocklet;
19
+ const getDidDomainForBlocklet = ({ appId, didDomain }) => {
20
+ return `${appId.toLowerCase()}.${didDomain}`;
21
+ };
22
+
23
+ module.exports = { getIpDnsDomainForBlocklet, getDidDomainForBlocklet };
package/lib/util/index.js CHANGED
@@ -315,7 +315,6 @@ const getHttpsCertInfo = (certificate) => {
315
315
  validFrom,
316
316
  validTo,
317
317
  issuer,
318
- serialNumber: info.serialNumber,
319
318
  sans: info.dnsNames,
320
319
  validityPeriod: info.validTo - info.validFrom,
321
320
  fingerprintAlg: 'SHA256',
@@ -332,6 +331,8 @@ const getDataDirs = (dataDir) => ({
332
331
  tmp: path.join(dataDir, 'tmp'),
333
332
  blocklets: path.join(dataDir, 'blocklets'),
334
333
  services: path.join(dataDir, 'services'),
334
+ modules: path.join(dataDir, 'modules'),
335
+ certManagerModule: path.join(dataDir, 'modules', 'certificate-manager'),
335
336
  });
336
337
 
337
338
  // Ensure data dir for Blocklet Server exists
@@ -356,39 +357,53 @@ const formatEnvironments = (environments) => Object.keys(environments).map((x) =
356
357
  const transformIPToDomain = (ip) => ip.split('.').join('-');
357
358
 
358
359
  const getBaseUrls = async (node, ips) => {
359
- const ipTypes = {
360
- internal: 'private',
361
- external: 'public',
362
- };
363
360
  const info = await node.getNodeInfo();
364
361
  const { https, httpPort, httpsPort } = info.routing;
365
- const ipKeys = Object.keys(ips).filter((x) => ips[x]);
366
362
  const getPort = (port, defaultPort) => (port && port !== defaultPort ? `:${port}` : '');
363
+ const availableIps = ips.filter(Boolean);
364
+
365
+ const getHttpInfo = async (domain) => {
366
+ const certificate = https ? await node.certManager.getNormalByDomain(domain) : null;
367
+ const protocol = certificate ? 'https' : 'http';
368
+ const port = certificate ? getPort(httpsPort, DEFAULT_HTTPS_PORT) : getPort(httpPort, DEFAULT_HTTP_PORT);
369
+
370
+ return { protocol, port };
371
+ };
367
372
 
368
373
  if (info.routing.provider !== ROUTER_PROVIDER_NONE && info.routing.snapshotHash && info.routing.adminPath) {
369
374
  const sites = await node.getSitesFromSnapshot();
370
375
 
371
376
  const { dashboardDomain } = info.routing;
372
377
  const adminPath = normalizePathPrefix(info.routing.adminPath);
373
- const httpUrls = ipKeys.map((x) => {
374
- const port = getPort(httpPort, DEFAULT_HTTP_PORT);
378
+ const tmpHttpPort = getPort(httpPort, DEFAULT_HTTP_PORT);
379
+
380
+ const httpUrls = availableIps.map((ip) => {
375
381
  return {
376
- url: `http://${ips[x]}${port}${adminPath}`,
377
- type: ipTypes[x],
382
+ url: `http://${ip}${tmpHttpPort}${adminPath}`,
378
383
  };
379
384
  });
380
385
 
381
386
  if (dashboardDomain) {
382
387
  const site = sites.find((c) => (c.domainAliases || []).find((item) => item.value === dashboardDomain));
383
388
  if (site) {
384
- const certificate = https ? await node.findCertificateByDomain(dashboardDomain) : null;
385
- const protocol = certificate ? 'https' : 'http';
386
- const port = certificate ? getPort(httpsPort, DEFAULT_HTTPS_PORT) : getPort(httpPort, DEFAULT_HTTP_PORT);
387
- const httpsUrls = ipKeys.map((x) => ({
388
- url: `${protocol}://${transformIPToDomain(ips[x])}.${dashboardDomain.substring(2)}${port}${adminPath}`,
389
- type: ipTypes[x],
389
+ const httpInfo = await getHttpInfo(dashboardDomain);
390
+ const httpsUrls = availableIps.map((ip) => ({
391
+ url: `${httpInfo.protocol}://${transformIPToDomain(ip)}.${dashboardDomain.substring(2)}${
392
+ httpInfo.port
393
+ }${adminPath}`,
390
394
  }));
391
395
 
396
+ // add did domain access url
397
+ const didDomainAlias = site.domainAliases.find((item) => item.value.endsWith(info.didDomain));
398
+ if (didDomainAlias) {
399
+ const didDomain = didDomainAlias.value;
400
+ const didDomainHttpInfo = await getHttpInfo(didDomain);
401
+
402
+ httpsUrls.push({
403
+ url: `${didDomainHttpInfo.protocol}://${didDomain}${didDomainHttpInfo.port}${adminPath}`,
404
+ });
405
+ }
406
+
392
407
  return httpUrls.concat(httpsUrls);
393
408
  }
394
409
  }
@@ -397,9 +412,8 @@ const getBaseUrls = async (node, ips) => {
397
412
  }
398
413
 
399
414
  // port urls
400
- return ipKeys.map((x) => ({
401
- url: `http://${ips[x]}:${info.port}`,
402
- type: ipTypes[x],
415
+ return availableIps.map((ip) => ({
416
+ url: `http://${ip}:${info.port}`,
403
417
  }));
404
418
  };
405
419
 
@@ -66,7 +66,7 @@ module.exports = ({ events, dataDirs, instance }) => {
66
66
  const webhookList = await webhookState.list();
67
67
  const nodeInfo = await nodeState.read();
68
68
  const { internal, external } = await IP.get();
69
- const baseUrls = await getBaseUrls(instance, { internal, external });
69
+ const baseUrls = await getBaseUrls(instance, [internal, external]);
70
70
  const senderFns = {};
71
71
 
72
72
  if (webhookList.length) {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.6.5",
6
+ "version": "1.6.9",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,26 +19,28 @@
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
- "@abtnode/constant": "1.6.5",
23
- "@abtnode/cron": "1.6.5",
24
- "@abtnode/logger": "1.6.5",
25
- "@abtnode/queue": "1.6.5",
26
- "@abtnode/rbac": "1.6.5",
27
- "@abtnode/router-provider": "1.6.5",
28
- "@abtnode/static-server": "1.6.5",
29
- "@abtnode/timemachine": "1.6.5",
30
- "@abtnode/util": "1.6.5",
31
- "@arcblock/did": "^1.13.77",
32
- "@arcblock/event-hub": "1.13.77",
22
+ "@abtnode/certificate-manager": "1.6.9",
23
+ "@abtnode/constant": "1.6.9",
24
+ "@abtnode/cron": "1.6.9",
25
+ "@abtnode/db": "1.6.9",
26
+ "@abtnode/logger": "1.6.9",
27
+ "@abtnode/queue": "1.6.9",
28
+ "@abtnode/rbac": "1.6.9",
29
+ "@abtnode/router-provider": "1.6.9",
30
+ "@abtnode/static-server": "1.6.9",
31
+ "@abtnode/timemachine": "1.6.9",
32
+ "@abtnode/util": "1.6.9",
33
+ "@arcblock/did": "^1.13.79",
34
+ "@arcblock/event-hub": "1.13.79",
33
35
  "@arcblock/pm2-events": "^0.0.5",
34
- "@arcblock/vc": "^1.13.77",
35
- "@blocklet/meta": "1.6.5",
36
+ "@arcblock/vc": "^1.13.79",
37
+ "@blocklet/meta": "1.6.9",
36
38
  "@fidm/x509": "^1.2.1",
37
39
  "@nedb/core": "^1.2.2",
38
40
  "@nedb/multi": "^1.2.2",
39
- "@ocap/mcrypto": "^1.13.77",
40
- "@ocap/util": "^1.13.77",
41
- "@ocap/wallet": "^1.13.77",
41
+ "@ocap/mcrypto": "^1.13.79",
42
+ "@ocap/util": "^1.13.79",
43
+ "@ocap/wallet": "^1.13.79",
42
44
  "@slack/webhook": "^5.0.3",
43
45
  "axios": "^0.21.4",
44
46
  "axon": "^2.0.3",
@@ -51,7 +53,7 @@
51
53
  "is-base64": "^1.1.0",
52
54
  "is-ip": "^3.1.0",
53
55
  "is-url": "^1.2.4",
54
- "joi": "^17.4.0",
56
+ "joi": "^17.5.0",
55
57
  "js-yaml": "^3.14.0",
56
58
  "lodash": "^4.17.21",
57
59
  "lru-cache": "^6.0.0",
@@ -71,7 +73,7 @@
71
73
  "compression": "^1.7.4",
72
74
  "expand-tilde": "^2.0.2",
73
75
  "express": "^4.17.1",
74
- "jest": "^27.3.1"
76
+ "jest": "^27.4.5"
75
77
  },
76
- "gitHead": "59da906682f8122797a932640aebbad5a30dfcc3"
78
+ "gitHead": "372e95513bd9a510702d93650c0c004589579cb3"
77
79
  }