@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 +22 -4
- package/lib/blocklet/manager/disk.js +149 -120
- package/lib/blocklet/storage/backup/spaces.js +2 -4
- package/lib/blocklet/storage/restore/spaces.js +1 -1
- package/lib/event.js +43 -0
- package/lib/index.js +2 -0
- package/lib/states/audit-log.js +24 -2
- package/lib/util/spaces.js +18 -5
- package/package.json +20 -19
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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,
|
|
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 =
|
|
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
|
|
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 (
|
|
957
|
-
|
|
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
|
-
|
|
969
|
-
|
|
963
|
+
return this._attachRuntimeInfo({ did, nodeInfo, diskInfo: true, context });
|
|
964
|
+
}
|
|
970
965
|
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
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
|
-
|
|
981
|
-
|
|
970
|
+
try {
|
|
971
|
+
const blocklet = await this.getBlocklet(did, { throwOnNotExist: false });
|
|
982
972
|
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
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
|
-
|
|
995
|
-
|
|
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,
|
|
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.
|
|
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(
|
|
1908
|
+
targetUrl: getBackupFilesUrlFromEndpoint(getBackupEndpoint(blocklet?.environments)),
|
|
1895
1909
|
});
|
|
1896
1910
|
|
|
1897
1911
|
// 备份成功了
|
|
1898
|
-
this.emit(BlockletEvents.backupProgress, {
|
|
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
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
});
|
|
1950
|
+
try {
|
|
1951
|
+
if (input.delay) {
|
|
1952
|
+
await sleep(input.delay);
|
|
1953
|
+
}
|
|
1936
1954
|
|
|
1937
|
-
|
|
1955
|
+
this.emit(BlockletEvents.restoreProgress, {
|
|
1956
|
+
appDid: input.appDid,
|
|
1957
|
+
meta: { did: appPid },
|
|
1958
|
+
status: RESTORE_PROGRESS_STATUS.start,
|
|
1959
|
+
});
|
|
1938
1960
|
|
|
1939
|
-
|
|
1940
|
-
|
|
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
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
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
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1990
|
+
removeRestoreDir();
|
|
1991
|
+
} catch (error) {
|
|
1992
|
+
removeRestoreDir();
|
|
1993
|
+
throw error;
|
|
1994
|
+
}
|
|
1955
1995
|
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
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
|
-
|
|
2111
|
+
if (level === 0) {
|
|
2112
|
+
component.diskInfo = await getDiskInfo(component, {
|
|
2113
|
+
useFakeDiskInfo: !diskInfo,
|
|
2114
|
+
});
|
|
2115
|
+
}
|
|
2084
2116
|
|
|
2085
|
-
|
|
2086
|
-
|
|
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(
|
|
223
|
+
throw new Error(message);
|
|
226
224
|
}
|
|
227
225
|
}
|
|
228
226
|
|
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,
|
package/lib/states/audit-log.js
CHANGED
|
@@ -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 {
|
|
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 =>
|
|
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
|
package/lib/util/spaces.js
CHANGED
|
@@ -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
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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-
|
|
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-
|
|
23
|
-
"@abtnode/certificate-manager": "1.16.8-beta-
|
|
24
|
-
"@abtnode/constant": "1.16.8-beta-
|
|
25
|
-
"@abtnode/cron": "1.16.8-beta-
|
|
26
|
-
"@abtnode/db": "1.16.8-beta-
|
|
27
|
-
"@abtnode/logger": "1.16.8-beta-
|
|
28
|
-
"@abtnode/queue": "1.16.8-beta-
|
|
29
|
-
"@abtnode/rbac": "1.16.8-beta-
|
|
30
|
-
"@abtnode/router-provider": "1.16.8-beta-
|
|
31
|
-
"@abtnode/static-server": "1.16.8-beta-
|
|
32
|
-
"@abtnode/timemachine": "1.16.8-beta-
|
|
33
|
-
"@abtnode/util": "1.16.8-beta-
|
|
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.
|
|
42
|
+
"@arcblock/validator": "^1.18.78",
|
|
43
43
|
"@arcblock/vc": "1.18.78",
|
|
44
|
-
"@blocklet/constant": "1.16.8-beta-
|
|
45
|
-
"@blocklet/meta": "1.16.8-beta-
|
|
46
|
-
"@blocklet/sdk": "1.16.8-beta-
|
|
47
|
-
"@did-space/client": "^0.2.
|
|
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": "
|
|
98
|
+
"gitHead": "8c4e3190de79a1fb2aaa14646255d938324cc5c2"
|
|
98
99
|
}
|