@abtnode/core 1.16.8-beta-0c0c5eb2 → 1.16.8-beta-81db8efa

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/node.js CHANGED
@@ -2,11 +2,13 @@
2
2
  /* eslint-disable no-underscore-dangle */
3
3
  const assert = require('assert');
4
4
  const os = require('os');
5
+ const LRU = require('lru-cache');
5
6
  const isDocker = require('@abtnode/util/lib/is-docker');
6
7
  const isGitpod = require('@abtnode/util/lib/is-gitpod');
7
8
  const getFolderSize = require('@abtnode/util/lib/get-folder-size');
8
9
  const canPackageReadWrite = require('@abtnode/util/lib/can-pkg-rw');
9
10
  const { toDelegateAddress } = require('@arcblock/did-util');
11
+ const { SERVER_CACHE_TTL } = require('@abtnode/constant');
10
12
 
11
13
  const logger = require('@abtnode/logger')('@abtnode/core:api:node');
12
14
 
@@ -36,6 +38,11 @@ class NodeAPI {
36
38
  historyLength: MONITOR_HISTORY_LENGTH,
37
39
  });
38
40
 
41
+ this.cache = new LRU({
42
+ max: 1,
43
+ maxAge: SERVER_CACHE_TTL,
44
+ });
45
+
39
46
  this.state = state;
40
47
  }
41
48
 
@@ -59,15 +66,26 @@ class NodeAPI {
59
66
  }
60
67
  }
61
68
 
