@abtnode/core 1.15.17 → 1.16.0-beta-b16cb035

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.
Files changed (119) hide show
  1. package/lib/api/node.js +67 -69
  2. package/lib/api/team.js +386 -55
  3. package/lib/blocklet/downloader/blocklet-downloader.js +226 -0
  4. package/lib/blocklet/downloader/bundle-downloader.js +272 -0
  5. package/lib/blocklet/downloader/constants.js +3 -0
  6. package/lib/blocklet/downloader/resolve-download.js +199 -0
  7. package/lib/blocklet/extras.js +83 -26
  8. package/lib/blocklet/hooks.js +18 -65
  9. package/lib/blocklet/manager/base.js +10 -16
  10. package/lib/blocklet/manager/disk.js +1679 -1566
  11. package/lib/blocklet/manager/helper/install-application-from-backup.js +177 -0
  12. package/lib/blocklet/manager/helper/install-application-from-dev.js +94 -0
  13. package/lib/blocklet/manager/helper/install-application-from-general.js +188 -0
  14. package/lib/blocklet/manager/helper/install-component-from-dev.js +84 -0
  15. package/lib/blocklet/manager/helper/install-component-from-upload.js +181 -0
  16. package/lib/blocklet/manager/helper/install-component-from-url.js +173 -0
  17. package/lib/blocklet/manager/helper/migrate-application-to-struct-v2.js +450 -0
  18. package/lib/blocklet/manager/helper/rollback-cache.js +41 -0
  19. package/lib/blocklet/manager/helper/upgrade-components.js +152 -0
  20. package/lib/blocklet/migration.js +30 -52
  21. package/lib/blocklet/storage/backup/audit-log.js +27 -0
  22. package/lib/blocklet/storage/backup/base.js +62 -0
  23. package/lib/blocklet/storage/backup/blocklet-extras.js +92 -0
  24. package/lib/blocklet/storage/backup/blocklet.js +70 -0
  25. package/lib/blocklet/storage/backup/blocklets.js +74 -0
  26. package/lib/blocklet/storage/backup/data.js +19 -0
  27. package/lib/blocklet/storage/backup/logs.js +24 -0
  28. package/lib/blocklet/storage/backup/routing-rule.js +19 -0
  29. package/lib/blocklet/storage/backup/spaces.js +240 -0
  30. package/lib/blocklet/storage/restore/base.js +67 -0
  31. package/lib/blocklet/storage/restore/blocklet-extras.js +86 -0
  32. package/lib/blocklet/storage/restore/blocklet.js +56 -0
  33. package/lib/blocklet/storage/restore/blocklets.js +43 -0
  34. package/lib/blocklet/storage/restore/logs.js +21 -0
  35. package/lib/blocklet/storage/restore/spaces.js +156 -0
  36. package/lib/blocklet/storage/utils/hash.js +51 -0
  37. package/lib/blocklet/storage/utils/zip.js +43 -0
  38. package/lib/cert.js +206 -0
  39. package/lib/event.js +237 -64
  40. package/lib/index.js +191 -83
  41. package/lib/migrations/1.0.21-update-config.js +1 -1
  42. package/lib/migrations/1.0.22-max-memory.js +1 -1
  43. package/lib/migrations/1.0.25.js +1 -1
  44. package/lib/migrations/1.0.32-update-config.js +1 -1
  45. package/lib/migrations/1.0.33-blocklets.js +1 -1
  46. package/lib/migrations/1.5.20-registry.js +15 -0
  47. package/lib/migrations/1.6.17-blocklet-children.js +48 -0
  48. package/lib/migrations/1.6.21-rename-ip-echo-domain.js +35 -0
  49. package/lib/migrations/1.6.4-security.js +59 -0
  50. package/lib/migrations/1.6.5-security.js +60 -0
  51. package/lib/migrations/1.6.9-update-node-info-and-certificate.js +38 -0
  52. package/lib/migrations/1.7.1-blocklet-setup.js +18 -0
  53. package/lib/migrations/1.7.12-blocklet-meta.js +51 -0
  54. package/lib/migrations/1.7.15-blocklet-bundle-source.js +42 -0
  55. package/lib/migrations/1.7.20-blocklet-component.js +41 -0
  56. package/lib/migrations/1.8.33-blocklet-mem-limit.js +20 -0
  57. package/lib/migrations/README.md +1 -1
  58. package/lib/migrations/index.js +6 -2
  59. package/lib/monitor/blocklet-runtime-monitor.js +200 -0
  60. package/lib/monitor/get-history-list.js +37 -0
  61. package/lib/monitor/node-runtime-monitor.js +228 -0
  62. package/lib/router/helper.js +572 -497
  63. package/lib/router/index.js +85 -21
  64. package/lib/router/manager.js +146 -187
  65. package/lib/states/README.md +36 -1
  66. package/lib/states/access-key.js +39 -17
  67. package/lib/states/audit-log.js +462 -0
  68. package/lib/states/base.js +4 -213
  69. package/lib/states/blocklet-extras.js +194 -138
  70. package/lib/states/blocklet.js +361 -104
  71. package/lib/states/cache.js +8 -6
  72. package/lib/states/challenge.js +5 -5
  73. package/lib/states/index.js +19 -36
  74. package/lib/states/migration.js +4 -4
  75. package/lib/states/node.js +135 -46
  76. package/lib/states/notification.js +22 -35
  77. package/lib/states/session.js +17 -9
  78. package/lib/states/site.js +50 -25
  79. package/lib/states/user.js +74 -20
  80. package/lib/states/webhook.js +10 -6
  81. package/lib/team/manager.js +124 -7
  82. package/lib/util/blocklet.js +1223 -246
  83. package/lib/util/chain.js +1 -1
  84. package/lib/util/default-node-config.js +5 -23
  85. package/lib/util/disk-monitor.js +13 -10
  86. package/lib/util/domain-status.js +84 -15
  87. package/lib/util/get-accessible-external-node-ip.js +2 -2
  88. package/lib/util/get-domain-for-blocklet.js +13 -0
  89. package/lib/util/get-meta-from-url.js +33 -0
  90. package/lib/util/index.js +207 -272
  91. package/lib/util/ip.js +6 -0
  92. package/lib/util/maintain.js +233 -0
  93. package/lib/util/public-to-store.js +85 -0
  94. package/lib/util/ready.js +1 -1
  95. package/lib/util/requirement.js +28 -9
  96. package/lib/util/reset-node.js +22 -7
  97. package/lib/util/router.js +13 -0
  98. package/lib/util/rpc.js +16 -0
  99. package/lib/util/store.js +179 -0
  100. package/lib/util/sysinfo.js +44 -0
  101. package/lib/util/ua.js +54 -0
  102. package/lib/validators/blocklet-extra.js +24 -0
  103. package/lib/validators/node.js +25 -12
  104. package/lib/validators/permission.js +16 -1
  105. package/lib/validators/role.js +17 -3
  106. package/lib/validators/router.js +40 -20
  107. package/lib/validators/trusted-passport.js +1 -0
  108. package/lib/validators/util.js +22 -5
  109. package/lib/webhook/index.js +45 -35
  110. package/lib/webhook/sender/index.js +5 -0
  111. package/lib/webhook/sender/slack/index.js +1 -1
  112. package/lib/webhook/sender/wallet/index.js +48 -0
  113. package/package.json +54 -36
  114. package/lib/blocklet/registry.js +0 -205
  115. package/lib/states/https-cert.js +0 -67
  116. package/lib/util/get-ip-dns-domain-for-blocklet.js +0 -19
  117. package/lib/util/service.js +0 -66
  118. package/lib/util/upgrade.js +0 -178
  119. /package/lib/{queue.js → util/queue.js} +0 -0
