@abtnode/core 1.16.8-beta-186fd5aa → 1.16.8-next-c66e39c7

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 (38) hide show
  1. package/lib/api/team.js +42 -62
  2. package/lib/blocklet/manager/disk.js +2 -8
  3. package/lib/blocklet/manager/helper/migrate-application-to-struct-v2.js +5 -5
  4. package/lib/blocklet/storage/backup/blocklet-extras.js +2 -2
  5. package/lib/blocklet/storage/backup/blocklet.js +2 -2
  6. package/lib/index.js +17 -16
  7. package/lib/migrations/1.16.8-component-title.js +1 -1
  8. package/lib/migrations/1.6.9-update-node-info-and-certificate.js +1 -1
  9. package/lib/migrations/index.js +190 -40
  10. package/lib/monitor/node-runtime-monitor.js +2 -29
  11. package/lib/router/helper.js +6 -6
  12. package/lib/router/manager.js +35 -36
  13. package/lib/states/access-key.js +3 -20
  14. package/lib/states/audit-log.js +7 -8
  15. package/lib/states/backup.js +11 -59
  16. package/lib/states/base.js +13 -5
  17. package/lib/states/blocklet-extras.js +11 -8
  18. package/lib/states/blocklet.js +148 -222
  19. package/lib/states/cache.js +3 -21
  20. package/lib/states/connect-account.js +8 -0
  21. package/lib/states/index.js +28 -18
  22. package/lib/states/job.js +8 -0
  23. package/lib/states/migration.js +3 -4
  24. package/lib/states/node.js +104 -145
  25. package/lib/states/notification.js +18 -40
  26. package/lib/states/passport.js +8 -0
  27. package/lib/states/session.js +28 -44
  28. package/lib/states/site.js +32 -39
  29. package/lib/states/user.js +169 -378
  30. package/lib/states/webhook.js +5 -7
  31. package/lib/team/manager.js +108 -116
  32. package/lib/util/blocklet.js +0 -1
  33. package/lib/util/index.js +3 -0
  34. package/lib/util/queue.js +14 -20
  35. package/lib/util/ready.js +1 -1
  36. package/lib/webhook/index.js +6 -4
  37. package/package.json +19 -18
  38. package/lib/states/challenge.js +0 -58
@@ -28,7 +28,7 @@ const { validateBlockletMeta } = require('../util/blocklet');
28
28
  const lock = new Lock('blocklet-port-assign-lock');
29
29
 
30
30
  const isHex = (str) => /^0x[0-9a-f]+$/i.test(str);
31
- const getMaxPort = (ports = {}) => Math.max(Object.values(ports).map(Number));
31
+ const getMaxPort = (ports = {}) => Math.max(...Object.values(ports).map(Number));
32
32
 
33
33
  // structV1Did is just for migration purpose and should be removed in the future
34
34
  const getConditions = (did) => [{ 'meta.did': did }, { appDid: did }, { appPid: did }, { structV1Did: did }];