62
- async getInfo() {
63
- const info = await this.state.read();
64
- const env = await this.state.getEnvironments();
65
- info.environments = Object.keys(env).map((x) => ({ key: x, value: env[x] }));
69
+ async getInfo({ useCache } = {}) {
70
+ let info;
71
+ if (useCache && this.cache.has('info')) {
72
+ info = this.cache.get('info');
73
+ } else {
74
+ info = await this.state.read();
75
+ const env = await this.state.getEnvironments();
76
+ info.environments = Object.keys(env).map((x) => ({ key: x, value: env[x] }));
77
+ this.cache.set('info', info);
78
+ }
79
+
66
80
  info.uptime = process.uptime() * 1000;
67
81
 
68
82
  return info;
69
83
  }
70
84
 
85
+ deleteCache() {
86
+ this.cache.del('del');
87
+ }
88
+
71
89
  async getDiskInfo() {
72
90
  let diskInfo = { app: 0, cache: 0, log: 0, data: 0, blocklets: 0 };
73
91
  try {
@@ -3,12 +3,12 @@
3
3
  const fs = require('fs-extra');
4
4
  const path = require('path');
5
5
  const flat = require('flat');
6
+ const LRU = require('lru-cache');
6
7
  const get = require('lodash/get');
7
8
  const omit = require('lodash/omit');
8
9
  const merge = require('lodash/merge');
9
10
  const pick = require('lodash/pick');
10
11
  const isEmpty = require('lodash/isEmpty');
11
- const cloneDeep = require('lodash/cloneDeep');
12
12
  const { isNFTExpired, getNftExpirationDate } = require('@abtnode/util/lib/nft');
13
13
  const didDocument = require('@abtnode/util/lib/did-document');
14
14
  const { sign } = require('@arcblock/jwt');
@@ -22,6 +22,7 @@ const {
22
22
  BLOCKLET_INSTALL_TYPE,
23
23
  NODE_MODES,
24
24
  APP_STRUCT_VERSION,
25
+ BLOCKLET_CACHE_TTL,
25
26
  } = require('@abtnode/constant');
26
27
 
27
28
  const getBlockletEngine = require('@blocklet/meta/lib/engine');
@@ -41,6 +42,7 @@ const {
41
42
  const getComponentProcessId = require('@blocklet/meta/lib/get-component-process-id');
42
43
  const { update: updateMetaFile } = require('@blocklet/meta/lib/file');
43
44
  const { titleSchema, updateMountPointSchema, environmentNameSchema } = require('@blocklet/meta/lib/schema');
45
+ const { emailConfigSchema } = require('@blocklet/sdk/lib/validators/email');
44
46
  const Lock = require('@abtnode/util/lib/lock');
45
47
 
46
48
  const {
@@ -82,7 +84,6 @@ const {
82
84
  checkBlockletProcessHealthy,
83
85
  validateBlocklet,
84
86
  validateBlockletChainInfo,
85
- statusMap,
86
87
  pruneBlockletBundle,
87
88
  getDiskInfo,
88
89
  getRuntimeEnvironments,
@@ -128,7 +129,7 @@ const UpgradeComponents = require('./helper/upgrade-components');
128
129
  const BlockletDownloader = require('../downloader/blocklet-downloader');
129
130
  const RollbackCache = require('./helper/rollback-cache');
130
131
  const { migrateApplicationToStructV2 } = require('./helper/migrate-application-to-struct-v2');
131
- const { getBackupFilesUrlFromEndpoint, getDIDSpaceBackupEndpoint } = require('../../util/spaces');
132
+ const { getBackupFilesUrlFromEndpoint, getBackupEndpoint } = require('../../util/spaces');
132
133
  const { validateAddSpaceGateway, validateUpdateSpaceGateway } = require('../../validators/space-gateway');
133
134
 
134
135
  const { formatEnvironments, shouldUpdateBlockletStatus, getBlockletMeta, validateOwner } = util;
@@ -221,7 +222,10 @@ class BlockletManager extends BaseBlockletManager {
221
222
  this.teamManager = teamManager;
222
223
 
223
224
  // cached installed blocklets for performance
224
- this.cachedBlocklets = null;
225
+ this.cachedBlocklets = new LRU({
226
+ max: 100,
227
+ maxAge: BLOCKLET_CACHE_TTL,
228
+ });
225
229
 
226
230
  this.runtimeMonitor = new BlockletRuntimeMonitor({ historyLength: MONITOR_HISTORY_LENGTH, states });
227
231
 
@@ -944,7 +948,7 @@ class BlockletManager extends BaseBlockletManager {
944
948
  }
945
949
 
946
950
  // Get blocklet by blockletDid or appDid
947
- async detail({ did, attachConfig = true, attachRuntimeInfo = true }, context) {
951
+ async detail({ did, attachConfig = true, attachRuntimeInfo, useCache }, context) {
948
952
  if (!did) {
949
953
  throw new Error('did should not be empty');
950
954
  }
@@ -953,49 +957,36 @@ class BlockletManager extends BaseBlockletManager {
953
957
  return states.blocklet.getBlocklet(did);
954
958
  }
955
959
 
956
- if (!attachRuntimeInfo) {
957
- try {
958
- const blocklet = await this.getBlocklet(did, { throwOnNotExist: false });
959
- return blocklet;
960
- } catch (e) {
961
- logger.error('get blocklet detail error', { error: e });
962
- return states.blocklet.getBlocklet(did);
963
- }
964
- }
965
-
966
- const nodeInfo = await states.node.read();
960
+ if (attachRuntimeInfo) {
961
+ const nodeInfo = await states.node.read();
967
962
 
968
- return this._attachRuntimeInfo({ did, nodeInfo, diskInfo: true, context });
969
- }
963
+ return this._attachRuntimeInfo({ did, nodeInfo, diskInfo: true, context });
964
+ }
970
965
 
971
- async attachBlockletListRuntimeInfo({ blocklets, useCache }, context) {
972
- const nodeInfo = await states.node.read();
973
- const updated = (
974
- await Promise.all(
975
- blocklets.map((x) => {
976
- if (isBeforeInstalled(x.status)) {
977
- return x;
978
- }
966
+ if (useCache && this.cachedBlocklets.has(did)) {
967
+ return this.cachedBlocklets.get(did);
968
+ }
979
969
 
980
- const cachedBlocklet =
981
- useCache && this.cachedBlocklets ? this.cachedBlocklets.find((y) => y.meta.did === x.meta.did) : null;
970
+ try {
971
+ const blocklet = await this.getBlocklet(did, { throwOnNotExist: false });
982
972
 
983
- return this._attachRuntimeInfo({
984
- did: x.meta.did,
985
- nodeInfo,
986
- diskInfo: false,
987
- context,
988
- cachedBlocklet,
989
- });
990
- })
991
- )
992
- ).filter(Boolean);
973
+ if (blocklet) {
974
+ if (blocklet.appDid) {
975
+ this.cachedBlocklets.set(blocklet.appDid, blocklet);
976
+ }
977
+ if (blocklet.appPid) {
978
+ this.cachedBlocklets.set(blocklet.appPid, blocklet);
979
+ }
980
+ }
993
981
 
994
- this.cachedBlocklets = cloneDeep(updated);
995
- return updated;
982
+ return blocklet;
983
+ } catch (e) {
984
+ logger.error('get blocklet detail error', { error: e });
985
+ return states.blocklet.getBlocklet(did);
986
+ }
996
987
  }
997
988
 
998
- async list({ includeRuntimeInfo = true, useCache = true, query, filter } = {}, context) {
989
+ async list({ includeRuntimeInfo = true, query, filter } = {}, context) {
999
990
  const condition = { ...flat(query || {}) };
1000
991
  if (filter === 'external-only') {
1001
992
  condition.controller = {
@@ -1012,7 +1003,7 @@ class BlockletManager extends BaseBlockletManager {
1012
1003
  const blocklets = await states.blocklet.getBlocklets(condition);
1013
1004
 
1014
1005
  if (includeRuntimeInfo) {
1015
- return this.attachBlockletListRuntimeInfo({ blocklets, useCache }, context);
1006
+ return this._attachBlockletListRuntimeInfo({ blocklets }, context);
1016
1007
  }
1017
1008
 
1018
1009
  return blocklets;
@@ -1235,6 +1226,21 @@ class BlockletManager extends BaseBlockletManager {
1235
1226
  return newState;
1236
1227
  }
1237
1228
 
1229
+ async configNotification({ did, notification = {} }) {
1230
+ const newConfig = JSON.parse(notification);
1231
+ const { error, value: validateConfig } = emailConfigSchema.validate(newConfig);
1232
+ if (error) {
1233
+ logger.error('configNotification validate error', { error });
1234
+ throw new Error(error.message);
1235
+ }
1236
+ const oldConfig = await states.blockletExtras.getSettings(did, 'notification', {});
1237
+ const mergeConfig = { ...oldConfig, ...validateConfig };
1238
+ await states.blockletExtras.setSettings(did, { notification: mergeConfig });
1239
+ const newState = await this.getBlocklet(did);
1240
+ this.emit(BlockletEvents.updated, newState);
1241
+ return newState;
1242
+ }
1243
+
1238
1244
  // TODO: this method can be removed if title is not changed anymore
1239
1245
  async updateComponentTitle({ did, rootDid: inputRootDid, title }) {
1240
1246
  await titleSchema.validateAsync(title);
@@ -1596,6 +1602,14 @@ class BlockletManager extends BaseBlockletManager {
1596
1602
  return states.backup.getBlockletBackups({ did });
1597
1603
  }
1598
1604
 
1605
+ deleteCache(did) {
1606
+ const cache = this.cachedBlocklets.get(did);
1607
+ if (cache) {
1608
+ this.cachedBlocklets.del(cache.appDid);
1609
+ this.cachedBlocklets.del(cache.appPid);
1610
+ }
1611
+ }
1612
+
1599
1613
  // ============================================================================================
1600
1614
  // Private API that are used by self of helper function
1601
1615
  // ============================================================================================
@@ -1891,11 +1905,18 @@ class BlockletManager extends BaseBlockletManager {
1891
1905
  await spacesBackup.backup();
1892
1906
 
1893
1907
  await states.backup.success(backup._id, {
1894
- targetUrl: getBackupFilesUrlFromEndpoint(getDIDSpaceBackupEndpoint(blocklet?.environments)),
1908
+ targetUrl: getBackupFilesUrlFromEndpoint(getBackupEndpoint(blocklet?.environments)),
1895
1909
  });
1896
1910
 
1897
1911
  // 备份成功了
1898
- this.emit(BlockletEvents.backupProgress, { appDid, meta: { did: appPid }, completed: true, progress: 100 });
1912
+ this.emit(BlockletEvents.backupProgress, {
1913
+ appDid,
1914
+ meta: { did: appPid },
1915
+ completed: true,
1916
+ progress: 100,
1917
+ context,
1918
+ blocklet,
1919
+ });
1899
1920
  } catch (error) {
1900
1921
  await states.backup.fail(backup._id, {
1901
1922
  message: error?.message,
@@ -1906,6 +1927,8 @@ class BlockletManager extends BaseBlockletManager {
1906
1927
  completed: true,
1907
1928
  progress: -1,
1908
1929
  message: error?.message,
1930
+ context,
1931
+ blocklet,
1909
1932
  });
1910
1933
  throw error;
1911
1934
  }
@@ -1922,59 +1945,69 @@ class BlockletManager extends BaseBlockletManager {
1922
1945
  */
1923
1946
  // eslint-disable-next-line no-unused-vars
1924
1947
  async _onRestoreFromSpaces({ input, context }) {
1925
- if (input.delay) {
1926
- await sleep(input.delay);
1927
- }
1928
-
1929
1948
  const appPid = input.appDid;
1930
1949
 
1931
- this.emit(BlockletEvents.restoreProgress, {
1932
- appDid: input.appDid,
1933
- meta: { did: appPid },
1934
- status: RESTORE_PROGRESS_STATUS.start,
1935
- });
1950
+ try {
1951
+ if (input.delay) {
1952
+ await sleep(input.delay);
1953
+ }
1936
1954
 
1937
- const userDid = context.user.did;
1955
+ this.emit(BlockletEvents.restoreProgress, {
1956
+ appDid: input.appDid,
1957
+ meta: { did: appPid },
1958
+ status: RESTORE_PROGRESS_STATUS.start,
1959
+ });
1938
1960
 
1939
- const spacesRestore = new SpacesRestore({ ...input, appPid, event: this, userDid, referrer: context.referrer });
1940
- const params = await spacesRestore.restore();
1961
+ const userDid = context.user.did;
1962
+ const spacesRestore = new SpacesRestore({ ...input, appPid, event: this, userDid, referrer: context.referrer });
1963
+ const params = await spacesRestore.restore();
1941
1964
 
1942
- const removeRestoreDir = () => {
1943
- if (fs.existsSync(spacesRestore.restoreDir)) {
1944
- fs.remove(spacesRestore.restoreDir).catch((err) => {
1945
- logger.error('failed to remove restore dir', { error: err, dir: spacesRestore.restoreDir });
1965
+ const removeRestoreDir = () => {
1966
+ if (fs.existsSync(spacesRestore.restoreDir)) {
1967
+ fs.remove(spacesRestore.restoreDir).catch((err) => {
1968
+ logger.error('failed to remove restore dir', { error: err, dir: spacesRestore.restoreDir });
1969
+ });
1970
+ }
1971
+ };
1972
+
1973
+ this.emit(BlockletEvents.restoreProgress, {
1974
+ appDid: input.appDid,
1975
+ meta: { did: appPid },
1976
+ status: RESTORE_PROGRESS_STATUS.installing,
1977
+ });
1978
+
1979
+ try {
1980
+ await installApplicationFromBackup({
1981
+ url: `file://${spacesRestore.restoreDir}`,
1982
+ moveDir: true,
1983
+ ...merge(...params),
1984
+ manager: this,
1985
+ states,
1986
+ controller: input.controller,
1987
+ context: { ...context, startImmediately: true },
1946
1988
  });
1947
- }
1948
- };
1949
1989
 
1950
- this.emit(BlockletEvents.restoreProgress, {
1951
- appDid: input.appDid,
1952
- meta: { did: appPid },
1953
- status: RESTORE_PROGRESS_STATUS.installing,
1954
- });
1990
+ removeRestoreDir();
1991
+ } catch (error) {
1992
+ removeRestoreDir();
1993
+ throw error;
1994
+ }
1955
1995
 
1956
- try {
1957
- await installApplicationFromBackup({
1958
- url: `file://${spacesRestore.restoreDir}`,
1959
- moveDir: true,
1960
- ...merge(...params),
1961
- manager: this,
1962
- states,
1963
- controller: input.controller,
1964
- context: { ...context, startImmediately: true },
1996
+ this.emit(BlockletEvents.restoreProgress, {
1997
+ appDid: input.appDid,
1998
+ meta: { did: appPid },
1999
+ status: RESTORE_PROGRESS_STATUS.completed,
1965
2000
  });
1966
-
1967
- removeRestoreDir();
1968
2001
  } catch (error) {
1969
- removeRestoreDir();
2002
+ this.emit(BlockletEvents.restoreProgress, {
2003
+ appDid: input.appDid,
2004
+ meta: { did: appPid },
2005
+ status: RESTORE_PROGRESS_STATUS.error,
2006
+ message: error.message,
2007
+ });
2008
+
1970
2009
  throw error;
1971
2010
  }
1972
-
1973
- this.emit(BlockletEvents.restoreProgress, {
1974
- appDid: input.appDid,
1975
- meta: { did: appPid },
1976
- status: RESTORE_PROGRESS_STATUS.completed,
1977
- });
1978
2011
  }
1979
2012
 
1980
2013
  async _updateBlockletEnvironment(did) {
@@ -2027,7 +2060,27 @@ class BlockletManager extends BaseBlockletManager {
2027
2060
  return states.blocklet.updateBlocklet(did, blocklet);
2028
2061
  }
2029
2062
 
2030
- async _attachRuntimeInfo({ did, nodeInfo, diskInfo = true, context, cachedBlocklet }) {
2063
+ async _attachBlockletListRuntimeInfo({ blocklets }, context) {
2064
+ const nodeInfo = await states.node.read();
2065
+ return (
2066
+ await Promise.all(
2067
+ blocklets.map((x) => {
2068
+ if (isBeforeInstalled(x.status)) {
2069
+ return x;
2070
+ }
2071
+
2072
+ return this._attachRuntimeInfo({
2073
+ did: x.meta.did,
2074
+ nodeInfo,
2075
+ diskInfo: false,
2076
+ context,
2077
+ });
2078
+ })
2079
+ )
2080
+ ).filter(Boolean);
2081
+ }
2082
+
2083
+ async _attachRuntimeInfo({ did, nodeInfo, diskInfo = true, context }) {
2031
2084
  if (!did) {
2032
2085
  throw new Error('did should not be empty');
2033
2086
  }
@@ -2039,24 +2092,6 @@ class BlockletManager extends BaseBlockletManager {
2039
2092
  return null;
2040
2093
  }
2041
2094
 
2042
- const fromCache = !!cachedBlocklet;
2043
-
2044
- // if from cached data, only use cache data of runtime info (engine, diskInfo, runtimeInfo...)
2045
- if (fromCache) {
2046
- const cached = {};
2047
- forEachBlockletSync(cachedBlocklet, (component, { id }) => {
2048
- cached[id] = component;
2049
- });
2050
-
2051
- Object.assign(blocklet, pick(cachedBlocklet, ['appRuntimeInfo', 'diskInfo']));
2052
-
2053
- forEachBlockletSync(blocklet, (component, { id }) => {
2054
- if (cached[id]) {
2055
- Object.assign(component, pick(cached[id], ['runtimeInfo']));
2056
- }
2057
- });
2058
- }
2059
-
2060
2095
  // 处理 domainAliases#value SLOT_FOR_IP_DNS_SITE
2061
2096
  if (blocklet?.site?.domainAliases?.length) {
2062
2097
  const nodeIp = await getAccessibleExternalNodeIp(nodeInfo);
@@ -2069,24 +2104,18 @@ class BlockletManager extends BaseBlockletManager {
2069
2104
  // app runtime info, app status
2070
2105
  blocklet.appRuntimeInfo = this.runtimeMonitor.getRuntimeInfo(blocklet.meta.did);
2071
2106
 
2072
- if (!fromCache) {
2073
- // app disk info, component runtime info, component status, component engine
2074
- await forEachBlocklet(blocklet, async (component, { level }) => {
2075
- component.engine = getEngine(getBlockletEngineNameByPlatform(component.meta)).describe();
2076
-
2077
- if (level === 0) {
2078
- component.diskInfo = await getDiskInfo(component, {
2079
- useFakeDiskInfo: !diskInfo,
2080
- });
2081
- }
2107
+ // app disk info, component runtime info, component engine
2108
+ await forEachBlocklet(blocklet, async (component, { level }) => {
2109
+ component.engine = getEngine(getBlockletEngineNameByPlatform(component.meta)).describe();
2082
2110
 
2083
- component.runtimeInfo = this.runtimeMonitor.getRuntimeInfo(blocklet.meta.did, component.env.id);
2111
+ if (level === 0) {
2112
+ component.diskInfo = await getDiskInfo(component, {
2113
+ useFakeDiskInfo: !diskInfo,
2114
+ });
2115
+ }
2084
2116
 
2085
- if (component.runtimeInfo?.status && shouldUpdateBlockletStatus(component.status)) {
2086
- component.status = statusMap[component.runtimeInfo.status];
2087
- }
2088
- });
2089
- }
2117
+ component.runtimeInfo = this.runtimeMonitor.getRuntimeInfo(blocklet.meta.did, component.env.id);
2118
+ });
2090
2119
 
2091
2120
  return blocklet;
2092
2121
  } catch (err) {
@@ -218,11 +218,9 @@ class SpacesBackup extends BaseBackup {
218
218
  const { locale } = this.input;
219
219
  // @FIXME: get locale @jianchao
220
220
  if (statusCode === 403) {
221
- throw new Error(
222
- `${translate(locale, 'backup.space.error.title')}: ${translate(locale, 'backup.space.error.forbidden')}`
223
- );
221
+ throw new Error(translate(locale, 'backup.space.error.forbidden'));
224
222
  }
225
- throw new Error(`${translate(locale, 'backup.space.error.title')}: ${message}`);
223
+ throw new Error(message);
226
224
  }
227
225
  }
228
226
 
@@ -135,7 +135,7 @@ class SpacesRestore extends BaseRestore {
135
135
  );
136
136
 
137
137
  if (errorCount !== 0) {
138
- throw new Error(`Sync from spaces encountered error: ${message}`);
138
+ throw new Error(message);
139
139
  }
140
140
  }
141
141
 
package/lib/event.js CHANGED
@@ -12,6 +12,7 @@ const eventHub =
12
12
  process.env.NODE_ENV === 'test' ? require('@arcblock/event-hub/single') : require('@arcblock/event-hub');
13
13
 
14
14
  const states = require('./states');
15
+ const { getBackupEndpoint, getBackupFilesUrlFromEndpoint, getDIDSpacesUrlFromEndpoint } = require('./util/spaces');
15
16
 
16
17
  const routingSnapshotPrefix = (blocklet) => (blocklet.mode === BLOCKLET_MODES.DEVELOPMENT ? '[DEV] ' : '');
17
18
 
@@ -25,6 +26,7 @@ module.exports = ({
25
26
  handleRouting,
26
27
  domainStatus,
27
28
  teamAPI,
29
+ nodeAPI,
28
30
  teamManager,
29
31
  certManager,
30
32
  routerManager,
@@ -53,12 +55,34 @@ module.exports = ({
53
55
  teamManager.deleteTeam(data?.meta?.did, { closeDatabase: false });
54
56
  }
55
57
 
58
+ // clear cache
59
+ if (
60
+ [
61
+ BlockletEvents.updated,
62
+ BlockletEvents.started,
63
+ BlockletEvents.removed,
64
+ BlockletEvents.statusChange,
65
+ BlockletEvents.installed,
66
+ ].includes(name)
67
+ ) {
68
+ const did = get(data, 'meta.did');
69
+ if (did) {
70
+ logger.info('delete blocklet cache on update', { did });
71
+ blockletManager.deleteCache(did);
72
+ }
73
+ }
74
+
56
75
  if (typeof eventHandler === 'function') {
57
76
  eventHandler({ name, data });
58
77
  }
59
78
  });
60
79
  });
61
80
 
81
+ eventHub.on(EVENTS.NODE_UPDATED, () => {
82
+ logger.info('node update');
83
+ nodeAPI.deleteCache();
84
+ });
85
+
62
86
  // Wipe sensitive data
63
87
  // Emit events to event hub
64
88
  // Emit events to node listener
@@ -259,6 +283,25 @@ module.exports = ({
259
283
  logger.error('Reload gateway failed on blocklet.connectedSpace', { error: err });
260
284
  });
261
285
  logger.info('Reload gateway after blocklet connected to space', { event: eventName, did: blocklet.appDid });
286
+ } else if (BlockletEvents.backupProgress === eventName && payload?.completed) {
287
+ try {
288
+ const backupEndpoint = getBackupEndpoint(blocklet?.environments);
289
+
290
+ await node.createAuditLog({
291
+ action: 'backupToSpaces',
292
+ args: {
293
+ did: blocklet.meta.did,
294
+ url: getDIDSpacesUrlFromEndpoint(backupEndpoint),
295
+ backupUrl: getBackupFilesUrlFromEndpoint(backupEndpoint),
296
+ success: payload?.progress === 100,
297
+ errorMessage: payload?.message,
298
+ },
299
+ context: payload?.context ?? {},
300
+ result: cloneDeep(blocklet),
301
+ });
302
+ } catch (error) {
303
+ logger.error('Failed to createAuditLog for backupToSpaces failed', { error });
304
+ }
262
305
  }
263
306
 
264
307
  if (
package/lib/index.js CHANGED
@@ -254,6 +254,7 @@ function ABTNode(options) {
254
254
  configPublicToStore: blockletManager.configPublicToStore.bind(blockletManager),
255
255
  configNavigations: blockletManager.configNavigations.bind(blockletManager),
256
256
  configOAuth: blockletManager.configOAuth.bind(blockletManager),
257
+ configNotification: blockletManager.configNotification.bind(blockletManager),
257
258
  updateWhoCanAccess: blockletManager.updateWhoCanAccess.bind(blockletManager),
258
259
  updateComponentTitle: blockletManager.updateComponentTitle.bind(blockletManager),
259
260
  updateComponentMountPoint: blockletManager.updateComponentMountPoint.bind(blockletManager),
@@ -471,6 +472,7 @@ function ABTNode(options) {
471
472
  handleRouting,
472
473
  domainStatus,
473
474
  teamAPI,
475
+ nodeAPI,
474
476
  teamManager,
475
477
  certManager,
476
478
  routerManager,
@@ -12,6 +12,14 @@ const BaseState = require('./base');
12
12
  const { parse } = require('../util/ua');
13
13
 
14
14
  const getServerInfo = (info) => `[${info.name}](${joinUrl(info.routing.adminPath, '/settings/about')})`;
15
+ /**
16
+ * @description
17
+ * @param {import('@abtnode/client').BlockletState} blocklet
18
+ * @param {{
19
+ * routing: { adminPath: string }
20
+ * }} info
21
+ * @returns {string}
22
+ */
15
23
  const getBlockletInfo = (blocklet, info) => `[${getDisplayName(blocklet)} v${blocklet.meta.version}](${joinUrl(info.routing.adminPath, '/blocklets/', blocklet.meta.did, '/overview')})`; // prettier-ignore
16
24
  const expandTeam = async (teamDid, info, node) => {
17
25
  if (!teamDid) {
@@ -81,7 +89,7 @@ const expandUser = async (teamDid, userDid, passportId, info, node) => {
81
89
  * Create log content in markdown format
82
90
  *
83
91
  * @param {string} action - GraphQL query/mutation name
84
- * @param {object} args - GraphQL arguments
92
+ * @param {Record<string, string>} args - GraphQL arguments
85
93
  * @param {object} context - request context: user, ip, user-agent, etc.
86
94
  * @param {object} result - GraphQL resolve result
87
95
  * @param {object} info - server info
@@ -116,12 +124,22 @@ const getLogContent = async (action, args, context, result, info, node) => {
116
124
  case 'deleteComponent':
117
125
  return `removed component ${args.did} from blocklet ${getBlockletInfo(result, info)}`;
118
126
  case 'configBlocklet':
119
- return `updated following config for blocklet ${getBlockletInfo(result, info)}:\n${args.configs.map(x => `- ${x.key}: ${x.value}\n`)}`; // prettier-ignore
127
+ return `updated following config for blocklet ${getBlockletInfo(result, info)}:\n${args.configs.map(x => `* ${x.key}: ${x.value}`).join('\n')}`; // prettier-ignore
120
128
  case 'upgradeBlocklet':
121
129
  if (result.resultStatus === 'failed') {
122
130
  return `upgrade blocklet failed: ${getBlockletInfo(result, info)}`;
123
131
  }
124
132
  return `upgraded blocklet ${getBlockletInfo(result, info)} to v${result.meta.version}`;
133
+ case 'backupToSpaces':
134
+ if (args?.success) {
135
+ return `Backup ${getBlockletInfo(result, info)} to ${
136
+ args.url
137
+ } successfully:\n- Backup files have been stored [here](${args.backupUrl})`;
138
+ }
139
+ return `Backup ${getBlockletInfo(result, info)} to ${
140
+ args.url
141
+ } failed:\n- The reason for the error is: <span style='color:red'>${args.errorMessage}</span>`;
142
+
125
143
  case 'upgradeComponents':
126
144
  return `upgraded components for blocklet ${getBlockletInfo(result, info)}`;
127
145
  case 'configPublicToStore':
@@ -136,6 +154,8 @@ const getLogContent = async (action, args, context, result, info, node) => {
136
154
  )}`;
137
155
  case 'configOAuth':
138
156
  return `updated following OAuth for blocklet ${getBlockletInfo(result, info)}:\n${args.oauth}`;
157
+ case 'configNotification':
158
+ return `updated following notification setting for blocklet ${getBlockletInfo(result, info)}`;
139
159
  case 'updateComponentTitle':
140
160
  return `update component title to **${args.title}** for blocklet ${getBlockletInfo(result, info)}`;
141
161
  case 'updateComponentMountPoint':
@@ -295,8 +315,10 @@ const getLogCategory = (action) => {
295
315
  case 'configPublicToStore':
296
316
  case 'configNavigations':
297
317
  case 'configOAuth':
318
+ case 'configNotification':
298
319
  case 'updateComponentTitle':
299
320
  case 'updateComponentMountPoint':
321
+ case 'backupToSpaces':
300
322
  return 'blocklet';
301
323
 
302
324
  // store,此处应该返回 server
@@ -1,4 +1,6 @@
1
1
  const { BLOCKLET_CONFIGURABLE_KEY } = require('@blocklet/constant');
2
+ const isUrl = require('is-url');
3
+ const isArray = require('lodash/isArray');
2
4
  const isEmpty = require('lodash/isEmpty');
3
5
  const joinUrl = require('url-join');
4
6
 
@@ -7,10 +9,12 @@ const joinUrl = require('url-join');
7
9
  * @param {import('@abtnode/client').ConfigEntry[]} configs
8
10
  * @return {string | null}
9
11
  */
10
- const getDIDSpaceBackupEndpoint = (configs) => {
11
- return (
12
- configs?.find((config) => config.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_BACKUP_ENDPOINT)?.value || null
13
- );
12
+ const getBackupEndpoint = (configs) => {
13
+ if (!isArray(configs) || isEmpty(configs)) {
14
+ return '';
15
+ }
16
+
17
+ return configs.find((config) => config.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_BACKUP_ENDPOINT)?.value || null;
14
18
  };
15
19
 
16
20
  /**
@@ -33,7 +37,16 @@ function getBackupFilesUrlFromEndpoint(endpoint) {
33
37
  return joinUrl(prefix, 'space', spaceDid, 'apps', appDid, 'explorer', `?key=/apps/${appDid}/.did-objects/${appDid}/`);
34
38
  }
35
39
 
40
+ function getDIDSpacesUrlFromEndpoint(endpoint) {
41
+ if (!isUrl(endpoint)) {
42
+ throw new Error(`Endpoint(${endpoint}) is not a valid url`);
43
+ }
44
+
45
+ return endpoint.replace(/\/api\/space\/.+/, '');
46
+ }
47
+
36
48
  module.exports = {
37
- getDIDSpaceBackupEndpoint,
49
+ getBackupEndpoint,
38
50
  getBackupFilesUrlFromEndpoint,
51
+ getDIDSpacesUrlFromEndpoint,
39
52
  };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.16.8-beta-0c0c5eb2",
6
+ "version": "1.16.8-beta-81db8efa",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,18 +19,18 @@
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
- "@abtnode/auth": "1.16.8-beta-0c0c5eb2",
23
- "@abtnode/certificate-manager": "1.16.8-beta-0c0c5eb2",
24
- "@abtnode/constant": "1.16.8-beta-0c0c5eb2",
25
- "@abtnode/cron": "1.16.8-beta-0c0c5eb2",
26
- "@abtnode/db": "1.16.8-beta-0c0c5eb2",
27
- "@abtnode/logger": "1.16.8-beta-0c0c5eb2",
28
- "@abtnode/queue": "1.16.8-beta-0c0c5eb2",
29
- "@abtnode/rbac": "1.16.8-beta-0c0c5eb2",
30
- "@abtnode/router-provider": "1.16.8-beta-0c0c5eb2",
31
- "@abtnode/static-server": "1.16.8-beta-0c0c5eb2",
32
- "@abtnode/timemachine": "1.16.8-beta-0c0c5eb2",
33
- "@abtnode/util": "1.16.8-beta-0c0c5eb2",
22
+ "@abtnode/auth": "1.16.8-beta-81db8efa",
23
+ "@abtnode/certificate-manager": "1.16.8-beta-81db8efa",
24
+ "@abtnode/constant": "1.16.8-beta-81db8efa",
25
+ "@abtnode/cron": "1.16.8-beta-81db8efa",
26
+ "@abtnode/db": "1.16.8-beta-81db8efa",
27
+ "@abtnode/logger": "1.16.8-beta-81db8efa",
28
+ "@abtnode/queue": "1.16.8-beta-81db8efa",
29
+ "@abtnode/rbac": "1.16.8-beta-81db8efa",
30
+ "@abtnode/router-provider": "1.16.8-beta-81db8efa",
31
+ "@abtnode/static-server": "1.16.8-beta-81db8efa",
32
+ "@abtnode/timemachine": "1.16.8-beta-81db8efa",
33
+ "@abtnode/util": "1.16.8-beta-81db8efa",
34
34
  "@arcblock/did": "1.18.78",
35
35
  "@arcblock/did-auth": "1.18.78",
36
36
  "@arcblock/did-ext": "^1.18.78",
@@ -39,12 +39,12 @@
39
39
  "@arcblock/event-hub": "1.18.78",
40
40
  "@arcblock/jwt": "^1.18.78",
41
41
  "@arcblock/pm2-events": "^0.0.5",
42
- "@arcblock/validator": "^1.18.77",
42
+ "@arcblock/validator": "^1.18.78",
43
43
  "@arcblock/vc": "1.18.78",
44
- "@blocklet/constant": "1.16.8-beta-0c0c5eb2",
45
- "@blocklet/meta": "1.16.8-beta-0c0c5eb2",
46
- "@blocklet/sdk": "1.16.8-beta-0c0c5eb2",
47
- "@did-space/client": "^0.2.90",
44
+ "@blocklet/constant": "1.16.8-beta-81db8efa",
45
+ "@blocklet/meta": "1.16.8-beta-81db8efa",
46
+ "@blocklet/sdk": "1.16.8-beta-81db8efa",
47
+ "@did-space/client": "^0.2.91",
48
48
  "@fidm/x509": "^1.2.1",
49
49
  "@ocap/mcrypto": "1.18.78",
50
50
  "@ocap/util": "1.18.78",
@@ -71,6 +71,7 @@
71
71
  "js-yaml": "^4.1.0",
72
72
  "kill-port": "^2.0.1",
73
73
  "lodash": "^4.17.21",
74
+ "lru-cache": "^6.0.0",
74
75
  "moment-timezone": "^0.5.37",
75
76
  "node-stream-zip": "^1.15.0",
76
77
  "p-limit": "^3.1.0",
@@ -94,5 +95,5 @@
94
95
  "express": "^4.18.2",
95
96
  "jest": "^27.5.1"
96
97
  },
97
- "gitHead": "0d8ec3154caf670770284dbdabc61a5f4e8b205f"
98
+ "gitHead": "8c4e3190de79a1fb2aaa14646255d938324cc5c2"
98
99
  }