@abtnode/core 1.16.8-beta-0c0c5eb2 → 1.16.8-beta-b3039c78

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,33 @@ class BlockletManager extends BaseBlockletManager {
1235
1226
  return newState;
1236
1227
  }
1237
1228
 
1229
+ async configNotification({ did, notification = {} }) {
1230
+ let newConfig = {};
1231
+ try {
1232
+ newConfig = JSON.parse(notification);
1233
+ } catch (error) {
1234
+ logger.error('parse configNotification error', { error });
1235
+ throw new Error('parse configNotification error');
1236
+ }
1237
+ const enabled = newConfig?.email?.enabled;
1238
+ if (enabled) {
1239
+ const { error } = emailConfigSchema.validate(
1240
+ pick(newConfig?.email || {}, ['from', 'host', 'port', 'user', 'password'])
1241
+ );
1242
+ if (error) {
1243
+ logger.error('configNotification validate error', { error });
1244
+ throw new Error(error.message);
1245
+ }
1246
+ }
1247
+ const oldConfig = await states.blockletExtras.getSettings(did, 'notification', {});
1248
+ // NOTICE:validate 本身没有做任何字段规范化处理,所以处理后的结果跟处理前是一样的,最终结果的合并先使用原始值
1249
+ const mergeConfig = { ...oldConfig, ...newConfig };
1250
+ await states.blockletExtras.setSettings(did, { notification: mergeConfig });
1251
+ const newState = await this.getBlocklet(did);
1252
+ this.emit(BlockletEvents.updated, newState);
1253
+ return newState;
1254
+ }
1255
+
1238
1256
  // TODO: this method can be removed if title is not changed anymore
