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

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 +24 -64
  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 +14 -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 +136 -225
  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 +187 -374
  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,60 @@ 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;
148
143
  }
149
144
 
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
- }
145
+ async getBlocklet(did, { decryptSk = true } = {}) {
146
+ if (!did) {
147
+ return null;
148
+ }
160
149
 
161
- return resolve(doc ? formatBlocklet(doc, 'onRead', decryptSk ? this.config.dek : null) : null);
162
- });
163
- });
150
+ const doc = await this.findOne({ $or: getConditions(did) });
151
+ return doc ? formatBlocklet(doc, 'onRead', decryptSk ? this.config.dek : null) : null;
164
152
  }
165
153
 
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);
175
- }
176
-
177
- return resolve(doc ? doc.meta.did : null);
178
- });
179
- });
154
+ async getBlockletMetaDid(did) {
155
+ const doc = await this.getBlocklet(did);
156
+ return doc ? doc.meta.did : null;
180
157
  }
181
158
 
182
159
  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
- });
160
+ const doc = await this.getBlocklet(did);
161
+ return doc ? doc.status : null;
196
162
  }
197
163
 
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
- }
164
+ async hasBlocklet(did) {
165
+ if (!did) {
166
+ return false;
167
+ }
208
168
 
209
- return resolve(!!count);
210
- });
211
- });
169
+ const count = await this.count({ $or: getConditions(did) });
170
+ return count > 0;
212
171
  }
213
172
 
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
- });
173
+ async getBlocklets(query = {}, projection = {}, sort = { createdAt: -1 }) {
174
+ const docs = await this.find(query, projection, sort);
175
+ return docs.filter(Boolean).map((doc) => formatBlocklet(doc, 'onRead', this.config.dek));
227
176
  }
228
177
 
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
- }
237
-
238
- this.remove({ _id: doc._id }, (err) => {
239
- if (err) {
240
- return reject(err);
241
- }
178
+ async deleteBlocklet(did) {
179
+ const doc = await this.getBlocklet(did);
180
+ if (!doc) {
181
+ throw new Error(`Try to remove non-existing blocklet ${did}`);
182
+ }
242
183
 
243
- this.emit('remove', doc);
244
- return resolve(formatBlocklet(doc, 'onRead', this.config.dek));
245
- });
246
- })
247
- );
184
+ await this.remove({ id: doc.id });
185
+ this.emit('remove', doc);
186
+ return formatBlocklet(doc, 'onRead', this.config.dek);
248
187
  }
249
188
 
250
- addBlocklet({
189
+ async addBlocklet({
251
190
  meta,
252
191
  source = BlockletSource.registry,
253
192
  status = BlockletStatus.added,
@@ -261,139 +200,111 @@ class BlockletState extends BaseState {
261
200
  externalSk = true,
262
201
  externalSkSource = '',
263
202
  } = {}) {
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
- }
203
+ let doc = await this.getBlocklet(meta.did);
204
+ if (doc) {
205
+ throw new Error('Blocklet already added');
206
+ }
271
207
 
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
- }
208
+ try {
209
+ fixPerson(meta);
210
+ fixInterfaces(meta);
211
+ let sanitized = validateBlockletMeta(meta);
212
+ // bundle info
213
+ sanitized = ensureMeta(sanitized);
214
+ sanitized = omit(sanitized, ['htmlAst']);
215
+
216
+ // get ports
217
+ await lock.acquire();
218
+ const ports = await this.getBlockletPorts({ interfaces: sanitized.interfaces || [] });
219
+ const children = await this.fillChildrenPorts(rawChildren, { defaultPort: getMaxPort(ports) });
220
+ fixChildren(children);
221
+
222
+ // add to db
223
+ doc = await this.insert({
224
+ appDid: appPid, // will updated later when updating blocklet environments
225
+ appPid,
226
+ mode,
227
+ meta: sanitized,
228
+ status,
229
+ source,
230
+ deployedFrom,
231
+ ports,
232
+ environments: [],
233
+ children,
234
+ migratedFrom,
235
+ externalSk,
236
+ externalSkSource,
237
+ structVersion: APP_STRUCT_VERSION,
238
+ });
314
239
 
315
- this.emit('add', doc);
316
- return resolve(doc);
317
- });
318
- } catch (err) {
319
- lock.release();
320
- reject(err);
321
- }
322
- })
323
- );
240
+ doc = await this.findOne({ id: doc.id });
241
+
242
+ lock.release();
243
+ this.emit('add', doc);
244
+
245
+ return doc;
246
+ } catch (err) {
247
+ lock.release();
248
+ throw err;
249
+ }
324
250
  }
