@abtnode/core 1.16.21-beta-445a8baa → 1.16.21-beta-2e75c75c

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/api/team.js CHANGED
@@ -1399,7 +1399,7 @@ class TeamAPI extends EventEmitter {
1399
1399
 
1400
1400
  const userSessions = await state.model.findAll({
1401
1401
  where,
1402
- attributes: ['id', 'appPid', 'userDid', 'visitorId', 'passportId', 'updatedAt'],
1402
+ attributes: ['id', 'appPid', 'userDid', 'visitorId', 'passportId', 'updatedAt', 'extra'],
1403
1403
  include: {
1404
1404
  model: userState.model,
1405
1405
  as: 'user',
@@ -1442,10 +1442,11 @@ class TeamAPI extends EventEmitter {
1442
1442
  * @param {string} params.lastLoginIp - 记录的 userSession 中当前用户的最后登录 ip
1443
1443
  * @param {string} params.passportId - 记录的 userSession 中当前用户登录所使用的 passportId
1444
1444
  * @param {string} params.status - 记录的 userSession 的状态
1445
+ * @param {Object} params.extra - 记录的 userSession 额外的数据
1445
1446
  * @returns
1446
1447
  */
1447
1448
 
1448
- async upsertUserSession({ teamDid, appPid, userDid, visitorId, ua, lastLoginIp, passportId, status }) {
1449
+ async upsertUserSession({ teamDid, appPid, userDid, visitorId, ua, lastLoginIp, passportId, status, extra }) {
1449
1450
  if (!userDid) {
1450
1451
  throw new Error('userDid are required');
1451
1452
  }
@@ -1459,13 +1460,14 @@ class TeamAPI extends EventEmitter {
1459
1460
  lastLoginIp,
1460
1461
  appPid,
1461
1462
  passportId,
1463
+ extra,
1462
1464
  });
1463
- logger.info('insert userSession successfully', { userDid, ua, lastLoginIp, appPid, passportId });
1465
+ logger.info('insert userSession successfully', { userDid, ua, lastLoginIp, appPid, passportId, extra });
1464
1466
  } else {
1465
1467
  const exist = await state.findOne({ userDid, visitorId, appPid });
1466
1468
  if (exist) {
1467
- [, [data]] = await state.update(exist.id, { ua, lastLoginIp, passportId, status });
1468
- logger.info('update userSession successfully', { id: exist.id, ua, lastLoginIp, passportId, status });
1469
+ [, [data]] = await state.update(exist.id, { ua, lastLoginIp, passportId, status, extra });
1470
+ logger.info('update userSession successfully', { id: exist.id, ua, lastLoginIp, passportId, status, extra });
1469
1471
  } else {
1470
1472
  data = await state.insert({
1471
1473
  visitorId,
@@ -1474,8 +1476,17 @@ class TeamAPI extends EventEmitter {
1474
1476
  lastLoginIp,
1475
1477
  appPid,
1476
1478
  passportId,
1479
+ extra,
1480
+ });
1481
+ logger.info('insert userSession successfully', {
1482
+ visitorId,
1483
+ userDid,
1484
+ ua,
1485
+ lastLoginIp,
1486
+ appPid,
1487
+ passportId,
1488
+ extra,
1477
1489
  });
1478
- logger.info('insert userSession successfully', { visitorId, userDid, ua, lastLoginIp, appPid, passportId });
1479
1490
  }
1480
1491
  }
1481
1492
 
@@ -1491,9 +1502,10 @@ class TeamAPI extends EventEmitter {
1491
1502
  * @param {string} params.ua - 同步的 userSession 中当前用户所使用设备的 ua
1492
1503
  * @param {string} params.lastLoginIp - 同步的 userSession 中当前用户的最后登录 ip
1493
1504
  * @param {string} params.passportId - 同步的 userSession 中当前用户登录所使用的 passportId
1505
+ * @param {object} params.extra - 同步的 userSession 中当前用户登录额外的数据
1494
1506
  * @returns
1495
1507
  */
1496
- async syncUserSession({ teamDid, targetAppPid, userDid, visitorId, ua, lastLoginIp, passportId }) {
1508
+ async syncUserSession({ teamDid, targetAppPid, userDid, visitorId, ua, lastLoginIp, passportId, extra }) {
1497
1509
  if (!targetAppPid) return;
1498
1510
 
1499
1511
  const blocklet = await getBlocklet({ did: teamDid, states: this.states, dataDirs: this.dataDirs });
@@ -1526,6 +1538,7 @@ class TeamAPI extends EventEmitter {
1526
1538
  lastLoginIp,
1527
1539
  appPid,
1528
1540
  passportId,
1541
+ extra,
1529
1542
  };
1530
1543
  await callFederated({
1531
1544
  action: 'sync',
@@ -1543,6 +1556,7 @@ class TeamAPI extends EventEmitter {
1543
1556
  appPid,
1544
1557
  passportId,
1545
1558
  targetSite,
1559
+ extra,
1546
1560
  });
1547
1561
  } catch (err) {
1548
1562
  logger.info(
@@ -1555,6 +1569,7 @@ class TeamAPI extends EventEmitter {
1555
1569
  appPid,
1556
1570
  passportId,
1557
1571
  targetSite,
1572
+ extra,
1558
1573
  },
1559
1574
  err
1560
1575
  );
@@ -30,6 +30,7 @@ const {
30
30
  BACKUPS,
31
31
  DEFAULT_DID_DOMAIN,
32
32
  FEDERATED,
33
+ CHECK_UPDATE,
33
34
  } = require('@abtnode/constant');
34
35
 
35
36
  const getBlockletEngine = require('@blocklet/meta/lib/engine');
@@ -50,6 +51,7 @@ const {
50
51
  findComponentByIdV2,
51
52
  isGatewayBlocklet,
52
53
  hasStartEngine,
54
+ getDisplayName,
53
55
  } = require('@blocklet/meta/lib/util');
54
56
  const { getComponentsInternalInfo } = require('@blocklet/meta/lib/blocklet');
55
57
  const { update: updateMetaFile } = require('@blocklet/meta/lib/file');
@@ -96,6 +98,7 @@ const pRetry = require('p-retry');
96
98
  const isFunction = require('lodash/isFunction');
97
99
  const { encode } = require('@abtnode/util/lib/base32');
98
100
  const formatContext = require('@abtnode/util/lib/format-context');
101
+ const md5 = require('@abtnode/util/lib/md5');
99
102
  const { consumeServerlessNFT, consumeLauncherSession } = require('../../util/launcher');
100
103
  const util = require('../../util');
101
104
  const {
@@ -173,6 +176,7 @@ const {
173
176
  getBackupEndpoint,
174
177
  getSpaceNameByEndpoint,
175
178
  getBackupJobId,
179
+ getCheckUpdateJobId,
176
180
  } = require('../../util/spaces');
177
181
  const { validateAddSpaceGateway, validateUpdateSpaceGateway } = require('../../validators/space-gateway');
178
182
  const { sessionConfigSchema } = require('../../validators/util');
@@ -216,7 +220,16 @@ const pm2StatusMap = {
216
220
  const getBlockletEngineNameByPlatform = (blockletMeta) => getBlockletEngine(blockletMeta).interpreter;
217
221
 
218
222
  class DiskBlockletManager extends BaseBlockletManager {
219
- constructor({ dataDirs, startQueue, installQueue, backupQueue, restoreQueue, daemon = false, teamManager }) {
223
+ constructor({
224
+ dataDirs,
225
+ startQueue,
226
+ installQueue,
227
+ backupQueue,
228
+ restoreQueue,
229
+ checkUpdateQueue,
230
+ daemon = false,
231
+ teamManager,
232
+ }) {
220
233
  super();
221
234
 
222
235
  this.dataDirs = dataDirs;
@@ -228,6 +241,7 @@ class DiskBlockletManager extends BaseBlockletManager {
228
241
  */
229
242
  this.backupQueue = backupQueue;
230
243
  this.restoreQueue = restoreQueue;
244
+ this.checkUpdateQueue = checkUpdateQueue;
231
245
  this.teamManager = teamManager;
232
246
 
233
247
  if (isFunction(this.backupQueue.on)) {
@@ -271,33 +285,54 @@ class DiskBlockletManager extends BaseBlockletManager {
271
285
  blockletPm2Events.on('online', (data) => this._syncPm2Status('online', data.blockletDid, data.componentDid));
272
286
  blockletPm2Events.on('stop', (data) => this._syncPm2Status('stop', data.blockletDid, data.componentDid));
273
287
  }
288
+
289
+ if (isFunction(this.checkUpdateQueue?.on)) {
290
+ /**
291
+ *
292
+ * @param {{
293
+ * id: string,
294
+ * job: { did: string }
295
+ * }} param0
296
+ */
297
+ const handleCheckUpdateComplete = async ({ id: jobId, job }) => {
298
+ await this.checkUpdateQueue.delete(jobId);
299
+ const { did } = job;
300
+ const checkUpdate = await this.getAutoCheckUpdate({ did });
301
+ if (checkUpdate?.enabled) {
302
+ this.checkUpdateQueue.push(job, jobId, true, CHECK_UPDATE.JOB.INTERVAL);
303
+ }
304
+ };
305
+ this.checkUpdateQueue.on('finished', handleCheckUpdateComplete).on('failed', handleCheckUpdateComplete);
306
+ }
274
307
  }
275
308
 
276
309
  async initialize() {
277
310
  await this.ensureAutoBackupJobs();
311
+ await this.ensureAutoCheckUpdateJobs();
278
312
  }
279
313
 
280
- async ensureAutoBackupJobs() {
281
- const blocklets = await states.blockletExtras.find({
282
- 'settings.autoBackup.enabled': true,
283
- });
314
+ async ensureJobs({ queue, getJobId, find, entity, action, interval, restoreCancelled }) {
315
+ const blocklets = await states.blockletExtras.find(find);
284
316
 
285
317
  const info = await states.node.read();
286
318
 
287
319
  await Promise.all(
288
320
  blocklets.map(async (x) => {
289
321
  const { did } = x;
290
- const jobId = getBackupJobId(did);
291
- const job = await this.backupQueue.get(jobId);
322
+ const jobId = getJobId(did);
323
+ const job = await queue.get(jobId);
324
+ if (restoreCancelled) {
325
+ queue.restoreCancelled(jobId);
326
+ }
292
327
 
293
328
  if (job) {
294
329
  return;
295
330
  }
296
331
 
297
- this.backupQueue.push(
332
+ queue.push(
298
333
  {
299
- entity: 'blocklet',
300
- action: 'backupToSpaces',
334
+ entity,
335
+ action,
301
336
  did,
302
337
  context: formatContext({
303
338
  user: { did: info.did },
@@ -305,12 +340,40 @@ class DiskBlockletManager extends BaseBlockletManager {
305
340
  },
306
341
  jobId,
307
342
  true,
308
- BACKUPS.JOB.INTERVAL
343
+ interval
309
344
  );
310
345
  })
311
346
  );
312
347
  }
313
348
 
349
+ async ensureAutoBackupJobs() {
350
+ await this.ensureJobs({
351
+ queue: this.backupQueue,
352
+ getJobId: getBackupJobId,
353
+ find: {
354
+ 'settings.autoBackup.enabled': true,
355
+ },
356
+ entity: 'blocklet',
357
+ action: 'backupToSpaces',
358
+ interval: BACKUPS.JOB.INTERVAL,
359
+ restoreCancelled: false,
360
+ });
361
+ }
362
+
363
+ async ensureAutoCheckUpdateJobs() {
364
+ await this.ensureJobs({
365
+ queue: this.checkUpdateQueue,
366
+ getJobId: getCheckUpdateJobId,
367
+ find: {
368
+ 'settings.autoCheckUpdate.enabled': true,
369
+ },
370
+ entity: 'blocklet',
371
+ action: 'autoCheckUpdate',
372
+ interval: CHECK_UPDATE.JOB.INTERVAL,
373
+ restoreCancelled: true,
374
+ });
375
+ }
376
+
314
377
  // ============================================================================================
315
378
  // Public API for Installing/Upgrading Application or Components
316
379
  // ============================================================================================
@@ -1024,6 +1087,7 @@ class DiskBlockletManager extends BaseBlockletManager {
1024
1087
  entityId: newBlocklet.meta.did,
1025
1088
  severity: 'success',
1026
1089
  action: `/blocklets/${newBlocklet.meta.did}/components`,
1090
+ blockletDashboardAction: '/.well-known/service/admin/components',
1027
1091
  });
1028
1092
 
1029
1093
  this.emit(BlockletEvents.componentRemoved, {
@@ -1792,6 +1856,10 @@ class DiskBlockletManager extends BaseBlockletManager {
1792
1856
  if (job.action === 'restoreFromSpaces') {
1793
1857
  await this._onRestoreFromSpaces(job);
1794
1858
  }
1859
+
1860
+ if (job.action === 'autoCheckUpdate') {
1861
+ await this._onCheckForComponentUpdate(job);
1862
+ }
1795
1863
  }
1796
1864
  }
1797
1865
 
@@ -1985,10 +2053,52 @@ class DiskBlockletManager extends BaseBlockletManager {
1985
2053
  */
1986
2054
  async getAutoBackup({ did }) {
1987
2055
  const autoBackup = await states.blockletExtras.getSettings(did, 'autoBackup', { enabled: false });
1988
-
1989
2056
  return autoBackup;
1990
2057
  }
1991
2058
 
2059
+ /**
2060
+ * @description
2061
+ * @param {{
2062
+ * did: string,
2063
+ * autoCheckUpdate: import('@abtnode/client').AutoCheckUpdate
2064
+ * }} { did, autoCheckUpdate }
2065
+ * @param {{}} context
2066
+ * @memberof DiskBlockletManager
2067
+ */
2068
+ async updateAutoCheckUpdate({ did, autoCheckUpdate }, context) {
2069
+ /** @type {import('@abtnode/client').AutoCheckUpdate} */
2070
+ const value = { ...autoCheckUpdate };
2071
+ await states.blockletExtras.setSettings(did, { autoCheckUpdate: value });
2072
+ const jobId = getCheckUpdateJobId(did);
2073
+ await this.checkUpdateQueue.delete(jobId);
2074
+
2075
+ logger.info('updateAutoCheckUpdate.$value', value);
2076
+
2077
+ if (value.enabled) {
2078
+ this.checkUpdateQueue.push(
2079
+ {
2080
+ entity: 'blocklet',
2081
+ action: 'autoCheckUpdate',
2082
+ did,
2083
+ context,
2084
+ },
2085
+ jobId,
2086
+ true,
2087
+ CHECK_UPDATE.JOB.DAY_INTERVAL
2088
+ );
2089
+ }
2090
+ }
2091
+
2092
+ /**
2093
+ * @description
2094
+ * @param {import('@abtnode/client').RequestGetAutoCheckUpdateInput} { did }
2095
+ * @return {Promise<import('@abtnode/client').AutoCheckUpdate>}
2096
+ * @memberof DiskBlockletManager
2097
+ */
2098
+ getAutoCheckUpdate({ did }) {
2099
+ return states.blockletExtras.getSettings(did, 'autoCheckUpdate', { enabled: false });
2100
+ }
2101
+
1992
2102
  /**
1993
2103
  * @description
1994
2104
  * @param {{ did: string }} { did }
@@ -2563,6 +2673,176 @@ class DiskBlockletManager extends BaseBlockletManager {
2563
2673
  }
2564
2674
  }
2565
2675
 
2676
+ async _onCheckForComponentUpdate({ did }) {
2677
+ const list = await UpgradeComponents.check({ did, states });
2678
+ if (!list || !list.updateList?.length) {
2679
+ return;
2680
+ }
2681
+ const updateList = list.updateList.map((item) => {
2682
+ return { title: item.meta.title, description: item.meta.description, version: item.meta.version };
2683
+ });
2684
+
2685
+ const checkUpdateMd5 = md5(JSON.stringify(updateList));
2686
+ const oldMd5 = await states.blockletExtras.getSettings(did, 'checkUpdateMd5', '');
2687
+ // notification has been sent, no more
2688
+ if (checkUpdateMd5 === oldMd5) {
2689
+ return;
2690
+ }
2691
+ const blocklet = await this.getBlocklet(did);
2692
+ const firstComponent = updateList[0];
2693
+ const blockletTitle = getDisplayName(blocklet);
2694
+
2695
+ const description = {
2696
+ en: `${blockletTitle} has components with a new version: ${firstComponent.title} ${firstComponent.version} ${
2697
+ updateList.length > 1 ? 'and more...' : '.'
2698
+ }`,
2699
+ zh: `${blockletTitle} 的组件有新版本: ${firstComponent.title} ${updateList.length > 1 ? '等等...' : '.'}`,
2700
+ };
2701
+ const title = {
2702
+ en: `${blockletTitle} has components with a new version`,
2703
+ zh: `${blockletTitle} 的组件有新版本`,
2704
+ };
2705
+ const body = {
2706
+ en: `${blockletTitle} has new versions of the following components:`,
2707
+ zh: `${blockletTitle} 以下组件有新版:`,
2708
+ };
2709
+ const button = {
2710
+ en: 'Upgrade to new version',
2711
+ zh: '升级到新版本',
2712
+ };
2713
+
2714
+ const action = `/blocklets/${did}/components?checkUpdate=1`;
2715
+ const blockletDashboardAction = '/.well-known/service/admin/components?checkUpdate=1';
2716
+ const users = await this.teamManager.getOwnerAndAdminUsers(did, 1);
2717
+ const nodeInfo = await states.node.read();
2718
+ const { wallet } = getBlockletInfo(blocklet, nodeInfo.sk);
2719
+
2720
+ const attachments = [
2721
+ {
2722
+ type: 'section',
2723
+ fields: updateList
2724
+ .slice(0, 3)
2725
+ .map((item) => {
2726
+ return [
2727
+ {
2728
+ type: 'text',
2729
+ data: { type: 'plain', color: '#9397A1', text: item.title },
2730
+ },
2731
+ {
2732
+ type: 'text',
2733
+ data: { type: 'plain', text: item.version },
2734
+ },
2735
+ ];
2736
+ })
2737
+ .flat(),
2738
+ },
2739
+ ];
2740
+
2741
+ try {
2742
+ await this._sendAllMessageToUser({
2743
+ did,
2744
+ blocklet,
2745
+ title,
2746
+ body,
2747
+ button,
2748
+ attachments,
2749
+ description,
2750
+ action,
2751
+ blockletDashboardAction,
2752
+ sender: {
2753
+ appDid: wallet.address,
2754
+ appSk: wallet.secretKey,
2755
+ },
2756
+ users,
2757
+ });
2758
+ await states.blockletExtras.setSettings(did, { checkUpdateMd5 });
2759
+ } catch (err) {
2760
+ logger.error('send checked update email failed', { error: err });
2761
+ }
2762
+ }
2763
+
2764
+ /**
2765
+ * @description send notification to multiple users, and send different notifications according to the user's own language
2766
+ * @param {{
2767
+ * did: string,
2768
+ * blocklet: import('@abtnode/client').BlockletState,
2769
+ * title: {zh: string, en: string},
2770
+ * body: {zh: string, en: string},
2771
+ * button: {zh: string, en: string},
2772
+ * description: {zh: string, en: string},
2773
+ * attachments: Array<import('@abtnode/client').NotificationAttachment>,
2774
+ * action: string,
2775
+ * blockletDashboardAction: string,
2776
+ * sender: { appDid: string, appSk: string },
2777
+ * users: Array<{ did: string, locale: string }>
2778
+ * */
2779
+ async _sendAllMessageToUser({
2780
+ did,
2781
+ blocklet,
2782
+ title,
2783
+ body,
2784
+ button,
2785
+ description,
2786
+ attachments,
2787
+ action,
2788
+ blockletDashboardAction,
2789
+ sender,
2790
+ users,
2791
+ }) {
2792
+ this._createNotification(did, {
2793
+ title: '',
2794
+ description: description.en,
2795
+ entityType: 'blocklet',
2796
+ entityId: did,
2797
+ severity: 'success',
2798
+ action,
2799
+ blockletDashboardAction,
2800
+ });
2801
+
2802
+ const zhUsers = users.filter((x) => x.locale === 'zh');
2803
+ const enUsers = users.filter((x) => x.locale !== 'zh');
2804
+ const url = `http://${getDidDomainForBlocklet({
2805
+ did: blocklet.appPid,
2806
+ })}`;
2807
+ const link = `${url}${blockletDashboardAction}`;
2808
+
2809
+ // send in batches by language
2810
+ if (zhUsers.length) {
2811
+ await sendToUser(
2812
+ zhUsers.map((x) => x.did),
2813
+ {
2814
+ title: title.zh,
2815
+ body: body.zh,
2816
+ attachments,
2817
+ actions: [
2818
+ {
2819
+ name: button.zh,
2820
+ link,
2821
+ },
2822
+ ],
2823
+ },
2824
+ sender
2825
+ );
2826
+ }
2827
+ if (enUsers.length) {
2828
+ await sendToUser(
2829
+ enUsers.map((x) => x.did),
2830
+ {
2831
+ title: title.en,
2832
+ body: body.en,
2833
+ attachments,
2834
+ actions: [
2835
+ {
2836
+ name: button.en,
2837
+ link,
2838
+ },
2839
+ ],
2840
+ },
2841
+ sender
2842
+ );
2843
+ }
2844
+ }
2845
+
2566
2846
  async getBlockletEnvironments(did) {
2567
2847
  const blockletWithEnv = await this.getBlocklet(did);
2568
2848
  const nodeInfo = await states.node.read();
@@ -3427,10 +3707,10 @@ class DiskBlockletManager extends BaseBlockletManager {
3427
3707
  if (!isExternal) {
3428
3708
  await states.notification.create({ ...notification, blockletUrl });
3429
3709
  }
3430
-
3431
3710
  await this.teamManager.createNotification({
3432
3711
  teamDid: did,
3433
3712
  ...notification,
3713
+ action: notification.blockletDashboardAction || notification.action,
3434
3714
  blockletUrl,
3435
3715
  });
3436
3716
  } catch (error) {
package/lib/index.js CHANGED
@@ -162,6 +162,27 @@ function ABTNode(options) {
162
162
  },
163
163
  });
164
164
 
165
+ const checkUpdateQueue = createQueue({
166
+ daemon: options.daemon,
167
+ model: states.job,
168
+ name: 'check_update_queue',
169
+ onJob: async (job) => {
170
+ // eslint-disable-next-line no-use-before-define
171
+ if (typeof blockletManager.onJob === 'function') {
172
+ // eslint-disable-next-line no-use-before-define
173
+ await blockletManager.onJob(job);
174
+ }
175
+ },
176
+ options: {
177
+ concurrency,
178
+ maxRetries: 0,
179
+ retryDelay: 10000, // retry after 10 seconds
180
+ maxTimeout: 60 * 1000 * 60, // throw timeout error after 60 minutes
181
+ id: (job) => (job ? md5(`${job.entity}-${job.action}-${job.id}`) : ''),
182
+ enableScheduledJob: true,
183
+ },
184
+ });
185
+
165
186
  const certManager = new Cert({
166
187
  maintainerEmail: DEFAULT_CERTIFICATE_EMAIL,
167
188
  dataDir: dataDirs.certManagerModule,
@@ -188,6 +209,7 @@ function ABTNode(options) {
188
209
  installQueue,
189
210
  backupQueue,
190
211
  restoreQueue,
212
+ checkUpdateQueue,
191
213
  daemon: options.daemon,
192
214
  teamManager,
193
215
  });
@@ -330,6 +352,10 @@ function ABTNode(options) {
330
352
  // blocklet backup record
331
353
  getBlockletBackups: blockletManager.getBlockletBackups.bind(blockletManager),
332
354
 
355
+ // check update
356
+ updateAutoCheckUpdate: blockletManager.updateAutoCheckUpdate.bind(blockletManager),
357
+ getAutoCheckUpdate: blockletManager.getAutoCheckUpdate.bind(blockletManager),
358
+
333
359
  // Store
334
360
  getBlockletMeta: StoreUtil.getBlockletMeta,
335
361
  getStoreMeta: StoreUtil.getStoreMeta,
@@ -235,7 +235,6 @@ class User extends ExtendBase {
235
235
  if (isNullOrUndefined(approved) === false) {
236
236
  condition.approved = !!approved;
237
237
  }
238
-
239
238
  return this.find({ where: condition, include: includeTags ? [this.getTagInclude()] : [] });
240
239
  }
241
240
 
@@ -7,6 +7,7 @@ const { EventEmitter } = require('events');
7
7
  const upperFirst = require('lodash/upperFirst');
8
8
  const get = require('lodash/get');
9
9
  const pick = require('lodash/pick');
10
+ const { Op } = require('sequelize');
10
11
 
11
12
  const { createRBAC, MemoryStorage, SequelizeStorage } = require('@abtnode/rbac');
12
13
  const logger = require('@abtnode/logger')('@abtnode/core:team:manager');
@@ -453,6 +454,23 @@ class TeamManager extends EventEmitter {
453
454
  getPid(did) {
454
455
  return this.isNodeTeam(did) ? did : this.states.blocklet.getBlockletMetaDid(did);
455
456
  }
457
+
458
+ async getOwnerAndAdminUsers(did, approved) {
459
+ const passportState = await this.getPassportState(did);
460
+
461
+ const passorts = await passportState.find({
462
+ where: { [Op.or]: [{ role: 'owner' }, { role: 'admin' }] },
463
+ attributes: ['userDid'],
464
+ });
465
+ const userDids = passorts.map((d) => d.userDid);
466
+
467
+ const userState = await this.getUserState(did);
468
+ const users = await userState.find({
469
+ where: { did: userDids, approved },
470
+ attributes: ['did', 'locale'],
471
+ });
472
+ return users;
473
+ }
456
474
  }
457
475
 
458
476
  module.exports = TeamManager;
package/lib/util/log.js CHANGED
@@ -87,15 +87,21 @@ class StreamLog {
87
87
  }
88
88
 
89
89
  const createFile = (files) => {
90
- Object.values(files).forEach((file) => {
91
- if (!fs.existsSync(file)) {
90
+ files.forEach((file) => {
91
+ const targetFile = typeof file === 'string' ? file : file.target;
92
+
93
+ if (!fs.existsSync(targetFile)) {
92
94
  try {
93
- const dir = path.dirname(file);
95
+ const dir = path.dirname(targetFile);
94
96
  fs.mkdirSync(dir, { recursive: true });
95
97
  } catch (err) {
96
98
  // Do nothing
97
99
  }
98
- fs.writeFileSync(file, '', 'utf8');
100
+ fs.writeFileSync(targetFile, '', 'utf8');
101
+ }
102
+
103
+ if (file.symbol && !fs.existsSync(file.symbol)) {
104
+ fs.symlinkSync(targetFile, file.symbol);
99
105
  }
100
106
  });
101
107
  };
@@ -112,25 +118,28 @@ const getLogFiles = async ({ name, node }) => {
112
118
  if (name === 'abtnode') {
113
119
  const logDir = path.join(node.dataDirs.logs, '_abtnode');
114
120
 
115
- const info = path.join(logDir, `daemon-${date}.log`);
116
- const error = path.join(logDir, `daemon-error-${date}.log`);
121
+ const infoSymbol = path.join(logDir, 'daemon.log');
122
+ const infoTarget = path.join(logDir, `daemon-${date}.log`);
123
+ const errorSymbol = path.join(logDir, 'daemon-error.log');
124
+ const errorTarget = path.join(logDir, `daemon-error-${date}.log`);
125
+
117
126
  const access = path.join(logDir, 'access.log');
118
127
  const stdout = path.join(logDir, 'daemon.stdout.log');
119
128
  const stderr = path.join(logDir, 'daemon.stderr.log');
120
129
  const pm2 = path.join(process.env.PM2_HOME, 'pm2.log');
121
130
 
122
- createFile({
123
- info,
124
- error,
131
+ createFile([
132
+ { symbol: infoSymbol, target: infoTarget },
133
+ { symbol: errorSymbol, target: errorTarget },
125
134
  access,
126
135
  stdout,
127
136
  stderr,
128
137
  pm2,
129
- });
138
+ ]);
130
139
 
131
140
  return {
132
- info,
133
- error,
141
+ info: infoSymbol,
142
+ error: errorSymbol,
134
143
  access,
135
144
  stdout,
136
145
  stderr,
@@ -140,13 +149,22 @@ const getLogFiles = async ({ name, node }) => {
140
149
 
141
150
  if (name === 'blocklet-services') {
142
151
  const logDir = path.join(node.dataDirs.logs, '_abtnode');
143
- const info = path.join(logDir, 'service.log');
144
- const error = path.join(logDir, 'service-error.log');
152
+ const infoSymbol = path.join(logDir, 'service.log');
153
+ const infoTarget = path.join(logDir, `service-${date}.log`);
154
+ const errorSymbol = path.join(logDir, 'service-error.log');
155
+ const errorTarget = path.join(logDir, `service-error-${date}.log`);
145
156
  const access = path.join(logDir, 'service.access.log');
146
157
  const stdout = path.join(logDir, 'service.stdout.log');
147
158
  const stderr = path.join(logDir, 'service.stderr.log');
148
- createFile({ error, info, access, stdout, stderr });
149
- return { error, info, access, stdout, stderr };
159
+ createFile([
160
+ { info: infoSymbol, target: infoTarget },
161
+ { symbol: errorSymbol, target: errorTarget },
162
+ access,
163
+ stdout,
164
+ stderr,
165
+ ]);
166
+
167
+ return { info: infoSymbol, error: errorSymbol, access, stdout, stderr };
150
168
  }
151
169
 
152
170
  if (name.indexOf('blocklet-') === 0) {
@@ -164,23 +182,25 @@ const getLogFiles = async ({ name, node }) => {
164
182
  }
165
183
 
166
184
  const dir = path.join(node.dataDirs.logs, blockletName);
167
- const info = path.join(dir, `info-${date}.log`);
168
- const error = path.join(dir, `info-error-${date}.log`);
185
+ const infoSymbol = path.join(dir, 'info.log');
186
+ const infoTarget = path.join(dir, `info-${date}.log`);
187
+ const errorSymbol = path.join(dir, 'info-error.log');
188
+ const errorTarget = path.join(dir, `info-error-${date}.log`);
169
189
  const access = path.join(dir, 'access.log');
170
190
  const stdout = path.join(dir, 'output.log');
171
191
  const stderr = path.join(dir, 'error.log');
172
192
 
173
- createFile({
174
- info,
175
- error,
193
+ createFile([
194
+ { symbol: infoSymbol, target: infoTarget },
195
+ { symbol: errorSymbol, target: errorTarget },
176
196
  access,
177
197
  stdout,
178
198
  stderr,
179
- });
199
+ ]);
180
200
 
181
201
  return {
182
- info,
183
- error,
202
+ info: infoSymbol,
203
+ error: errorSymbol,
184
204
  access,
185
205
  stdout,
186
206
  stderr,
@@ -343,6 +363,7 @@ const createStreamLogManager = ({ onLog, onGetLogFiles }) => {
343
363
  // update files
344
364
  // push recent 100 log
345
365
  const { recent = 100, ...logFiles } = await onGetLogFiles(name);
366
+
346
367
  const changed = await log.setFiles(logFiles);
347
368
  logger.info('log stream: added', { name, logFiles });
348
369
  log.getRecent(recent, (error, level, data) => {
@@ -81,10 +81,20 @@ function getBackupJobId(did) {
81
81
  return `${did}.backupToSpaces`;
82
82
  }
83
83
 
84
+ /**
85
+ * @description
86
+ * @param {string} did
87
+ * @return {string}
88
+ */
89
+ function getCheckUpdateJobId(did) {
90
+ return `${did}.checkUpdate`;
91
+ }
92
+
84
93
  module.exports = {
85
94
  getBackupEndpoint,
86
95
  getBackupFilesUrlFromEndpoint,
87
96
  getDIDSpacesUrlFromEndpoint,
88
97
  getSpaceNameByEndpoint,
89
98
  getBackupJobId,
99
+ getCheckUpdateJobId,
90
100
  };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.16.21-beta-445a8baa",
6
+ "version": "1.16.21-beta-2e75c75c",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,39 +19,39 @@
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
20
  "license": "Apache-2.0",
21
21
  "dependencies": {
22
- "@abtnode/analytics": "1.16.21-beta-445a8baa",
23
- "@abtnode/auth": "1.16.21-beta-445a8baa",
24
- "@abtnode/certificate-manager": "1.16.21-beta-445a8baa",
25
- "@abtnode/constant": "1.16.21-beta-445a8baa",
26
- "@abtnode/cron": "1.16.21-beta-445a8baa",
27
- "@abtnode/logger": "1.16.21-beta-445a8baa",
28
- "@abtnode/models": "1.16.21-beta-445a8baa",
29
- "@abtnode/queue": "1.16.21-beta-445a8baa",
30
- "@abtnode/rbac": "1.16.21-beta-445a8baa",
31
- "@abtnode/router-provider": "1.16.21-beta-445a8baa",
32
- "@abtnode/static-server": "1.16.21-beta-445a8baa",
33
- "@abtnode/timemachine": "1.16.21-beta-445a8baa",
34
- "@abtnode/util": "1.16.21-beta-445a8baa",
35
- "@arcblock/did": "1.18.107",
36
- "@arcblock/did-auth": "1.18.107",
37
- "@arcblock/did-ext": "^1.18.107",
22
+ "@abtnode/analytics": "1.16.21-beta-2e75c75c",
23
+ "@abtnode/auth": "1.16.21-beta-2e75c75c",
24
+ "@abtnode/certificate-manager": "1.16.21-beta-2e75c75c",
25
+ "@abtnode/constant": "1.16.21-beta-2e75c75c",
26
+ "@abtnode/cron": "1.16.21-beta-2e75c75c",
27
+ "@abtnode/logger": "1.16.21-beta-2e75c75c",
28
+ "@abtnode/models": "1.16.21-beta-2e75c75c",
29
+ "@abtnode/queue": "1.16.21-beta-2e75c75c",
30
+ "@abtnode/rbac": "1.16.21-beta-2e75c75c",
31
+ "@abtnode/router-provider": "1.16.21-beta-2e75c75c",
32
+ "@abtnode/static-server": "1.16.21-beta-2e75c75c",
33
+ "@abtnode/timemachine": "1.16.21-beta-2e75c75c",
34
+ "@abtnode/util": "1.16.21-beta-2e75c75c",
35
+ "@arcblock/did": "1.18.108",
36
+ "@arcblock/did-auth": "1.18.108",
37
+ "@arcblock/did-ext": "^1.18.108",
38
38
  "@arcblock/did-motif": "^1.1.13",
39
- "@arcblock/did-util": "1.18.107",
40
- "@arcblock/event-hub": "1.18.107",
41
- "@arcblock/jwt": "^1.18.107",
39
+ "@arcblock/did-util": "1.18.108",
40
+ "@arcblock/event-hub": "1.18.108",
41
+ "@arcblock/jwt": "^1.18.108",
42
42
  "@arcblock/pm2-events": "^0.0.5",
43
- "@arcblock/validator": "^1.18.107",
44
- "@arcblock/vc": "1.18.107",
45
- "@blocklet/constant": "1.16.21-beta-445a8baa",
46
- "@blocklet/env": "1.16.21-beta-445a8baa",
47
- "@blocklet/meta": "1.16.21-beta-445a8baa",
48
- "@blocklet/resolver": "1.16.21-beta-445a8baa",
49
- "@blocklet/sdk": "1.16.21-beta-445a8baa",
50
- "@did-space/client": "^0.3.45",
43
+ "@arcblock/validator": "^1.18.108",
44
+ "@arcblock/vc": "1.18.108",
45
+ "@blocklet/constant": "1.16.21-beta-2e75c75c",
46
+ "@blocklet/env": "1.16.21-beta-2e75c75c",
47
+ "@blocklet/meta": "1.16.21-beta-2e75c75c",
48
+ "@blocklet/resolver": "1.16.21-beta-2e75c75c",
49
+ "@blocklet/sdk": "1.16.21-beta-2e75c75c",
50
+ "@did-space/client": "^0.3.49",
51
51
  "@fidm/x509": "^1.2.1",
52
- "@ocap/mcrypto": "1.18.107",
53
- "@ocap/util": "1.18.107",
54
- "@ocap/wallet": "1.18.107",
52
+ "@ocap/mcrypto": "1.18.108",
53
+ "@ocap/util": "1.18.108",
54
+ "@ocap/wallet": "1.18.108",
55
55
  "@slack/webhook": "^5.0.4",
56
56
  "archiver": "^5.3.1",
57
57
  "axios": "^0.27.2",
@@ -102,5 +102,5 @@
102
102
  "jest": "^27.5.1",
103
103
  "unzipper": "^0.10.11"
104
104
  },
105
- "gitHead": "7a7ff8be7f424775c3bde0eead773d8e6177fa1a"
105
+ "gitHead": "1fca0b58e1ed4f7a9bb268829a7c84290a01c89d"
106
106
  }