@@ -133,121 +133,75 @@ const fixChildren = (children) => {
133
133
  // [name: string]: Number; // name is in meta.interfaces[].port
134
134
  // };
135
135
 
136
+ /**
137
+ * @extends BaseState<import('@abtnode/models').BlockletState>
138
+ */
136
139
  class BlockletState extends BaseState {
137
- /**
138
- * Creates an instance of BlockletState
139
- * @param {string} baseDir
140
- * @param {object} config
141
- * @param {string} config.blockletPort - from which port to start new blocklets
142
- * @memberof BlockletState
143
- */
144
- constructor(baseDir, config = {}) {
145
- super(baseDir, { filename: 'blocklet.db', ...config });
146
-
140
+ constructor(model, config = {}) {
141
+ super(model, config);
147
142
  this.defaultPort = config.blockletPort || 5555;
143
+ // @didMap: { [did: string]: metaDid: string }
144
+ this.didMap = new Map();
148
145
  }
149
146
 
150
- getBlocklet(did, { decryptSk = true } = {}) {
151
- return new Promise((resolve, reject) => {
152
- if (!did) {
153
- resolve(null);
154
- }
155
-
156
- this.findOne({ $or: getConditions(did) }, (err, doc) => {
157
- if (err) {
158
- return reject(err);
159
- }
147
+ async getBlocklet(did, { decryptSk = true } = {}) {
148
+ if (!did) {
149
+ return null;
150
+ }
160
151
 
161
- return resolve(doc ? formatBlocklet(doc, 'onRead', decryptSk ? this.config.dek : null) : null);
162
- });
163
- });
152
+ const doc = await this.findOne({ $or: getConditions(did) });
153
+ return doc ? formatBlocklet(doc, 'onRead', decryptSk ? this.config.dek : null) : null;
164
154
  }
165
155
 
166
- getBlockletMetaDid(did) {
167
- return new Promise((resolve, reject) => {
168
- if (!did) {
169
- resolve(null);
170
- }
156
+ async getBlockletMetaDid(did) {
157
+ if (this.didMap.has(did)) {
158
+ return this.didMap.get(did);
159
+ }
171
160
 
172
- this.findOne({ $or: getConditions(did) }, (err, doc) => {
173
- if (err) {
174
- return reject(err);
175
- }
161
+ const doc = await this.getBlocklet(did);
162
+ if (doc?.meta?.did) {
163
+ this.didMap.set(did, doc.meta.did);
164
+ return doc.meta.did;
165
+ }
176
166
 
177
- return resolve(doc ? doc.meta.did : null);
178
- });
179
- });
167
+ return null;
180
168
  }
181
169
 
182
170
  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
- });
171
+ const doc = await this.getBlocklet(did);
172
+ return doc ? doc.status : null;
196
173
  }
197
174
 
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
- }
175
+ async hasBlocklet(did) {
176
+ if (!did) {
177
+ return false;
178
+ }
208
179
 
209
- return resolve(!!count);
210
- });
211
- });
180
+ const count = await this.count({ $or: getConditions(did) });
181
+ return count > 0;
212
182
  }
213
183
 