1239
1257
  async updateComponentTitle({ did, rootDid: inputRootDid, title }) {
1240
1258
  await titleSchema.validateAsync(title);
@@ -1596,6 +1614,14 @@ class BlockletManager extends BaseBlockletManager {
1596
1614
  return states.backup.getBlockletBackups({ did });
1597
1615
  }
1598
1616
 
1617
+ deleteCache(did) {
1618
+ const cache = this.cachedBlocklets.get(did);
1619
+ if (cache) {
1620
+ this.cachedBlocklets.del(cache.appDid);
1621
+ this.cachedBlocklets.del(cache.appPid);
1622
+ }
1623
+ }
1624
+
1599
1625
  // ============================================================================================
1600
1626
  // Private API that are used by self of helper function
1601
1627
  // ============================================================================================
@@ -1891,11 +1917,18 @@ class BlockletManager extends BaseBlockletManager {
1891
1917
  await spacesBackup.backup();
1892
1918
 
1893
1919
  await states.backup.success(backup._id, {
1894
- targetUrl: getBackupFilesUrlFromEndpoint(getDIDSpaceBackupEndpoint(blocklet?.environments)),
1920
+ targetUrl: getBackupFilesUrlFromEndpoint(getBackupEndpoint(blocklet?.environments)),
1895
1921
  });
1896
1922
 
1897
1923
  // 备份成功了
1898
- this.emit(BlockletEvents.backupProgress, { appDid, meta: { did: appPid }, completed: true, progress: 100 });
1924
+ this.emit(BlockletEvents.backupProgress, {
1925
+ appDid,
1926
+ meta: { did: appPid },
1927
+ completed: true,
1928
+ progress: 100,
1929
+ context,
1930
+ blocklet,
1931
+ });
1899
1932
  } catch (error) {
1900
1933
  await states.backup.fail(backup._id, {
1901
1934
  message: error?.message,
@@ -1906,6 +1939,8 @@ class BlockletManager extends BaseBlockletManager {
1906
1939
  completed: true,
1907
1940
  progress: -1,
1908
1941
  message: error?.message,
1942
+ context,
1943
+ blocklet,
1909
1944
  });
1910
1945
  throw error;
1911
1946
  }
@@ -1922,59 +1957,69 @@ class BlockletManager extends BaseBlockletManager {
1922
1957
  */
1923
1958
  // eslint-disable-next-line no-unused-vars
1924
1959
  async _onRestoreFromSpaces({ input, context }) {
1925
- if (input.delay) {
1926
- await sleep(input.delay);
1927
- }
1928
-
1929
1960
  const appPid = input.appDid;
1930
1961
 
1931
- this.emit(BlockletEvents.restoreProgress, {
1932
- appDid: input.appDid,
1933
- meta: { did: appPid },
1934
- status: RESTORE_PROGRESS_STATUS.start,
1935
- });
1962
+ try {
1963
+ if (input.delay) {
1964
+ await sleep(input.delay);
1965
+ }
1966
+
1967
+ this.emit(BlockletEvents.restoreProgress, {
1968
+ appDid: input.appDid,
1969
+ meta: { did: appPid },
1970
+ status: RESTORE_PROGRESS_STATUS.start,
1971
+ });
1936
1972
 
1937
- const userDid = context.user.did;
1973
+ const userDid = context.user.did;
1974
+ const spacesRestore = new SpacesRestore({ ...input, appPid, event: this, userDid, referrer: context.referrer });
1975
+ const params = await spacesRestore.restore();
1938
1976
 
1939
- const spacesRestore = new SpacesRestore({ ...input, appPid, event: this, userDid, referrer: context.referrer });
1940
- const params = await spacesRestore.restore();
1977
+ const removeRestoreDir = () => {
1978
+ if (fs.existsSync(spacesRestore.restoreDir)) {
1979
+ fs.remove(spacesRestore.restoreDir).catch((err) => {
1980
+ logger.error('failed to remove restore dir', { error: err, dir: spacesRestore.restoreDir });
1981
+ });
1982
+ }
1983
+ };
1941
1984
 
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 });
1985
+ this.emit(BlockletEvents.restoreProgress, {
1986
+ appDid: input.appDid,
1987
+ meta: { did: appPid },
1988
+ status: RESTORE_PROGRESS_STATUS.installing,
1989
+ });
1990
+
1991
+ try {
1992
+ await installApplicationFromBackup({
1993
+ url: `file://${spacesRestore.restoreDir}`,
1994
+ moveDir: true,
1995
+ ...merge(...params),
1996
+ manager: this,
1997
+ states,
1998
+ controller: input.controller,
1999
+ context: { ...context, startImmediately: true },
1946
2000
  });
1947
- }
1948
- };
1949
2001
 
1950
- this.emit(BlockletEvents.restoreProgress, {
1951
- appDid: input.appDid,
1952
- meta: { did: appPid },
1953
- status: RESTORE_PROGRESS_STATUS.installing,
1954
- });
2002
+ removeRestoreDir();
2003
+ } catch (error) {
2004
+ removeRestoreDir();
2005
+ throw error;
2006
+ }
1955
2007
 
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 },
2008
+ this.emit(BlockletEvents.restoreProgress, {
2009
+ appDid: input.appDid,
2010
+ meta: { did: appPid },
2011
+ status: RESTORE_PROGRESS_STATUS.completed,
1965
2012
  });
1966
-
1967
- removeRestoreDir();
1968
2013
  } catch (error) {
1969
- removeRestoreDir();
2014
+ this.emit(BlockletEvents.restoreProgress, {
2015
+ appDid: input.appDid,
2016
+ meta: { did: appPid },
2017
+ status: RESTORE_PROGRESS_STATUS.error,
2018
+ message: error.message,
2019
+ });
2020
+
1970
2021
  throw error;
1971
2022
  }
1972
-
1973
- this.emit(BlockletEvents.restoreProgress, {
1974
- appDid: input.appDid,
1975
- meta: { did: appPid },
1976
- status: RESTORE_PROGRESS_STATUS.completed,
1977
- });
1978
2023
  }
1979
2024
 
