@abtnode/core 1.17.8-beta-20260109-075740-5f484e08 → 1.17.8-beta-20260111-112953-aed5ff39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/team/access-key-manager.js +104 -0
- package/lib/api/team/invitation-manager.js +461 -0
- package/lib/api/team/notification-manager.js +189 -0
- package/lib/api/team/oauth-manager.js +60 -0
- package/lib/api/team/org-crud-manager.js +202 -0
- package/lib/api/team/org-manager.js +56 -0
- package/lib/api/team/org-member-manager.js +403 -0
- package/lib/api/team/org-query-manager.js +126 -0
- package/lib/api/team/org-resource-manager.js +186 -0
- package/lib/api/team/passport-manager.js +670 -0
- package/lib/api/team/rbac-manager.js +335 -0
- package/lib/api/team/session-manager.js +540 -0
- package/lib/api/team/store-manager.js +198 -0
- package/lib/api/team/tag-manager.js +230 -0
- package/lib/api/team/user-auth-manager.js +132 -0
- package/lib/api/team/user-manager.js +78 -0
- package/lib/api/team/user-query-manager.js +299 -0
- package/lib/api/team/user-social-manager.js +354 -0
- package/lib/api/team/user-update-manager.js +224 -0
- package/lib/api/team/verify-code-manager.js +161 -0
- package/lib/api/team.js +439 -3287
- package/lib/blocklet/manager/disk/auth-manager.js +68 -0
- package/lib/blocklet/manager/disk/backup-manager.js +288 -0
- package/lib/blocklet/manager/disk/cleanup-manager.js +157 -0
- package/lib/blocklet/manager/disk/component-manager.js +83 -0
- package/lib/blocklet/manager/disk/config-manager.js +191 -0
- package/lib/blocklet/manager/disk/controller-manager.js +64 -0
- package/lib/blocklet/manager/disk/delete-reset-manager.js +328 -0
- package/lib/blocklet/manager/disk/download-manager.js +96 -0
- package/lib/blocklet/manager/disk/env-config-manager.js +311 -0
- package/lib/blocklet/manager/disk/federated-manager.js +651 -0
- package/lib/blocklet/manager/disk/hook-manager.js +124 -0
- package/lib/blocklet/manager/disk/install-component-manager.js +95 -0
- package/lib/blocklet/manager/disk/install-core-manager.js +448 -0
- package/lib/blocklet/manager/disk/install-download-manager.js +313 -0
- package/lib/blocklet/manager/disk/install-manager.js +36 -0
- package/lib/blocklet/manager/disk/install-upgrade-manager.js +340 -0
- package/lib/blocklet/manager/disk/job-manager.js +467 -0
- package/lib/blocklet/manager/disk/lifecycle-manager.js +26 -0
- package/lib/blocklet/manager/disk/notification-manager.js +343 -0
- package/lib/blocklet/manager/disk/query-manager.js +562 -0
- package/lib/blocklet/manager/disk/settings-manager.js +507 -0
- package/lib/blocklet/manager/disk/start-manager.js +611 -0
- package/lib/blocklet/manager/disk/stop-restart-manager.js +292 -0
- package/lib/blocklet/manager/disk/update-manager.js +153 -0
- package/lib/blocklet/manager/disk.js +669 -5796
- package/lib/blocklet/manager/helper/blue-green-start-blocklet.js +5 -0
- package/lib/blocklet/manager/lock.js +18 -0
- package/lib/event/index.js +28 -24
- package/lib/util/blocklet/app-utils.js +192 -0
- package/lib/util/blocklet/blocklet-loader.js +258 -0
- package/lib/util/blocklet/config-manager.js +232 -0
- package/lib/util/blocklet/did-document.js +240 -0
- package/lib/util/blocklet/environment.js +555 -0
- package/lib/util/blocklet/health-check.js +449 -0
- package/lib/util/blocklet/install-utils.js +365 -0
- package/lib/util/blocklet/logo.js +57 -0
- package/lib/util/blocklet/meta-utils.js +269 -0
- package/lib/util/blocklet/port-manager.js +141 -0
- package/lib/util/blocklet/process-manager.js +504 -0
- package/lib/util/blocklet/runtime-info.js +105 -0
- package/lib/util/blocklet/validation.js +418 -0
- package/lib/util/blocklet.js +98 -3066
- package/lib/util/wallet-app-notification.js +40 -0
- package/package.json +22 -22
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
const pick = require('lodash/pick');
|
|
2
|
+
const defaultsDeep = require('lodash/defaultsDeep');
|
|
3
|
+
const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager:config');
|
|
4
|
+
const { getBlockletInfo } = require('@blocklet/meta/lib/info');
|
|
5
|
+
const { CustomError } = require('@blocklet/error');
|
|
6
|
+
const { sendToUser } = require('@blocklet/sdk/lib/util/send-notification');
|
|
7
|
+
const { emailConfigSchema } = require('@abtnode/auth/lib/email');
|
|
8
|
+
const { BlockletEvents, BlockletInternalEvents } = require('@blocklet/constant');
|
|
9
|
+
|
|
10
|
+
const states = require('../../../states');
|
|
11
|
+
const { blockletThemeSchema } = require('../../../validators/theme');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Config navigations
|
|
15
|
+
* @param {Object} manager - BlockletManager instance
|
|
16
|
+
* @param {Object} params
|
|
17
|
+
* @param {Object} context
|
|
18
|
+
* @returns {Promise<Object>}
|
|
19
|
+
*/
|
|
20
|
+
async function configNavigations(manager, { did, navigations = [] }, context) {
|
|
21
|
+
if (!Array.isArray(navigations)) {
|
|
22
|
+
throw new Error('navigations is not an array');
|
|
23
|
+
}
|
|
24
|
+
await states.blockletExtras.setSettings(did, { navigations });
|
|
25
|
+
|
|
26
|
+
const newState = await manager.getBlocklet(did);
|
|
27
|
+
manager.emit(BlockletInternalEvents.appSettingChanged, { appDid: did });
|
|
28
|
+
manager.emit(BlockletEvents.updated, { ...newState, context });
|
|
29
|
+
return newState;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Config theme
|
|
34
|
+
* @param {Object} manager - BlockletManager instance
|
|
35
|
+
* @param {Object} params
|
|
36
|
+
* @param {Object} context
|
|
37
|
+
* @returns {Promise<Object>}
|
|
38
|
+
*/
|
|
39
|
+
async function configTheme(manager, { did, theme = {} }, context) {
|
|
40
|
+
const { error, value } = blockletThemeSchema.validate(theme);
|
|
41
|
+
if (error) {
|
|
42
|
+
throw new Error(error.message);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const currentSettings = await states.blockletExtras.getSettings(did);
|
|
46
|
+
const currentTheme = currentSettings.theme || {};
|
|
47
|
+
|
|
48
|
+
if (value.concepts && currentTheme.meta?.locked) {
|
|
49
|
+
throw new CustomError(403, 'Theme is locked and cannot be modified');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const updatedTheme = { ...currentTheme, ...value };
|
|
53
|
+
await states.blockletExtras.setSettings(did, { theme: updatedTheme });
|
|
54
|
+
|
|
55
|
+
const newState = await manager.getBlocklet(did);
|
|
56
|
+
manager.emit(BlockletInternalEvents.appSettingChanged, { appDid: did });
|
|
57
|
+
manager.emit(BlockletEvents.updated, { ...newState, context });
|
|
58
|
+
manager.emit(BlockletEvents.configTheme, { args: { did, theme }, result: newState.settings.theme, context });
|
|
59
|
+
return newState;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Config authentication
|
|
64
|
+
* @param {Object} manager - BlockletManager instance
|
|
65
|
+
* @param {Object} params
|
|
66
|
+
* @param {Object} context
|
|
67
|
+
* @returns {Promise<Object>}
|
|
68
|
+
*/
|
|
69
|
+
async function configAuthentication(manager, { did, authentication = {} }, context) {
|
|
70
|
+
const oldConfig = await states.blockletExtras.getSettings(did, 'authentication', {});
|
|
71
|
+
const parsedData = JSON.parse(authentication);
|
|
72
|
+
const mergeConfig = defaultsDeep(parsedData, oldConfig);
|
|
73
|
+
await states.blockletExtras.setSettings(did, { authentication: mergeConfig });
|
|
74
|
+
const newState = await manager.getBlocklet(did);
|
|
75
|
+
manager.emit(BlockletInternalEvents.appSettingChanged, { appDid: did });
|
|
76
|
+
manager.emit(BlockletEvents.updated, { ...newState, context });
|
|
77
|
+
return newState;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Config DID connect
|
|
82
|
+
* @param {Object} manager - BlockletManager instance
|
|
83
|
+
* @param {Object} params
|
|
84
|
+
* @param {Object} context
|
|
85
|
+
* @returns {Promise<Object>}
|
|
86
|
+
*/
|
|
87
|
+
async function configDidConnect(manager, { did, didConnect = {} }, context) {
|
|
88
|
+
const oldConfig = await states.blockletExtras.getSettings(did, 'didConnect', {});
|
|
89
|
+
const parsedData = JSON.parse(didConnect);
|
|
90
|
+
const mergeConfig = defaultsDeep(parsedData, oldConfig);
|
|
91
|
+
await states.blockletExtras.setSettings(did, { didConnect: mergeConfig });
|
|
92
|
+
const newState = await manager.getBlocklet(did);
|
|
93
|
+
manager.emit(BlockletInternalEvents.appSettingChanged, { appDid: did });
|
|
94
|
+
manager.emit(BlockletEvents.updated, { ...newState, context });
|
|
95
|
+
return newState;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Config notification
|
|
100
|
+
* @param {Object} manager - BlockletManager instance
|
|
101
|
+
* @param {Object} params
|
|
102
|
+
* @param {Object} context
|
|
103
|
+
* @returns {Promise<Object>}
|
|
104
|
+
*/
|
|
105
|
+
async function configNotification(manager, { did, notification = {} }, context) {
|
|
106
|
+
let newConfig = {};
|
|
107
|
+
try {
|
|
108
|
+
newConfig = JSON.parse(notification);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
logger.error('parse configNotification error', { error });
|
|
111
|
+
throw new Error('parse configNotification error');
|
|
112
|
+
}
|
|
113
|
+
const enabled = newConfig?.email?.enabled;
|
|
114
|
+
if (enabled) {
|
|
115
|
+
const { error } = emailConfigSchema.validate(
|
|
116
|
+
pick(newConfig?.email || {}, ['from', 'host', 'port', 'user', 'password', 'secure'])
|
|
117
|
+
);
|
|
118
|
+
if (error) {
|
|
119
|
+
logger.error('configNotification validate error', { error });
|
|
120
|
+
throw new Error(error.message);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const oldConfig = await states.blockletExtras.getSettings(did, 'notification', {});
|
|
124
|
+
const mergeConfig = { ...oldConfig, ...newConfig };
|
|
125
|
+
await states.blockletExtras.setSettings(did, { notification: mergeConfig });
|
|
126
|
+
const newState = await manager.getBlocklet(did);
|
|
127
|
+
manager.emit(BlockletInternalEvents.appSettingChanged, { appDid: did });
|
|
128
|
+
manager.emit(BlockletEvents.updated, { ...newState, context });
|
|
129
|
+
return newState;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Send test email
|
|
134
|
+
* @param {Object} manager - BlockletManager instance
|
|
135
|
+
* @param {Object} params
|
|
136
|
+
* @returns {Promise<Object>}
|
|
137
|
+
*/
|
|
138
|
+
async function sendEmail(manager, { did, receiver, email }) {
|
|
139
|
+
const blocklet = await manager.getBlocklet(did);
|
|
140
|
+
const nodeInfo = await states.node.read();
|
|
141
|
+
const { wallet } = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
142
|
+
|
|
143
|
+
const [result] = await sendToUser(
|
|
144
|
+
receiver,
|
|
145
|
+
JSON.parse(email),
|
|
146
|
+
{
|
|
147
|
+
appDid: wallet.address,
|
|
148
|
+
appSk: wallet.secretKey,
|
|
149
|
+
},
|
|
150
|
+
undefined,
|
|
151
|
+
'send-to-mail'
|
|
152
|
+
);
|
|
153
|
+
if (result && result.status === 'fulfilled') {
|
|
154
|
+
return result.value;
|
|
155
|
+
}
|
|
156
|
+
throw new Error(result?.reason || 'failed to send email');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Send push notification
|
|
161
|
+
* @param {Object} manager - BlockletManager instance
|
|
162
|
+
* @param {Object} params
|
|
163
|
+
* @returns {Promise<Object>}
|
|
164
|
+
*/
|
|
165
|
+
async function sendPush(manager, { did, receiver, notification }) {
|
|
166
|
+
const blocklet = await manager.getBlocklet(did);
|
|
167
|
+
const nodeInfo = await states.node.read();
|
|
168
|
+
const { wallet } = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
169
|
+
|
|
170
|
+
const result = await sendToUser(
|
|
171
|
+
receiver,
|
|
172
|
+
JSON.parse(notification),
|
|
173
|
+
{
|
|
174
|
+
appDid: wallet.address,
|
|
175
|
+
appSk: wallet.secretKey,
|
|
176
|
+
},
|
|
177
|
+
undefined,
|
|
178
|
+
'send-to-push-kit'
|
|
179
|
+
);
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
module.exports = {
|
|
184
|
+
configNavigations,
|
|
185
|
+
configTheme,
|
|
186
|
+
configAuthentication,
|
|
187
|
+
configDidConnect,
|
|
188
|
+
configNotification,
|
|
189
|
+
sendEmail,
|
|
190
|
+
sendPush,
|
|
191
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const isEmpty = require('lodash/isEmpty');
|
|
2
|
+
const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager:controller');
|
|
3
|
+
const { BLOCKLET_CONTROLLER_STATUS, SUSPENDED_REASON } = require('@blocklet/constant');
|
|
4
|
+
|
|
5
|
+
const states = require('../../../states');
|
|
6
|
+
const launcher = require('../../../util/launcher');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Check controller status
|
|
10
|
+
* @param {Object} manager - BlockletManager instance
|
|
11
|
+
* @param {Object} blocklet - Blocklet
|
|
12
|
+
* @param {string} action - Action name
|
|
13
|
+
* @returns {Promise<void>}
|
|
14
|
+
*/
|
|
15
|
+
async function checkControllerStatus(manager, blocklet, action) {
|
|
16
|
+
if (isEmpty(blocklet.controller)) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const isExpired = await launcher.isBlockletExpired(blocklet.meta.did, blocklet.controller);
|
|
21
|
+
|
|
22
|
+
if (isExpired) {
|
|
23
|
+
if (blocklet.controller.status?.value !== BLOCKLET_CONTROLLER_STATUS.suspended) {
|
|
24
|
+
await states.blockletExtras.updateByDid(blocklet.meta.did, {
|
|
25
|
+
controller: {
|
|
26
|
+
...blocklet.controller,
|
|
27
|
+
status: {
|
|
28
|
+
value: BLOCKLET_CONTROLLER_STATUS.suspended,
|
|
29
|
+
reason: SUSPENDED_REASON.expired,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
logger.info('update blocklet controller status to suspended', {
|
|
34
|
+
did: blocklet.meta.did,
|
|
35
|
+
nftId: blocklet.controller?.nftId,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
logger.error(`try to ${action} an expired blocklet`, {
|
|
39
|
+
did: blocklet.meta.did,
|
|
40
|
+
nftId: blocklet.controller?.nftId,
|
|
41
|
+
});
|
|
42
|
+
throw new Error(`Can not ${action} an expired blocklet`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (
|
|
46
|
+
blocklet.controller.status?.value === BLOCKLET_CONTROLLER_STATUS.suspended &&
|
|
47
|
+
blocklet.controller.status?.reason === SUSPENDED_REASON.expired
|
|
48
|
+
) {
|
|
49
|
+
// 如果是因为过期被暂停的,续期后, 并且不影响启动
|
|
50
|
+
await states.blockletExtras.updateByDid(blocklet.meta.did, {
|
|
51
|
+
controller: {
|
|
52
|
+
...blocklet.controller,
|
|
53
|
+
status: {
|
|
54
|
+
value: BLOCKLET_CONTROLLER_STATUS.normal,
|
|
55
|
+
reason: '',
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = {
|
|
63
|
+
checkControllerStatus,
|
|
64
|
+
};
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/* eslint-disable no-await-in-loop */
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const pick = require('lodash/pick');
|
|
5
|
+
const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager:delete-reset');
|
|
6
|
+
const { BlockletEvents, BlockletStatus } = require('@blocklet/constant');
|
|
7
|
+
const {
|
|
8
|
+
forEachBlockletSync,
|
|
9
|
+
isInProgress,
|
|
10
|
+
getDisplayName,
|
|
11
|
+
isExternalBlocklet,
|
|
12
|
+
isDeletableBlocklet,
|
|
13
|
+
} = require('@blocklet/meta/lib/util');
|
|
14
|
+
|
|
15
|
+
const { APP_CONFIG_DIR, COMPONENT_ENV_FILE_NAME } = require('@blocklet/constant');
|
|
16
|
+
const states = require('../../../states');
|
|
17
|
+
const hooks = require('../../hooks');
|
|
18
|
+
const {
|
|
19
|
+
deleteBlockletProcess,
|
|
20
|
+
getRuntimeEnvironments,
|
|
21
|
+
getHookArgs,
|
|
22
|
+
removeAppConfigsFromComponent,
|
|
23
|
+
} = require('../../../util/blocklet');
|
|
24
|
+
const checkNeedRunDocker = require('../../../util/docker/check-need-run-docker');
|
|
25
|
+
const { dockerExec } = require('../../../util/docker/docker-exec');
|
|
26
|
+
const { dockerExecChown } = require('../../../util/docker/docker-exec-chown');
|
|
27
|
+
const checkDockerRunHistory = require('../../../util/docker/check-docker-run-history');
|
|
28
|
+
const parseDockerName = require('../../../util/docker/parse-docker-name');
|
|
29
|
+
const { removeDockerNetwork } = require('../../../util/docker/docker-network');
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Safely remove directories
|
|
33
|
+
*/
|
|
34
|
+
async function _safeRemoveDir(manager, did, childDid, inputDirs) {
|
|
35
|
+
const dirs = inputDirs.filter(Boolean);
|
|
36
|
+
try {
|
|
37
|
+
for (const dir of dirs) {
|
|
38
|
+
fs.removeSync(dir);
|
|
39
|
+
logger.info(`removed blocklet ${did} ${childDid}: ${dir}`);
|
|
40
|
+
}
|
|
41
|
+
} catch (error) {
|
|
42
|
+
const nodeInfo = await states.node.read();
|
|
43
|
+
// 如果删除因为权限问题失败,尝试用 docker 用户去调整用户组为宿主机当前用户
|
|
44
|
+
if (checkDockerRunHistory(nodeInfo) && error?.message?.includes('EACCES')) {
|
|
45
|
+
await dockerExecChown({ name: `${did}-${childDid}-remove-catch`, dirs, force: true });
|
|
46
|
+
for (const dir of dirs) {
|
|
47
|
+
fs.removeSync(dir);
|
|
48
|
+
logger.info(`removed blocklet ${did} ${childDid}: ${dir}`);
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Delete blocklet
|
|
58
|
+
*/
|
|
59
|
+
async function deleteBlocklet(manager, { did, keepData, keepLogsDir, keepConfigs, sessionId }, context) {
|
|
60
|
+
logger.info('delete blocklet', { did, keepData, sessionId });
|
|
61
|
+
|
|
62
|
+
const blocklet = await manager.getBlocklet(did);
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
if (isDeletableBlocklet(blocklet) === false) {
|
|
66
|
+
throw new Error('Blocklet is protected from accidental deletion');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const nodeInfo = await states.node.read();
|
|
70
|
+
const nodeEnvironments = await states.node.getEnvironments(nodeInfo);
|
|
71
|
+
await deleteBlockletProcess(blocklet, {
|
|
72
|
+
preDelete: async (b, { ancestors }) => {
|
|
73
|
+
const needRunDocker = await checkNeedRunDocker(
|
|
74
|
+
b.meta,
|
|
75
|
+
getRuntimeEnvironments(b, nodeEnvironments, ancestors),
|
|
76
|
+
nodeInfo,
|
|
77
|
+
isExternalBlocklet(blocklet)
|
|
78
|
+
);
|
|
79
|
+
const hookArgs = getHookArgs(b);
|
|
80
|
+
const nextEnv = getRuntimeEnvironments(b, nodeEnvironments, ancestors);
|
|
81
|
+
if (needRunDocker) {
|
|
82
|
+
if (b.meta.scripts?.preUninstall) {
|
|
83
|
+
await dockerExec({
|
|
84
|
+
blocklet,
|
|
85
|
+
meta: b.meta,
|
|
86
|
+
env: nextEnv,
|
|
87
|
+
script: b.meta.scripts.preUninstall,
|
|
88
|
+
hookName: 'preUninstall',
|
|
89
|
+
nodeInfo,
|
|
90
|
+
retry: 0,
|
|
91
|
+
...hookArgs,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
return hooks.preUninstall(b.meta.title, {
|
|
97
|
+
appDir: b.env.appDir,
|
|
98
|
+
hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
|
|
99
|
+
env: nextEnv,
|
|
100
|
+
did, // root blocklet did
|
|
101
|
+
notification: states.notification,
|
|
102
|
+
context,
|
|
103
|
+
exitOnError: false,
|
|
104
|
+
...hookArgs,
|
|
105
|
+
});
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const doc = await manager._deleteBlocklet({ did, keepData, keepLogsDir, keepConfigs }, context);
|
|
110
|
+
manager._createNotification(
|
|
111
|
+
doc.meta.did,
|
|
112
|
+
{
|
|
113
|
+
title: 'Blocklet Deleted',
|
|
114
|
+
description: `Blocklet ${getDisplayName(blocklet)} is deleted.`,
|
|
115
|
+
entityType: 'blocklet',
|
|
116
|
+
entityId: doc.meta.did,
|
|
117
|
+
severity: 'success',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
skipGetBlocklet: true,
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
await removeDockerNetwork(parseDockerName(did, 'docker-network'));
|
|
124
|
+
return doc;
|
|
125
|
+
} catch (error) {
|
|
126
|
+
// If we installed a corrupted blocklet accidentally, just cleanup the disk and state db
|
|
127
|
+
logger.error('blocklet delete failed, will delete again', { did, error });
|
|
128
|
+
const doc = await manager._deleteBlocklet({ did, keepData, keepLogsDir, keepConfigs }, context);
|
|
129
|
+
await removeDockerNetwork(parseDockerName(did, 'docker-network'));
|
|
130
|
+
|
|
131
|
+
manager._createNotification(
|
|
132
|
+
doc.meta.did,
|
|
133
|
+
{
|
|
134
|
+
title: 'Blocklet Deleted',
|
|
135
|
+
description: `Blocklet ${getDisplayName(blocklet)} is deleted.`,
|
|
136
|
+
entityType: 'blocklet',
|
|
137
|
+
entityId: doc.meta.did,
|
|
138
|
+
severity: 'success',
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
skipGetBlocklet: true,
|
|
142
|
+
}
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
return doc;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Reset blocklet
|
|
151
|
+
*/
|
|
152
|
+
async function reset(manager, { did, childDid }, context = {}) {
|
|
153
|
+
logger.info('reset blocklet', { did, childDid });
|
|
154
|
+
|
|
155
|
+
const blocklet = await manager.getBlocklet(did);
|
|
156
|
+
|
|
157
|
+
if (!childDid) {
|
|
158
|
+
if (isInProgress(blocklet.status || blocklet.status === BlockletStatus.running)) {
|
|
159
|
+
throw new Error('Cannot reset when blocklet is in progress');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
await manager.deleteProcess({ did }, context);
|
|
164
|
+
} catch {
|
|
165
|
+
// do nothing
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Cleanup disk storage
|
|
169
|
+
const { cacheDir, logsDir, dataDir } = blocklet.env;
|
|
170
|
+
await _safeRemoveDir(manager, did, blocklet.meta.did, [cacheDir, dataDir, logsDir]);
|
|
171
|
+
|
|
172
|
+
// Reset config in db
|
|
173
|
+
await manager._resetExtras(blocklet.meta.did);
|
|
174
|
+
await manager._updateBlockletEnvironment(did);
|
|
175
|
+
await manager.resetSiteByDid(did, context);
|
|
176
|
+
} else {
|
|
177
|
+
const child = blocklet.children.find((x) => x.meta.did === childDid);
|
|
178
|
+
|
|
179
|
+
if (!child) {
|
|
180
|
+
throw new Error('Child does not exist');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (isInProgress(child.status || child.status === BlockletStatus.running)) {
|
|
184
|
+
throw new Error('Cannot reset when component is in progress');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
try {
|
|
188
|
+
await manager.deleteProcess({ did, componentDids: [child.meta.did] }, context);
|
|
189
|
+
} catch {
|
|
190
|
+
// do nothing
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Cleanup disk storage
|
|
194
|
+
const { cacheDir, logsDir, dataDir } = child.env;
|
|
195
|
+
await _safeRemoveDir(manager, did, child.meta.did, [cacheDir, dataDir, logsDir]);
|
|
196
|
+
|
|
197
|
+
// Reset config in db
|
|
198
|
+
await states.blockletExtras.delConfigs([blocklet.meta.did, child.meta.did]);
|
|
199
|
+
await manager._setConfigsFromMeta(blocklet.meta.did, child.meta.did);
|
|
200
|
+
await manager._updateBlockletEnvironment(did);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
logger.info('blocklet reset', { did, childDid });
|
|
204
|
+
return blocklet;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Delete component
|
|
209
|
+
*/
|
|
210
|
+
async function deleteComponent(manager, { did, rootDid, keepData, keepState, sessionId }, context) {
|
|
211
|
+
logger.info('delete blocklet component', { did, rootDid, keepData, keepState, sessionId });
|
|
212
|
+
|
|
213
|
+
const nodeInfo = await states.node.read();
|
|
214
|
+
if (checkDockerRunHistory(nodeInfo)) {
|
|
215
|
+
await dockerExecChown({
|
|
216
|
+
name: `${did}-${rootDid}-deleteComponent`,
|
|
217
|
+
dirs: [path.join(process.env.ABT_NODE_DATA_DIR, 'data', rootDid)],
|
|
218
|
+
force: true,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
const app = await manager.getBlocklet(rootDid);
|
|
222
|
+
|
|
223
|
+
const child = app.children.find((x) => x.meta.did === did);
|
|
224
|
+
if (!child) {
|
|
225
|
+
throw new Error('Component does not exist');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// delete state
|
|
229
|
+
const doc = await states.blocklet.getBlocklet(rootDid);
|
|
230
|
+
doc.children = doc.children.filter((x) => x.meta.did !== did);
|
|
231
|
+
const deletedChildren = await states.blockletExtras.getSettings(did, 'children', []);
|
|
232
|
+
if (keepData !== false && keepState !== false) {
|
|
233
|
+
deletedChildren.push({
|
|
234
|
+
meta: pick(child.meta, ['did', 'name', 'bundleDid', 'bundleName', 'version', 'title', 'description']),
|
|
235
|
+
mountPoint: child.mountPoint,
|
|
236
|
+
status: BlockletStatus.deleted,
|
|
237
|
+
deletedAt: new Date(),
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
await states.blocklet.updateBlocklet(rootDid, doc);
|
|
242
|
+
states.blockletExtras.setSettings(doc.meta.did, { children: deletedChildren });
|
|
243
|
+
|
|
244
|
+
// delete process
|
|
245
|
+
try {
|
|
246
|
+
const skippedProcessIds = [];
|
|
247
|
+
forEachBlockletSync(app, (b) => {
|
|
248
|
+
if (!b.env.id.startsWith(child.env.id)) {
|
|
249
|
+
skippedProcessIds.push(b.env.processId);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
await deleteBlockletProcess(app, { skippedProcessIds });
|
|
253
|
+
logger.info('delete blocklet process for deleting component', { did, rootDid });
|
|
254
|
+
} catch (err) {
|
|
255
|
+
logger.error('delete blocklet process for deleting component', { did, rootDid, error: err });
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// delete storage
|
|
259
|
+
const { cacheDir, logsDir, dataDir } = child.env;
|
|
260
|
+
await _safeRemoveDir(
|
|
261
|
+
manager,
|
|
262
|
+
rootDid,
|
|
263
|
+
child.meta.did,
|
|
264
|
+
[
|
|
265
|
+
cacheDir,
|
|
266
|
+
logsDir,
|
|
267
|
+
path.join(app.env.dataDir, APP_CONFIG_DIR, child.meta.did, COMPONENT_ENV_FILE_NAME),
|
|
268
|
+
keepData === false ? dataDir : null,
|
|
269
|
+
].filter(Boolean)
|
|
270
|
+
);
|
|
271
|
+
if (keepData === false) {
|
|
272
|
+
const componentEnvs = await states.blockletExtras.getConfigs([app.meta.did, child.meta.did]);
|
|
273
|
+
await states.blockletExtras.delConfigs([app.meta.did, child.meta.did]);
|
|
274
|
+
|
|
275
|
+
// remove app configs if no component use it
|
|
276
|
+
const tmpApp = await manager.getBlocklet(rootDid);
|
|
277
|
+
await removeAppConfigsFromComponent(componentEnvs, tmpApp, states.blockletExtras);
|
|
278
|
+
}
|
|
279
|
+
const newBlocklet = await manager.getBlocklet(rootDid);
|
|
280
|
+
|
|
281
|
+
if (newBlocklet.controller) {
|
|
282
|
+
const componentDids = [did];
|
|
283
|
+
const eventType = 'uninstall';
|
|
284
|
+
|
|
285
|
+
manager.reportComponentUsageQueue.push({
|
|
286
|
+
entity: 'blocklet',
|
|
287
|
+
action: 'reportComponentUsage',
|
|
288
|
+
did: newBlocklet.meta.did,
|
|
289
|
+
time: new Date().toISOString(),
|
|
290
|
+
componentDids,
|
|
291
|
+
eventType,
|
|
292
|
+
context,
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
logger.info('pushed reporting uninstall components event job to queue', {
|
|
296
|
+
did: newBlocklet.meta.did,
|
|
297
|
+
componentDids,
|
|
298
|
+
eventType,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
await manager._updateDependents(rootDid);
|
|
303
|
+
|
|
304
|
+
// support edge case
|
|
305
|
+
if (newBlocklet.children.length === 0) {
|
|
306
|
+
await states.blocklet.setBlockletStatus(newBlocklet.meta.did, BlockletStatus.stopped);
|
|
307
|
+
await removeDockerNetwork(parseDockerName(app.meta.did, 'docker-network'));
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
manager.emit(BlockletEvents.upgraded, { blocklet: newBlocklet, context: { ...context, createAuditLog: false } }); // trigger router refresh
|
|
311
|
+
|
|
312
|
+
manager._createNotification(newBlocklet.meta.did, {
|
|
313
|
+
title: 'Component delete succeed',
|
|
314
|
+
description: `${child.meta.title} is successfully deleted for ${getDisplayName(newBlocklet)}.`,
|
|
315
|
+
entityType: 'blocklet',
|
|
316
|
+
entityId: newBlocklet.meta.did,
|
|
317
|
+
severity: 'success',
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
return newBlocklet;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
module.exports = {
|
|
324
|
+
_safeRemoveDir,
|
|
325
|
+
delete: deleteBlocklet,
|
|
326
|
+
reset,
|
|
327
|
+
deleteComponent,
|
|
328
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager:download');
|
|
2
|
+
const { BlockletStatus, fromBlockletStatus } = require('@blocklet/constant');
|
|
3
|
+
const { forEachComponentV2Sync } = require('@blocklet/meta/lib/util');
|
|
4
|
+
|
|
5
|
+
const states = require('../../../states');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Cancel download
|
|
9
|
+
* @param {Object} manager - BlockletManager instance
|
|
10
|
+
* @param {Object} params
|
|
11
|
+
* @param {string} params.did - Blocklet DID
|
|
12
|
+
* @param {Object} statusLock - Status lock
|
|
13
|
+
* @returns {Promise<Object>}
|
|
14
|
+
*/
|
|
15
|
+
async function cancelDownload(manager, { did: inputDid }, statusLock) {
|
|
16
|
+
await statusLock.acquire('cancel-download');
|
|
17
|
+
try {
|
|
18
|
+
const blocklet = await states.blocklet.getBlocklet(inputDid);
|
|
19
|
+
if (!blocklet) {
|
|
20
|
+
throw new Error(`Can not cancel download for non-exist blocklet in database. did: ${inputDid}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const { name, did, version } = blocklet.meta;
|
|
24
|
+
|
|
25
|
+
if (
|
|
26
|
+
![BlockletStatus.downloading, BlockletStatus.waiting].includes(blocklet.greenStatus) &&
|
|
27
|
+
![BlockletStatus.downloading, BlockletStatus.waiting].includes(blocklet.status)
|
|
28
|
+
) {
|
|
29
|
+
throw new Error(`Can not cancel blocklet that status is ${fromBlockletStatus(blocklet.status)}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const job = await manager.installQueue.get(did);
|
|
33
|
+
|
|
34
|
+
// cancel job
|
|
35
|
+
if (blocklet.greenStatus === BlockletStatus.downloading || blocklet.status === BlockletStatus.downloading) {
|
|
36
|
+
try {
|
|
37
|
+
await manager.blockletDownloader.cancelDownload(blocklet.meta.did);
|
|
38
|
+
} catch (error) {
|
|
39
|
+
logger.error('failed to exec blockletDownloader.download', { did: blocklet.meta.did, error });
|
|
40
|
+
}
|
|
41
|
+
} else if (blocklet.greenStatus === BlockletStatus.waiting || blocklet.status === BlockletStatus.waiting) {
|
|
42
|
+
try {
|
|
43
|
+
await manager.installQueue.cancel(blocklet.meta.did);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
logger.error('failed to cancel waiting', { did: blocklet.meta.did, error });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// rollback
|
|
50
|
+
if (job) {
|
|
51
|
+
const { postAction, oldBlocklet } = job;
|
|
52
|
+
await manager._rollback(postAction, did, oldBlocklet);
|
|
53
|
+
} else {
|
|
54
|
+
const data = await manager._rollbackCache.restore({ did });
|
|
55
|
+
if (data) {
|
|
56
|
+
const { action, oldBlocklet } = data;
|
|
57
|
+
await manager._rollback(action, did, oldBlocklet);
|
|
58
|
+
await manager._rollbackCache.remove({ did: inputDid });
|
|
59
|
+
} else {
|
|
60
|
+
throw new Error(`Cannot find rollback data in queue or backup file of blocklet ${inputDid}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
logger.info('cancel download blocklet', { did, name, version, status: fromBlockletStatus(blocklet.status) });
|
|
65
|
+
|
|
66
|
+
return blocklet;
|
|
67
|
+
} catch (error) {
|
|
68
|
+
try {
|
|
69
|
+
// fallback blocklet status to error
|
|
70
|
+
const blocklet = await states.blocklet.getBlocklet(inputDid);
|
|
71
|
+
if (blocklet) {
|
|
72
|
+
const componentDids = [];
|
|
73
|
+
forEachComponentV2Sync(blocklet, (x) => {
|
|
74
|
+
if (
|
|
75
|
+
[BlockletStatus.waiting, BlockletStatus.downloading].includes(x.status) ||
|
|
76
|
+
[BlockletStatus.waiting, BlockletStatus.downloading].includes(x.greenStatus)
|
|
77
|
+
) {
|
|
78
|
+
componentDids.push(x.meta.did);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
await states.blocklet.setBlockletStatus(blocklet.meta.did, BlockletStatus.error, { componentDids });
|
|
83
|
+
}
|
|
84
|
+
} catch (err) {
|
|
85
|
+
logger.error('Failed to fallback blocklet status to error on cancelDownload', { error });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
throw error;
|
|
89
|
+
} finally {
|
|
90
|
+
await statusLock.releaseLock('cancel-download');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
module.exports = {
|
|
95
|
+
cancelDownload,
|
|
96
|
+
};
|