214
- getBlocklets(query = {}, projection) {
215
- return new Promise((resolve, reject) => {
216
- this.cursor(query)
217
- .projection(projection)
218
- .sort({ createdAt: -1 })
219
- .exec((err, docs = []) => {
220
- if (err) {
221
- return reject(err);
222
- }
223
-
224
- return resolve(docs.filter(Boolean).map((doc) => formatBlocklet(doc, 'onRead', this.config.dek)));
225
- });
226
- });
184
+ async getBlocklets(query = {}, projection = {}, sort = { createdAt: -1 }) {
185
+ const docs = await this.find(query, projection, sort);
186
+ return docs.filter(Boolean).map((doc) => formatBlocklet(doc, 'onRead', this.config.dek));
227
187
  }
228
188
 
229
- deleteBlocklet(did) {
230
- return this.getBlocklet(did).then(
231
- (doc) =>
232
- // eslint-disable-next-line consistent-return
233
- new Promise((resolve, reject) => {
234
- if (!doc) {
235
- return reject(new Error(`Try to remove non-existing blocklet ${did}`));
236
- }
189
+ async deleteBlocklet(did) {
190
+ const doc = await this.getBlocklet(did);
191
+ if (!doc) {
192
+ throw new Error(`Try to remove non-existing blocklet ${did}`);
193
+ }
237
194
 
238
- this.remove({ _id: doc._id }, (err) => {
239
- if (err) {
240
- return reject(err);
241
- }
195
+ await this.remove({ id: doc.id });
242
196
 
243
- this.emit('remove', doc);
244
- return resolve(formatBlocklet(doc, 'onRead', this.config.dek));
245
- });
246
- })
247
- );
197
+ this.didMap.delete(doc.meta?.did);
198
+ this.didMap.delete(doc.appDid);
199
+
200
+ this.emit('remove', doc);
201
+ return formatBlocklet(doc, 'onRead', this.config.dek);
248
202
  }
249
203
 
250
- addBlocklet({
204
+ async addBlocklet({
251
205
  meta,
252
206
  source = BlockletSource.registry,
253
207
  status = BlockletStatus.added,
@@ -261,139 +215,111 @@ class BlockletState extends BaseState {
261
215
  externalSk = true,
262
216
  externalSkSource = '',
263
217
  } = {}) {
264
- return this.getBlocklet(meta.did).then(
265
- (exist) =>
266
- new Promise(async (resolve, reject) => {
267
- if (exist) {
268
- reject(new Error('Blocklet already added'));
269
- return;
270
- }
218
+ let doc = await this.getBlocklet(meta.did);
219
+ if (doc) {
220
+ throw new Error('Blocklet already added');
221
+ }
271
222
 
272
- try {
273
- fixPerson(meta);
274
- fixInterfaces(meta);
275
- let sanitized = validateBlockletMeta(meta);
276
- // bundle info
277
- sanitized = ensureMeta(sanitized);
278
- sanitized = omit(sanitized, ['htmlAst']);
279
-
280
- // get ports
281
- await lock.acquire();
282
-
283
- const ports = await this.getBlockletPorts({ interfaces: sanitized.interfaces || [] });
284
-
285
- const children = await this.fillChildrenPorts(rawChildren, {
286
- defaultPort: getMaxPort(ports),
287
- });
288
-
289
- fixChildren(children);
290
-
291
- const data = {
292
- appDid: null, // will updated later when updating blocklet environments
293
- appPid,
294
- mode,
295
- meta: sanitized,
296
- status,
297
- source,
298
- deployedFrom,
299
- ports,
300
- environments: [],
301
- children,
302
- migratedFrom,
303
- externalSk,
304
- externalSkSource,
305
- structVersion: APP_STRUCT_VERSION,
306
- };
307
-
308
- // add to db
309
- this.insert(data, (err, doc) => {
310
- lock.release();
311
- if (err) {
312
- return reject(err);
313
- }
223
+ try {
224
+ fixPerson(meta);
225
+ fixInterfaces(meta);
226
+ let sanitized = validateBlockletMeta(meta);
227
+ // bundle info
228
+ sanitized = ensureMeta(sanitized);
229
+ sanitized = omit(sanitized, ['htmlAst']);
230
+
231
+ // get ports
232
+ await lock.acquire();
233
+ const ports = await this.getBlockletPorts({ interfaces: sanitized.interfaces || [] });
234
+ const children = await this.fillChildrenPorts(rawChildren, { defaultPort: getMaxPort(ports) });
235
+ fixChildren(children);
236
+
237
+ // add to db
238
+ doc = await this.insert({
239
+ appDid: appPid, // will updated later when updating blocklet environments
240
+ appPid,
241
+ mode,
242
+ meta: sanitized,
243
+ status,
244
+ source,
245
+ deployedFrom,
246
+ ports,
247
+ environments: [],
248
+ children,
249
+ migratedFrom,
250
+ externalSk,
251
+ externalSkSource,
252
+ structVersion: APP_STRUCT_VERSION,
253
+ });
314
254
 
315
- this.emit('add', doc);
316
- return resolve(doc);
317
- });
318
- } catch (err) {
319
- lock.release();
320
- reject(err);
321
- }
322
- })
323
- );
255
+ doc = await this.findOne({ id: doc.id });
256
+
257
+ lock.release();
258
+ this.emit('add', doc);
259
+
260
+ return doc;
261
+ } catch (err) {
262
+ lock.release();
263
+ throw err;
264
+ }
324
265
  }
325
266
 
326
267
  // FIXME: 这个接口比较危险,可能会修改一些本不应该修改的字段,后续需要考虑改进
327
- updateBlocklet(did, updates) {
328
- return this.getBlocklet(did).then(
329
- (doc) =>
330
- new Promise(async (resolve, reject) => {
331
- if (!doc) {
332
- reject(new Error('Blocklet does not exist'));
333
- return;
334
- }
268
+ async updateBlocklet(did, updates) {
269
+ const doc = await this.getBlocklet(did);
270
+ if (!doc) {
271
+ throw new Error('Blocklet does not exist');
272
+ }
335
273
 
336
- try {
337
- const formatted = formatBlocklet(cloneDeep(updates), 'onUpdate', this.config.dek);
338
- const newDoc = await this.updateById(doc._id, { $set: formatted });
339
- resolve(newDoc);
340
- } catch (err) {
341
- reject(err);
342
- }
343
- })
344
- );
274
+ const formatted = formatBlocklet(cloneDeep(updates), 'onUpdate', this.config.dek);
275
+ const [, [updated]] = await this.update({ id: doc.id }, { $set: formatted });
276
+ return updated;
345
277
  }
346
278
 
347
- upgradeBlocklet({ meta, source, deployedFrom = '', children } = {}) {
348
- return this.getBlocklet(meta.did).then(
349
- (doc) =>
350
- // eslint-disable-next-line no-async-promise-executor
351
- new Promise(async (resolve, reject) => {
352
- if (!doc) {
353
- reject(new Error('Blocklet does not exist'));
354
- return;
355
- }
279
+ async upgradeBlocklet({ meta, source, deployedFrom = '', children } = {}) {
280
+ const doc = await this.getBlocklet(meta.did);
281
+ if (!doc) {
282
+ throw new Error('Blocklet does not exist');
283
+ }
356
284
 
357
- try {
358
- fixPerson(meta);
359
- fixInterfaces(meta);
360
- const sanitized = validateBlockletMeta(meta);
361
-
362
- // get ports
363
- await lock.acquire();
364
-
365
- const ports = await this.getBlockletPorts({
366
- interfaces: sanitized.interfaces || [],
367
- skipOccupiedCheckPorts: getExternalPortsFromMeta(doc.meta),
368
- });
369
- Object.keys(ports).forEach((p) => {
370
- ports[p] = doc.ports[p] || ports[p];
371
- });
372
-
373
- // fill
374
- logger.info('Fill children ports when when upgrading blocklet', { name: doc.meta.name, did: doc.meta.did });
375
- await this.fillChildrenPorts(children, { oldChildren: doc.children, defaultPort: getMaxPort(ports) });
376
-
377
- fixChildren(children);
378
-
379
- // add to db
380
- const newDoc = await this.updateBlocklet(meta.did, {
381
- meta: omit(sanitized, ['htmlAst']),
382
- source,
383
- deployedFrom,
384
- children,
385
- ports,
386
- });
387
- lock.release();
388
-
389
- this.emit('upgrade', newDoc);
390
- resolve(newDoc);
391
- } catch (err) {
392
- lock.release();
393
- reject(err);
394
- }
395
- })
396
- );
285
+ try {
286
+ fixPerson(meta);
287
+ fixInterfaces(meta);
288
+ const sanitized = validateBlockletMeta(meta);
289
+
290
+ // get ports
291
+ await lock.acquire();
292
+
293
+ const ports = await this.getBlockletPorts({
294
+ interfaces: sanitized.interfaces || [],
295
+ skipOccupiedCheckPorts: getExternalPortsFromMeta(doc.meta),
296
+ });
297
+ Object.keys(ports).forEach((p) => {
298
+ ports[p] = doc.ports[p] || ports[p];
299
+ });
300
+
301
+ // fill
302
+ logger.info('Fill children ports when when upgrading blocklet', { name: doc.meta.name, did: doc.meta.did });
303
+ await this.fillChildrenPorts(children, { oldChildren: doc.children, defaultPort: getMaxPort(ports) });
304
+
305
+ fixChildren(children);
306
+
307
+ // add to db
308
+ const newDoc = await this.updateBlocklet(meta.did, {
309
+ meta: omit(sanitized, ['htmlAst']),
310
+ source,
311
+ deployedFrom,
312
+ children,
313
+ ports,
314
+ });
315
+ lock.release();
316
+
317
+ this.emit('upgrade', newDoc);
318
+ return newDoc;
319
+ } catch (err) {
320
+ lock.release();
321
+ throw err;
322
+ }
397
323
  }
398
324
 
399
325
  async getBlockletPorts({
@@ -404,24 +330,24 @@ class BlockletState extends BaseState {
404
330
  defaultPort = 0,
405
331
  } = {}) {
406
332
  try {
407
- const blocklets = await this.getBlocklets({}, { port: 1, ports: 1, 'meta.interfaces': 1, children: 1 });
333
+ const blocklets = await this.getBlocklets({}, { port: 1, ports: 1, meta: 1, children: 1 });
408
334
 
409
335
  const occupiedExternalPorts = new Map();
410
336
  const occupiedInternalPorts = new Map();
411
337
 
412
338
  const calcPortsFromBlocklet = (blocklet) => {
413
- occupiedInternalPorts.set(Number(blocklet.port));
339
+ occupiedInternalPorts.set(Number(blocklet.port), true);
414
340
 
415
341
  if (blocklet.ports && typeof blocklet.ports === 'object') {
416
342
  Object.keys(blocklet.ports).forEach((key) => {
417
- occupiedInternalPorts.set(Number(blocklet.ports[key]));
343
+ occupiedInternalPorts.set(Number(blocklet.ports[key]), true);
418
344
  });
419
345
  }
420
346
 
421
347
  if (Array.isArray(blocklet.meta.interfaces)) {
422
348
  blocklet.meta.interfaces.forEach((x) => {
423
349
  if (x.port && x.port.external) {
424
- occupiedExternalPorts.set(Number(x.port.external));
350
+ occupiedExternalPorts.set(Number(x.port.external), true);
425
351
  }
426
352
  });
427
353
  }
@@ -566,7 +492,7 @@ class BlockletState extends BaseState {
566
492
  return formatBlocklet(doc, 'onRead', this.config.dek);
567
493
  }
568
494
 
569
- const updates = { status, startedAt: undefined, stoppedAt: undefined };
495
+ const updates = { status, startedAt: null, stoppedAt: null };
570
496
  if (status === BlockletStatus.running) {
571
497
  updates.startedAt = new Date();
572
498
  }
@@ -1,31 +1,13 @@
1
- const logger = require('@abtnode/logger')('@abtnode/core:states:cache');
2
1
  const BaseState = require('./base');
3
2
 
4
3
  /**
5
4
  * This db is used to save arbitrary cached data.
5
+ * @extends BaseState<import('@abtnode/models').CacheState>
6
6
  */
7
7
  class CacheState extends BaseState {
8
- constructor(baseDir, config = {}) {
9
- super(baseDir, { filename: 'cache.db', ...config });
10
-
11
- this.onReady(() => {
12
- this.ensureIndex({ fieldName: 'key', unique: true }, (error) => {
13
- if (error) {
14
- logger.error('ensure index failed', { error });
15
- }
16
- });
17
- });
18
- }
19
-
20
8
  async set(key, value) {
21
- const [, { value: res }] = await this.update(
22
- { key },
23
- { $set: { value } },
24
- {
25
- upsert: true,
26
- }
27
- );
28
- return res;
9
+ const doc = await this.upsert({ key }, { value });
10
+ return doc.value;
29
11
  }
30
12
 
31
13
  async get(key) {
@@ -0,0 +1,8 @@
1
+ const BaseState = require('./base');
2
+
3
+ /**
4
+ * @extends BaseState<import('@abtnode/models').ConnectedAccountState>
5
+ */
6
+ class ConnectedAccount extends BaseState {}
7
+
8
+ module.exports = ConnectedAccount;
@@ -1,6 +1,7 @@
1
- const stateFactory = require('@abtnode/db/lib/factory');
1
+ const path = require('path');
2
+ const { createSequelize, createStateFactory, getServerModels, setupModels } = require('@abtnode/models');
3
+
2
4
  const NodeState = require('./node');
3
- const ChallengeState = require('./challenge');
4
5
  const BlockletState = require('./blocklet');
5
6
  const NotificationState = require('./notification');
6
7
  const SiteState = require('./site');
@@ -11,22 +12,31 @@ const SessionState = require('./session');
11
12
  const ExtrasState = require('./blocklet-extras');
12
13
  const CacheState = require('./cache');
13
14
  const AuditLogState = require('./audit-log');
15
+ const JobState = require('./job');
14
16
  const BackupState = require('./backup');
17
+ const { getDbFilePath } = require('../util');
18
+
19
+ const models = getServerModels();
20
+
21
+ const init = (dataDirs, config = {}) => {
22
+ const dbPath = getDbFilePath(path.join(dataDirs.core, 'server.db'));
23
+ const sequelize = createSequelize(dbPath);
24
+ setupModels(models, sequelize);
25
+ console.info(`Init server states in ${dbPath}`); // eslint-disable-line
15
26
 
16
- const init = (dataDirs, config) => {
17
- const notificationState = new NotificationState(dataDirs.core, config);
18
- const nodeState = new NodeState(dataDirs.core, config, dataDirs, notificationState);
19
- const blockletState = new BlockletState(dataDirs.core, config);
20
- const challengeState = new ChallengeState(dataDirs.core, config);
21
- const siteState = new SiteState(dataDirs.core, config);
22
- const accessKeyState = new AccessKeyState(dataDirs.core, config);
23
- const webhookState = new WebhookState(dataDirs.core, config);
24
- const migrationState = new MigrationState(dataDirs.core, config);
25
- const sessionState = new SessionState(dataDirs.core, config);
26
- const extrasState = new ExtrasState(dataDirs.core, config);
27
- const cacheState = new CacheState(dataDirs.core, config);
28
- const auditLogState = new AuditLogState(dataDirs.core, config);
29
- const backupState = new BackupState(dataDirs.core, config);
27
+ const notificationState = new NotificationState(models.Notification, config);
28
+ const nodeState = new NodeState(models.Server, config, dataDirs, notificationState);
29
+ const blockletState = new BlockletState(models.Blocklet, config);
30
+ const siteState = new SiteState(models.Site, config);
31
+ const accessKeyState = new AccessKeyState(models.AccessKey, config);
32
+ const webhookState = new WebhookState(models.WebHook, config);
33
+ const migrationState = new MigrationState(models.Migration, config);
34
+ const sessionState = new SessionState(models.Session, config);
35
+ const extrasState = new ExtrasState(models.BlockletExtra, config);
36
+ const cacheState = new CacheState(models.Cache, config);
37
+ const auditLogState = new AuditLogState(models.AuditLog, config);
38
+ const jobState = new JobState(models.Job, config);
39
+ const backupState = new BackupState(models.Backup, config);
30
40
 
31
41
  return {
32
42
  node: nodeState,
@@ -36,13 +46,13 @@ const init = (dataDirs, config) => {
36
46
  accessKey: accessKeyState,
37
47
  webhook: webhookState,
38
48
  migration: migrationState,
39
- challenge: challengeState,
40
49
  session: sessionState,
41
50
  blockletExtras: extrasState,
42
51
  cache: cacheState,
43
52
  auditLog: auditLogState,
53
+ job: jobState,
44
54
  backup: backupState,
45
55
  };
46
56
  };
47
57
 
48
- module.exports = stateFactory(init);
58
+ module.exports = createStateFactory(init, models);
@@ -0,0 +1,8 @@
1
+ const BaseState = require('./base');
2
+
3
+ /**
4
+ * @extends BaseState<import('@abtnode/models').JobState>
5
+ */
6
+ class Job extends BaseState {}
7
+
8
+ module.exports = Job;
@@ -3,11 +3,10 @@ const logger = require('@abtnode/logger')('@abtnode/core:states:migration');
3
3
 
4
4
  const BaseState = require('./base');
5
5
 
6
+ /**
7
+ * @extends BaseState<import('@abtnode/models').MigrationState>
8
+ */
6
9
  class MigrationState extends BaseState {
7
- constructor(baseDir, config = {}) {
8
- super(baseDir, { filename: 'migration.db', ...config });
9
- }
10
-
11
10
  // eslint-disable-next-line no-unused-vars
12
11
  async isExecuted({ script, version }, context) {
13
12
  try {