325
251
 
326
252
  // 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
- }
253
+ async updateBlocklet(did, updates) {
254
+ const doc = await this.getBlocklet(did);
255
+ if (!doc) {
256
+ throw new Error('Blocklet does not exist');
257
+ }
335
258
 
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
- );
259
+ const formatted = formatBlocklet(cloneDeep(updates), 'onUpdate', this.config.dek);
260
+ const [, [updated]] = await this.update({ id: doc.id }, { $set: formatted });
261
+ return updated;
345
262
  }
346
263
 
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
- }
264
+ async upgradeBlocklet({ meta, source, deployedFrom = '', children } = {}) {
265
+ const doc = await this.getBlocklet(meta.did);
266
+ if (!doc) {
267
+ throw new Error('Blocklet does not exist');
268
+ }
356
269
 
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
- );
270
+ try {
271
+ fixPerson(meta);
272
+ fixInterfaces(meta);
273
+ const sanitized = validateBlockletMeta(meta);
274
+
275
+ // get ports
276
+ await lock.acquire();
277
+
278
+ const ports = await this.getBlockletPorts({
279
+ interfaces: sanitized.interfaces || [],
280
+ skipOccupiedCheckPorts: getExternalPortsFromMeta(doc.meta),
281
+ });
282
+ Object.keys(ports).forEach((p) => {
283
+ ports[p] = doc.ports[p] || ports[p];
284
+ });
285
+
286
+ // fill
287
+ logger.info('Fill children ports when when upgrading blocklet', { name: doc.meta.name, did: doc.meta.did });
288
+ await this.fillChildrenPorts(children, { oldChildren: doc.children, defaultPort: getMaxPort(ports) });
289
+
290
+ fixChildren(children);
291
+
292
+ // add to db
293
+ const newDoc = await this.updateBlocklet(meta.did, {
294
+ meta: omit(sanitized, ['htmlAst']),
295
+ source,
296
+ deployedFrom,
297
+ children,
298
+ ports,
299
+ });
300
+ lock.release();
301
+
302
+ this.emit('upgrade', newDoc);
303
+ return newDoc;
304
+ } catch (err) {
305
+ lock.release();
306
+ throw err;
307
+ }
397
308
  }
398
309
 
399
310
  async getBlockletPorts({
@@ -404,24 +315,24 @@ class BlockletState extends BaseState {
404
315
  defaultPort = 0,
405
316
  } = {}) {
406
317
  try {
407
- const blocklets = await this.getBlocklets({}, { port: 1, ports: 1, 'meta.interfaces': 1, children: 1 });
318
+ const blocklets = await this.getBlocklets({}, { port: 1, ports: 1, meta: 1, children: 1 });
408
319
 
409
320
  const occupiedExternalPorts = new Map();
410
321
  const occupiedInternalPorts = new Map();
411
322
 
412
323
  const calcPortsFromBlocklet = (blocklet) => {
413
- occupiedInternalPorts.set(Number(blocklet.port));
324
+ occupiedInternalPorts.set(Number(blocklet.port), true);
414
325
 
415
326
  if (blocklet.ports && typeof blocklet.ports === 'object') {
416
327
  Object.keys(blocklet.ports).forEach((key) => {
417
- occupiedInternalPorts.set(Number(blocklet.ports[key]));
328
+ occupiedInternalPorts.set(Number(blocklet.ports[key]), true);
418
329
  });
419
330
  }
420
331
 
421
332
  if (Array.isArray(blocklet.meta.interfaces)) {
422
333
  blocklet.meta.interfaces.forEach((x) => {
423
334
  if (x.port && x.port.external) {
424
- occupiedExternalPorts.set(Number(x.port.external));
335
+ occupiedExternalPorts.set(Number(x.port.external), true);
425
336
  }
426
337
  });
427
338
  }
@@ -566,7 +477,7 @@ class BlockletState extends BaseState {
566
477
  return formatBlocklet(doc, 'onRead', this.config.dek);
567
478
  }
568
479
 
569
- const updates = { status, startedAt: undefined, stoppedAt: undefined };
480
+ const updates = { status, startedAt: null, stoppedAt: null };
570
481
  if (status === BlockletStatus.running) {
571
482
  updates.startedAt = new Date();
572
483
  }
@@ -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 {