1980
2025
  async _updateBlockletEnvironment(did) {
@@ -2027,7 +2072,27 @@ class BlockletManager extends BaseBlockletManager {
2027
2072
  return states.blocklet.updateBlocklet(did, blocklet);
2028
2073
  }
2029
2074
 
2030
- async _attachRuntimeInfo({ did, nodeInfo, diskInfo = true, context, cachedBlocklet }) {
2075
+ async _attachBlockletListRuntimeInfo({ blocklets }, context) {
2076
+ const nodeInfo = await states.node.read();
2077
+ return (
2078
+ await Promise.all(
2079
+ blocklets.map((x) => {
2080
+ if (isBeforeInstalled(x.status)) {
2081
+ return x;
2082
+ }
2083
+
2084
+ return this._attachRuntimeInfo({
2085
+ did: x.meta.did,
2086
+ nodeInfo,
2087
+ diskInfo: false,
2088
+ context,
2089
+ });
2090
+ })
2091
+ )
2092
+ ).filter(Boolean);
2093
+ }
2094
+
2095
+ async _attachRuntimeInfo({ did, nodeInfo, diskInfo = true, context }) {
2031
2096
  if (!did) {
2032
2097
  throw new Error('did should not be empty');
2033
2098
  }
@@ -2039,24 +2104,6 @@ class BlockletManager extends BaseBlockletManager {
2039
2104
  return null;
2040
2105
  }
2041
2106
 
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
2107
  // 处理 domainAliases#value SLOT_FOR_IP_DNS_SITE
2061
2108
  if (blocklet?.site?.domainAliases?.length) {
2062
2109
  const nodeIp = await getAccessibleExternalNodeIp(nodeInfo);
@@ -2069,24 +2116,18 @@ class BlockletManager extends BaseBlockletManager {
2069
2116
  // app runtime info, app status
2070
2117
  blocklet.appRuntimeInfo = this.runtimeMonitor.getRuntimeInfo(blocklet.meta.did);
2071
2118
 
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
- }
2119
+ // app disk info, component runtime info, component engine
2120
+ await forEachBlocklet(blocklet, async (component, { level }) => {
2121
+ component.engine = getEngine(getBlockletEngineNameByPlatform(component.meta)).describe();
2082
2122
 
2083
- component.runtimeInfo = this.runtimeMonitor.getRuntimeInfo(blocklet.meta.did, component.env.id);
2123
+ if (level === 0) {
2124
+ component.diskInfo = await getDiskInfo(component, {
2125
+ useFakeDiskInfo: !diskInfo,
2126
+ });
2127
+ }
2084
2128
 
2085
- if (component.runtimeInfo?.status && shouldUpdateBlockletStatus(component.status)) {
2086
- component.status = statusMap[component.runtimeInfo.status];
2087
- }
2088
- });
2089
- }
2129
+ component.runtimeInfo = this.runtimeMonitor.getRuntimeInfo(blocklet.meta.did, component.env.id);
2130
+ });
2090
2131
 
2091
2132
  return blocklet;
2092
2133
  } 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-b3039c78",
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-b3039c78",
23
+ "@abtnode/certificate-manager": "1.16.8-beta-b3039c78",
24
+ "@abtnode/constant": "1.16.8-beta-b3039c78",
25
+ "@abtnode/cron": "1.16.8-beta-b3039c78",
26
+ "@abtnode/db": "1.16.8-beta-b3039c78",
27
+ "@abtnode/logger": "1.16.8-beta-b3039c78",
28
+ "@abtnode/queue": "1.16.8-beta-b3039c78",
29
+ "@abtnode/rbac": "1.16.8-beta-b3039c78",
30
+ "@abtnode/router-provider": "1.16.8-beta-b3039c78",
31
+ "@abtnode/static-server": "1.16.8-beta-b3039c78",
32
+ "@abtnode/timemachine": "1.16.8-beta-b3039c78",
33
+ "@abtnode/util": "1.16.8-beta-b3039c78",
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-b3039c78",
45
+ "@blocklet/meta": "1.16.8-beta-b3039c78",
46
+ "@blocklet/sdk": "1.16.8-beta-b3039c78",
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": "3f27acff94c144a1cf3dcf14bc1972c79a330641"
98
99
  }