@@ -1,83 +1,227 @@
1
+ /* eslint-disable no-async-promise-executor */
1
2
  /* eslint-disable no-await-in-loop */
2
3
  /* eslint-disable function-paren-newline */
3
4
  /* eslint-disable no-underscore-dangle */
4
5
  const omit = require('lodash/omit');
5
6
  const uniq = require('lodash/uniq');
7
+ const cloneDeep = require('lodash/cloneDeep');
6
8
  const detectPort = require('detect-port');
7
9
  const Lock = require('@abtnode/util/lib/lock');
10
+ const security = require('@abtnode/util/lib/security');
8
11
  const { fixPerson, fixInterfaces } = require('@blocklet/meta/lib/fix');
12
+ const { getDisplayName, forEachBlocklet, forEachBlockletSync, forEachChildSync } = require('@blocklet/meta/lib/util');
9
13
  const {
10
14
  BlockletStatus,
11
15
  BlockletSource,
12
16
  BLOCKLET_MODES,
13
17
  BLOCKLET_DEFAULT_PORT_NAME,
14
18
  BLOCKLET_INTERFACE_TYPE_SERVICE,
15
- } = require('@blocklet/meta/lib/constants');
19
+ } = require('@blocklet/constant');
20
+ const { APP_STRUCT_VERSION } = require('@abtnode/constant');
16
21
 
17
22
  const logger = require('@abtnode/logger')('state-blocklet');
18
23
 
19
24
  const BaseState = require('./base');
20
- const { forEachBlocklet } = require('../util/blocklet');
21
- const { validateBlockletMeta } = require('../util');
25
+ const { checkDuplicateComponents, ensureMeta } = require('../util/blocklet');
26
+ const { validateBlockletMeta } = require('../util/blocklet');
22
27
 
23
28
  const lock = new Lock('blocklet-port-assign-lock');
24
29
 
30
+ const isHex = (str) => /^0x[0-9a-f]+$/i.test(str);
25
31
  const getMaxPort = (ports = {}) => Math.max(Object.values(ports).map(Number));
26
32
 
33
+ // structV1Did is just for migration purpose and should be removed in the future
34
+ const getConditions = (did) => [{ 'meta.did': did }, { appDid: did }, { appPid: did }, { structV1Did: did }];
35
+
27
36
  const getExternalPortsFromMeta = (meta) =>
28
37
  (meta.interfaces || []).map((x) => x.port && x.port.external).filter(Boolean);
29
38
 
39
+ const formatBlocklet = (blocklet, phase, dek) => {
40
+ forEachBlockletSync(blocklet, (b) => {
41
+ if (b.meta) {
42
+ fixPerson(b.meta);
43
+ fixInterfaces(b.meta);
44
+ }
45
+
46
+ if (phase === 'onRead') {
47
+ b.children = b.children || [];
48
+ b.environments = b.environments || [];
49
+ }
50
+
51
+ if (!b.environments || !b.meta || !dek) {
52
+ return;
53
+ }
54
+
55
+ (Array.isArray(b.migratedFrom) ? b.migratedFrom : []).forEach((x) => {
56
+ if (phase === 'onUpdate' && isHex(x.appSk) === true) {
57
+ x.appSk = security.encrypt(x.appSk, b.meta.did, dek);
58
+ }
59
+ if (phase === 'onRead' && isHex(x.appSk) === false) {
60
+ x.appSk = security.decrypt(x.appSk, b.meta.did, dek);
61
+ }
62
+ });
63
+
64
+ ['BLOCKLET_APP_SK', 'BLOCKLET_APP_PSK'].forEach((key) => {
65
+ const env = b.environments.find((x) => x.key === key);
66
+ if (!env) {
67
+ return;
68
+ }
69
+ // salt in blocklet state is different from the salt in blocklet-extra state
70
+ // in blocklet-extra state, salt is app meta did in each component
71
+ // in blocklet state, salt is component meta did in each component
72
+ if (phase === 'onUpdate' && isHex(env.value) === true) {
73
+ env.value = security.encrypt(env.value, b.meta.did, dek);
74
+ }
75
+ if (phase === 'onRead' && isHex(env.value) === false) {
76
+ env.value = security.decrypt(env.value, b.meta.did, dek);
77
+ }
78
+ });
79
+ });
80
+
81
+ return blocklet;
82
+ };
83
+
84
+ const fixChildren = (children) => {
85
+ if (!children) {
86
+ return;
87
+ }
88
+
89
+ children.forEach((child) => {
90
+ child.mode = child.mode || BLOCKLET_MODES.PRODUCTION;
91
+ });
92
+ };
93
+
94
+ // type Application = {
95
+ // appDid: DID,
96
+ // appPid: DID,
97
+ // meta: Meta,
98
+ // source: BlockletSource,
99
+ // deployedFrom: string,
100
+ // mode: BLOCKLET_MODES;
101
+ // status: BlockletStatus,
102
+ // children: Component,
103
+ // ports: Ports;
104
+ // mountPoint: string; // deprecated
105
+ // migratedFrom: Array<{ appSk: string, appDid: DID, at: Date }>,
106
+ // externalSk: boolean,
107
+ // structV1Did: DID, // just for migration purpose and should be removed in the future
108
+ // };
109
+
110
+ // type Component = {
111
+ // mountPoint: string;
112
+ // meta: Meta,
113
+ // bundleSource: ComponentSource,
114
+ // source: BlockletSource,
115
+ // deployedFrom: string,
116
+ // mode: BLOCKLET_MODES;
117
+ // status: BlockletStatus,
118
+ // children: Component,
119
+ // ports: Ports;
120
+ // dependents: Array<{
121
+ // did: DID, // blocklet did
122
+ // required: boolean,
123
+ // version: SemverRange,
124
+ // }>;
125
+ // dependencies: Array<{
126
+ // id: string, // format: <did1/did2/did3>
127
+ // required: boolean,
128
+ // }>;
129
+ // dynamic: boolean; // deprecated
130
+ // };
131
+
132
+ // type Ports = {
133
+ // [name: string]: Number; // name is in meta.interfaces[].port
134
+ // };
135
+
30
136
  class BlockletState extends BaseState {
31
137
  /**
32
138
  * Creates an instance of BlockletState
33
139
  * @param {string} baseDir
34
- * @param {object} options
35
- * @param {string} options.blockletPort - from which port to start new blocklets
140
+ * @param {object} config
141
+ * @param {string} config.blockletPort - from which port to start new blocklets
36
142
  * @memberof BlockletState
37
143
  */
38
- constructor(baseDir, options = {}) {
39
- super(baseDir, { filename: 'blocklet.db', ...options });
144
+ constructor(baseDir, config = {}) {
145
+ super(baseDir, { filename: 'blocklet.db', ...config });
40
146
 
41
- this.defaultPort = options.blockletPort || 5555;
147
+ this.defaultPort = config.blockletPort || 5555;
42
148
  }
43
149
 
44
- getBlocklet(did) {
150
+ getBlocklet(did, { decryptSk = true } = {}) {
45
151
  return new Promise((resolve, reject) => {
46
- this.db.findOne({ 'meta.did': did }, (err, doc) => {
152
+ if (!did) {
153
+ resolve(null);
154
+ }
155
+
156
+ this.findOne({ $or: getConditions(did) }, (err, doc) => {
47
157
  if (err) {
48
158
  return reject(err);
49
159
  }
50
160
 
51
- if (doc) {
52
- // TODO: this only exists for backward compatible
53
- fixPerson(doc.meta);
54
- fixInterfaces(doc.meta);
55
- doc.children = doc.children || [];
161
+ return resolve(doc ? formatBlocklet(doc, 'onRead', decryptSk ? this.config.dek : null) : null);
162
+ });
163
+ });
164
+ }
165
+
166
+ getBlockletMetaDid(did) {
167
+ return new Promise((resolve, reject) => {
168
+ if (!did) {
169
+ resolve(null);
170
+ }
171
+
172
+ this.findOne({ $or: getConditions(did) }, (err, doc) => {
173
+ if (err) {
174
+ return reject(err);
56
175
  }
57
176
 
58
- return resolve(doc);
177
+ return resolve(doc ? doc.meta.did : null);
178
+ });
179
+ });
180
+ }
181
+
182
+ async getBlockletStatus(did) {
183
+ return new Promise((resolve, reject) => {
184
+ if (!did) {
185
+ resolve(null);
186
+ }
187
+
188
+ this.findOne({ $or: getConditions(did) }, { status: 1 }, (err, doc) => {
189
+ if (err) {
190
+ return reject(err);
191
+ }
192
+
193
+ return resolve(doc ? doc.status : null);
194
+ });
195
+ });
196
+ }
197
+
198
+ hasBlocklet(did) {
199
+ return new Promise((resolve, reject) => {
200
+ if (!did) {
201
+ resolve(false);
202
+ }
203
+
204
+ this.count({ $or: getConditions(did) }, (err, count) => {
205
+ if (err) {
206
+ return reject(err);
207
+ }
208
+
209
+ return resolve(!!count);
59
210
  });
60
211
  });
61
212
  }
62
213
 
63
214
  getBlocklets(query = {}, projection) {
64
215
  return new Promise((resolve, reject) => {
65
- this.db
66
- .find(query, projection)
67
- .sort('-createdAt')
68
- .exec((err, docs) => {
216
+ this.cursor(query)
217
+ .projection(projection)
218
+ .sort({ createdAt: -1 })
219
+ .exec((err, docs = []) => {
69
220
  if (err) {
70
221
  return reject(err);
71
222
  }
72
223
 
73
- return resolve(
74
- docs.filter(Boolean).map((doc) => {
75
- // TODO: this only exists for backward compatible
76
- fixPerson(doc.meta);
77
- fixInterfaces(doc.meta);
78
- return doc;
79
- })
80
- );
224
+ return resolve(docs.filter(Boolean).map((doc) => formatBlocklet(doc, 'onRead', this.config.dek)));
81
225
  });
82
226
  });
83
227
  }
@@ -91,32 +235,35 @@ class BlockletState extends BaseState {
91
235
  return reject(new Error(`Try to remove non-existing blocklet ${did}`));
92
236
  }
93
237
 
94
- this.db.remove({ _id: doc._id }, (err) => {
238
+ this.remove({ _id: doc._id }, (err) => {
95
239
  if (err) {
96
240
  return reject(err);
97
241
  }
98
242
 
99
243
  this.emit('remove', doc);
100
- return resolve(doc);
244
+ return resolve(formatBlocklet(doc, 'onRead', this.config.dek));
101
245
  });
102
246
  })
103
247
  );
104
248
  }
105
249
 
106
250
  addBlocklet({
107
- did,
108
251
  meta,
109
252
  source = BlockletSource.registry,
110
253
  status = BlockletStatus.added,
111
- deployedFrom,
254
+ deployedFrom = '',
112
255
  mode = BLOCKLET_MODES.PRODUCTION,
113
- childrenMeta = [],
256
+ children: rawChildren = [],
257
+ appPid = null, // the permanent appDid, which will not change after initial set
258
+ migratedFrom = [], // the complete migrate history
259
+ // whether sk is managed by some party beside server, such as did-wallet
260
+ // externalSk is always true in struct V2 blocklet
261
+ externalSk = true,
114
262
  } = {}) {
115
- return this.getBlocklet(did).then(
116
- (doc) =>
117
- // eslint-disable-next-line no-async-promise-executor
263
+ return this.getBlocklet(meta.did).then(
264
+ (exist) =>
118
265
  new Promise(async (resolve, reject) => {
119
- if (doc) {
266
+ if (exist) {
120
267
  reject(new Error('Blocklet already added'));
121
268
  return;
122
269
  }
@@ -124,39 +271,48 @@ class BlockletState extends BaseState {
124
271
  try {
125
272
  fixPerson(meta);
126
273
  fixInterfaces(meta);
127
- const sanitized = validateBlockletMeta(meta);
274
+ let sanitized = validateBlockletMeta(meta);
275
+ // bundle info
276
+ sanitized = ensureMeta(sanitized);
277
+ sanitized = omit(sanitized, ['htmlAst']);
128
278
 
129
279
  // get ports
130
280
  await lock.acquire();
131
281
 
132
282
  const ports = await this.getBlockletPorts({ interfaces: sanitized.interfaces || [] });
133
283
 
134
- const children = await this.fillChildrenPorts(this.getChildrenFromMetas(childrenMeta), {
284
+ const children = await this.fillChildrenPorts(rawChildren, {
135
285
  defaultPort: getMaxPort(ports),
136
286
  });
137
287
 
288
+ fixChildren(children);
289
+
290
+ const data = {
291
+ appDid: null, // will updated later when updating blocklet environments
292
+ appPid,
293
+ mode,
294
+ meta: sanitized,
295
+ status,
296
+ source,
297
+ deployedFrom,
298
+ ports,
299
+ environments: [],
300
+ children,
301
+ migratedFrom,
302
+ externalSk,
303
+ structVersion: APP_STRUCT_VERSION,
304
+ };
305
+
138
306
  // add to db
139
- this.db.insert(
140
- {
141
- mode,
142
- meta: omit(sanitized, ['htmlAst']),
143
- status,
144
- source,
145
- deployedFrom,
146
- ports,
147
- environments: [],
148
- children,
149
- },
150
- (err, newDoc) => {
151
- lock.release();
152
- if (err) {
153
- return reject(err);
154
- }
155
-
156
- this.emit('add', newDoc);
157
- return resolve(newDoc);
307
+ this.insert(data, (err, doc) => {
308
+ lock.release();
309
+ if (err) {
310
+ return reject(err);
158
311
  }
159
- );
312
+
313
+ this.emit('add', doc);
314
+ return resolve(doc);
315
+ });
160
316
  } catch (err) {
161
317
  lock.release();
162
318
  reject(err);
@@ -165,7 +321,28 @@ class BlockletState extends BaseState {
165
321
  );
166
322
  }
167
323
 
168
- upgradeBlocklet({ meta, source, deployedFrom, children } = {}) {
324
+ // FIXME: 这个接口比较危险,可能会修改一些本不应该修改的字段,后续需要考虑改进
325
+ updateBlocklet(did, updates) {
326
+ return this.getBlocklet(did).then(
327
+ (doc) =>
328
+ new Promise(async (resolve, reject) => {
329
+ if (!doc) {
330
+ reject(new Error('Blocklet does not exist'));
331
+ return;
332
+ }
333
+
334
+ try {
335
+ const formatted = formatBlocklet(cloneDeep(updates), 'onUpdate', this.config.dek);
336
+ const newDoc = await this.updateById(doc._id, { $set: formatted });
337
+ resolve(newDoc);
338
+ } catch (err) {
339
+ reject(err);
340
+ }
341
+ })
342
+ );
343
+ }
344
+
345
+ upgradeBlocklet({ meta, source, deployedFrom = '', children } = {}) {
169
346
  return this.getBlocklet(meta.did).then(
170
347
  (doc) =>
171
348
  // eslint-disable-next-line no-async-promise-executor
@@ -195,17 +372,18 @@ class BlockletState extends BaseState {
195
372
  logger.info('Fill children ports when when upgrading blocklet', { name: doc.meta.name, did: doc.meta.did });
196
373
  await this.fillChildrenPorts(children, { oldChildren: doc.children, defaultPort: getMaxPort(ports) });
197
374
 
375
+ fixChildren(children);
376
+
198
377
  // add to db
199
- const newDoc = await this.updateById(doc._id, {
200
- $set: {
201
- meta: omit(sanitized, ['htmlAst']),
202
- source,
203
- deployedFrom,
204
- children,
205
- ports,
206
- },
378
+ const newDoc = await this.updateBlocklet(meta.did, {
379
+ meta: omit(sanitized, ['htmlAst']),
380
+ source,
381
+ deployedFrom,
382
+ children,
383
+ ports,
207
384
  });
208
385
  lock.release();
386
+
209
387
  this.emit('upgrade', newDoc);
210
388
  resolve(newDoc);
211
389
  } catch (err) {
@@ -224,7 +402,7 @@ class BlockletState extends BaseState {
224
402
  defaultPort = 0,
225
403
  } = {}) {
226
404
  try {
227
- const blocklets = await this.getBlocklets({}, { port: 1, ports: 1, 'meta.interfaces': 1 });
405
+ const blocklets = await this.getBlocklets({}, { port: 1, ports: 1, 'meta.interfaces': 1, children: 1 });
228
406
 
229
407
  const occupiedExternalPorts = new Map();
230
408
  const occupiedInternalPorts = new Map();
@@ -347,15 +525,23 @@ class BlockletState extends BaseState {
347
525
  return result;
348
526
  }
349
527
 
350
- async setBlockletStatus(did, status) {
528
+ /**
529
+ * @param {String} did blocklet did
530
+ * @param {BlockletStatus} status blocklet status
531
+ *
532
+ * children status only different with parent before blocklet installation
533
+ * @param {Array<componentId>} children
534
+ */
535
+ async setBlockletStatus(did, status, { children } = {}) {
351
536
  if (typeof status === 'undefined') {
352
537
  throw new Error('Unsupported blocklet status');
353
538
  }
354
- const record = await this.getBlocklet(did);
355
- // when status change update record
356
- if (record.status === status) {
357
- return record;
539
+
540
+ const doc = await this.getBlocklet(did);
541
+ if (doc.status === status && !children) {
542
+ return formatBlocklet(doc, 'onRead', this.config.dek);
358
543
  }
544
+
359
545
  const updates = { status, startedAt: undefined, stoppedAt: undefined };
360
546
  if (status === BlockletStatus.running) {
361
547
  updates.startedAt = new Date();
@@ -367,30 +553,46 @@ class BlockletState extends BaseState {
367
553
  updates.stoppedAt = new Date();
368
554
  }
369
555
 
370
- const res = await this.update(record._id, {
371
- $set: updates,
556
+ // update children status
557
+ forEachChildSync(doc, (child, { id }) => {
558
+ if (children === 'all') {
559
+ child.status = status;
560
+ return;
561
+ }
562
+
563
+ if (!children) {
564
+ if (
565
+ ![
566
+ BlockletStatus.waiting,
567
+ BlockletStatus.upgrading,
568
+ BlockletStatus.installing,
569
+ BlockletStatus.starting,
570
+ ].includes(status)
571
+ ) {
572
+ child.status = status;
573
+ }
574
+
575
+ return;
576
+ }
577
+
578
+ if (children.includes(id)) {
579
+ child.status = status;
580
+ }
372
581
  });
373
- return res;
374
- }
375
582
 
376
- getChildrenFromMetas(childrenMeta) {
377
- return childrenMeta.map((x) => ({ meta: x }));
583
+ updates.children = doc.children;
584
+ return this.updateBlocklet(did, updates);
378
585
  }
379
586
 
380
- async fillChildrenPorts(children, { defaultPort = 0, oldChildren } = {}) {
587
+ async fillChildrenPorts(children, { defaultPort = 0, oldChildren, returnMaxPort } = {}) {
381
588
  let _maxPort = defaultPort;
382
589
  for (const child of children || []) {
383
590
  // generate ports
384
591
  const childMeta = child.meta;
592
+ const oldChild = (oldChildren || []).find((x) => x.meta.did === child.meta.did);
385
593
 
386
594
  // get skipOccupiedCheckPorts
387
- let skipOccupiedCheckPorts = [];
388
- if (Array.isArray(oldChildren)) {
389
- const oldChild = oldChildren.find((x) => x.meta.did === child.meta.did);
390
- if (oldChild) {
391
- skipOccupiedCheckPorts = getExternalPortsFromMeta(oldChild.meta);
392
- }
393
- }
595
+ const skipOccupiedCheckPorts = oldChild ? getExternalPortsFromMeta(oldChild.meta) : [];
394
596
 
395
597
  const ports = await this.getBlockletPorts({
396
598
  interfaces: childMeta.interfaces || [],
@@ -399,22 +601,17 @@ class BlockletState extends BaseState {
399
601
  });
400
602
  _maxPort = getMaxPort(ports);
401
603
 
402
- // fill old child's port to new child
403
- if (Array.isArray(oldChildren)) {
404
- const oldChild = oldChildren.find((x) => x.meta.did === child.meta.did);
405
- if (oldChild && oldChild.ports) {
406
- logger.info('Merge the previous ports to child blocklet', {
407
- did: child.meta.did,
408
- name: child.meta.name,
409
- oldPorts: oldChild.ports,
410
- ports,
411
- });
412
- Object.keys(ports).forEach((p) => {
413
- ports[p] = oldChild.ports[p] || ports[p];
414
- });
415
- child.ports = ports;
416
- continue; // eslint-disable-line
417
- }
604
+ if (oldChild && oldChild.ports) {
605
+ // fill old child's port to new child
606
+ logger.info('Merge the previous ports to child blocklet', {
607
+ did: child.meta.did,
608
+ name: child.meta.name,
609
+ oldPorts: oldChild.ports,
610
+ ports,
611
+ });
612
+ Object.keys(ports).forEach((p) => {
613
+ ports[p] = oldChild.ports[p] || ports[p];
614
+ });
418
615
  }
419
616
 
420
617
  // assign a new port to child
@@ -422,10 +619,70 @@ class BlockletState extends BaseState {
422
619
  child.ports = ports;
423
620
  }
424
621
 
622
+ for (const child of children || []) {
623
+ const oldChild = (oldChildren || []).find((x) => x.meta.did === child.meta.did);
624
+
625
+ _maxPort = await this.fillChildrenPorts(child.children || [], {
626
+ defaultPort: _maxPort,
627
+ oldChildren: oldChild?.children,
628
+ returnMaxPort: true,
629
+ });
630
+ }
631
+
632
+ if (returnMaxPort) {
633
+ return _maxPort;
634
+ }
635
+
425
636
  return children;
426
637
  }
638
+
639
+ async addChildren(did, children) {
640
+ const parent = await this.getBlocklet(did);
641
+ if (!parent) {
642
+ throw new Error('Blocklet does not exist');
643
+ }
644
+
645
+ const oldChildren = parent.children || [];
646
+
647
+ const newChildren = [...oldChildren];
648
+ for (const child of children) {
649
+ const { meta, mountPoint, bundleSource = null, source = '', deployedFrom = '', mode } = child;
650
+
651
+ if (!mountPoint) {
652
+ throw new Error(`mountPoint is required when adding component ${getDisplayName(child, true)}`);
653
+ }
654
+
655
+ checkDuplicateComponents([child, ...newChildren]);
656
+
657
+ newChildren.push({
658
+ mountPoint,
659
+ meta,
660
+ bundleSource,
661
+ source,
662
+ deployedFrom,
663
+ mode,
664
+ status: BlockletStatus.added,
665
+ children: child.children,
666
+ });
667
+
668
+ fixChildren(newChildren);
669
+ }
670
+
671
+ // use upgradeBlocklet to assign ports to children and write new data to db
672
+ return this.upgradeBlocklet({
673
+ meta: parent.meta,
674
+ source: parent.source,
675
+ deployedFrom: parent.deployedFrom,
676
+ children: newChildren,
677
+ });
678
+ }
679
+
680
+ async updateStructV1Did(did, v1Did) {
681
+ return this.updateBlocklet(did, { structV1Did: v1Did });
682
+ }
427
683
  }
428
684
 
429
685
  BlockletState.BlockletStatus = BlockletStatus;
686
+ BlockletState.formatBlocklet = formatBlocklet;
430
687
 
431
688
  module.exports = BlockletState;
@@ -5,13 +5,15 @@ const BaseState = require('./base');
5
5
  * This db is used to save arbitrary cached data.
6
6
  */
7
7
  class CacheState extends BaseState {
8
- constructor(baseDir, options = {}) {
9
- super(baseDir, { filename: 'cache.db', ...options });
8
+ constructor(baseDir, config = {}) {
9
+ super(baseDir, { filename: 'cache.db', ...config });
10
10
 
11
- this.db.ensureIndex({ fieldName: 'key', unique: true }, (error) => {
12
- if (error) {
13
- logger.error('ensure index failed', { error });
14
- }
11
+ this.onReady(() => {
12
+ this.ensureIndex({ fieldName: 'key', unique: true }, (error) => {
13
+ if (error) {
14
+ logger.error('ensure index failed', { error });
15
+ }
16
+ });
15
17
  });
16
18
  }
17
19
 
@@ -9,17 +9,17 @@ class ChallengeState extends BaseState {
9
9
  /**
10
10
  * Creates an instance of ChallengeState
11
11
  * @param {string} baseDir
12
- * @param {object} options
12
+ * @param {object} config
13
13
  * @memberof ChallengeState
14
14
  */
15
- constructor(baseDir, options = {}) {
16
- super(baseDir, { filename: 'challenge.db', ...options });
15
+ constructor(baseDir, config = {}) {
16
+ super(baseDir, { filename: 'challenge.db', ...config });
17
17
  }
18
18
 
19
19
  generate() {
20
20
  return new Promise((resolve, reject) => {
21
21
  const challenge = stripHexPrefix(Mcrypto.getRandomBytes(16)).toUpperCase();
22
- this.db.insert({ challenge }, (err, data) => {
22
+ this.insert({ challenge }, (err, data) => {
23
23
  if (err) {
24
24
  logger.error('generating error', { error: err });
25
25
  return reject(err);
@@ -38,7 +38,7 @@ class ChallengeState extends BaseState {
38
38
  return;
39
39
  }
40
40
 
41
- this.db.findOne({ challenge }, (err, data) => {
41
+ this.findOne({ challenge }, (err, data) => {
42
42
  if (err) {
43
43
  logger.error('error find challenge', { error: err });
44
44
  }