@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.
Files changed (65) hide show
  1. package/lib/api/team/access-key-manager.js +104 -0
  2. package/lib/api/team/invitation-manager.js +461 -0
  3. package/lib/api/team/notification-manager.js +189 -0
  4. package/lib/api/team/oauth-manager.js +60 -0
  5. package/lib/api/team/org-crud-manager.js +202 -0
  6. package/lib/api/team/org-manager.js +56 -0
  7. package/lib/api/team/org-member-manager.js +403 -0
  8. package/lib/api/team/org-query-manager.js +126 -0
  9. package/lib/api/team/org-resource-manager.js +186 -0
  10. package/lib/api/team/passport-manager.js +670 -0
  11. package/lib/api/team/rbac-manager.js +335 -0
  12. package/lib/api/team/session-manager.js +540 -0
  13. package/lib/api/team/store-manager.js +198 -0
  14. package/lib/api/team/tag-manager.js +230 -0
  15. package/lib/api/team/user-auth-manager.js +132 -0
  16. package/lib/api/team/user-manager.js +78 -0
  17. package/lib/api/team/user-query-manager.js +299 -0
  18. package/lib/api/team/user-social-manager.js +354 -0
  19. package/lib/api/team/user-update-manager.js +224 -0
  20. package/lib/api/team/verify-code-manager.js +161 -0
  21. package/lib/api/team.js +439 -3287
  22. package/lib/blocklet/manager/disk/auth-manager.js +68 -0
  23. package/lib/blocklet/manager/disk/backup-manager.js +288 -0
  24. package/lib/blocklet/manager/disk/cleanup-manager.js +157 -0
  25. package/lib/blocklet/manager/disk/component-manager.js +83 -0
  26. package/lib/blocklet/manager/disk/config-manager.js +191 -0
  27. package/lib/blocklet/manager/disk/controller-manager.js +64 -0
  28. package/lib/blocklet/manager/disk/delete-reset-manager.js +328 -0
  29. package/lib/blocklet/manager/disk/download-manager.js +96 -0
  30. package/lib/blocklet/manager/disk/env-config-manager.js +311 -0
  31. package/lib/blocklet/manager/disk/federated-manager.js +651 -0
  32. package/lib/blocklet/manager/disk/hook-manager.js +124 -0
  33. package/lib/blocklet/manager/disk/install-component-manager.js +95 -0
  34. package/lib/blocklet/manager/disk/install-core-manager.js +448 -0
  35. package/lib/blocklet/manager/disk/install-download-manager.js +313 -0
  36. package/lib/blocklet/manager/disk/install-manager.js +36 -0
  37. package/lib/blocklet/manager/disk/install-upgrade-manager.js +340 -0
  38. package/lib/blocklet/manager/disk/job-manager.js +467 -0
  39. package/lib/blocklet/manager/disk/lifecycle-manager.js +26 -0
  40. package/lib/blocklet/manager/disk/notification-manager.js +343 -0
  41. package/lib/blocklet/manager/disk/query-manager.js +562 -0
  42. package/lib/blocklet/manager/disk/settings-manager.js +507 -0
  43. package/lib/blocklet/manager/disk/start-manager.js +611 -0
  44. package/lib/blocklet/manager/disk/stop-restart-manager.js +292 -0
  45. package/lib/blocklet/manager/disk/update-manager.js +153 -0
  46. package/lib/blocklet/manager/disk.js +669 -5796
  47. package/lib/blocklet/manager/helper/blue-green-start-blocklet.js +5 -0
  48. package/lib/blocklet/manager/lock.js +18 -0
  49. package/lib/event/index.js +28 -24
  50. package/lib/util/blocklet/app-utils.js +192 -0
  51. package/lib/util/blocklet/blocklet-loader.js +258 -0
  52. package/lib/util/blocklet/config-manager.js +232 -0
  53. package/lib/util/blocklet/did-document.js +240 -0
  54. package/lib/util/blocklet/environment.js +555 -0
  55. package/lib/util/blocklet/health-check.js +449 -0
  56. package/lib/util/blocklet/install-utils.js +365 -0
  57. package/lib/util/blocklet/logo.js +57 -0
  58. package/lib/util/blocklet/meta-utils.js +269 -0
  59. package/lib/util/blocklet/port-manager.js +141 -0
  60. package/lib/util/blocklet/process-manager.js +504 -0
  61. package/lib/util/blocklet/runtime-info.js +105 -0
  62. package/lib/util/blocklet/validation.js +418 -0
  63. package/lib/util/blocklet.js +98 -3066
  64. package/lib/util/wallet-app-notification.js +40 -0
  65. package/package.json +22 -22
@@ -0,0 +1,68 @@
1
+ const omit = require('lodash/omit');
2
+ const { getConfigs } = require('@blocklet/meta/lib/util-config');
3
+ const { LOGIN_PROVIDER } = require('@blocklet/constant');
4
+ const { getEmailServiceProvider } = require('@abtnode/auth/lib/email');
5
+
6
+ /**
7
+ * Migrate blocklet authentication
8
+ * @param {Object} manager - BlockletManager instance
9
+ * @param {Object} params
10
+ * @param {string} params.did - Blocklet DID
11
+ * @returns {Promise<void>}
12
+ */
13
+ async function migrateBlockletAuthentication(manager, { did }) {
14
+ const blocklet = await manager.getBlocklet(did);
15
+ if (blocklet.settings?.authentication) return;
16
+
17
+ const authenticationList = [];
18
+
19
+ const configs = getConfigs(blocklet, did);
20
+ const allowWallet = configs.find((x) => x.key === 'DID_CONNECT_ALLOW_WALLET')?.value;
21
+ if (['1', 'true', undefined, null].includes(allowWallet)) {
22
+ // 默认是允许使用 wallet
23
+ authenticationList.push({
24
+ enabled: true,
25
+ showQrcode: true,
26
+ type: 'builtin',
27
+ provider: LOGIN_PROVIDER.WALLET,
28
+ });
29
+ }
30
+
31
+ const isEmailServiceEnabled = Boolean(getEmailServiceProvider(blocklet));
32
+ if (isEmailServiceEnabled) {
33
+ authenticationList.push({
34
+ enabled: isEmailServiceEnabled,
35
+ type: 'builtin',
36
+ provider: LOGIN_PROVIDER.EMAIL,
37
+ });
38
+ }
39
+
40
+ // 对旧的数据进行转换,并保存
41
+ if (blocklet.settings?.oauth) {
42
+ const oauthList = Object.keys(blocklet.settings?.oauth)
43
+ .map((key) => ({
44
+ ...omit(blocklet.settings?.oauth[key], 'order'),
45
+ type: 'oauth',
46
+ provider: key,
47
+ }))
48
+ .filter((x) => x.enabled === true);
49
+ authenticationList.push(...oauthList);
50
+ }
51
+ authenticationList.push({
52
+ enabled: true,
53
+ type: 'builtin',
54
+ provider: LOGIN_PROVIDER.PASSKEY,
55
+ });
56
+ const authentication = authenticationList.reduce((acc, curr, index) => {
57
+ acc[curr.provider] = {
58
+ ...omit(curr, ['provider']),
59
+ order: index,
60
+ };
61
+ return acc;
62
+ }, {});
63
+ await manager.configAuthentication({ did, authentication: JSON.stringify(authentication) });
64
+ }
65
+
66
+ module.exports = {
67
+ migrateBlockletAuthentication,
68
+ };
@@ -0,0 +1,288 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const merge = require('lodash/merge');
4
+ const isEmpty = require('lodash/isEmpty');
5
+ const { joinURL } = require('ufo');
6
+ const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager:backup');
7
+ const sleep = require('@abtnode/util/lib/sleep');
8
+ const { encode } = require('@abtnode/util/lib/base32');
9
+ const { BACKUPS, DEFAULT_DID_DOMAIN } = require('@abtnode/constant');
10
+ const { BlockletStatus, BlockletEvents, RESTORE_PROGRESS_STATUS } = require('@blocklet/constant');
11
+
12
+ const states = require('../../../states');
13
+ const { SpacesBackup } = require('../../storage/backup/spaces');
14
+ const { SpacesRestore } = require('../../storage/restore/spaces');
15
+ const {
16
+ getBackupJobId,
17
+ getBackupEndpoint,
18
+ getBackupFilesUrlFromEndpoint,
19
+ getSpaceNameByEndpoint,
20
+ } = require('../../../util/spaces');
21
+ const { shouldJobBackoff } = require('../../../util');
22
+ const { installApplicationFromBackup } = require('../helper/install-application-from-backup');
23
+
24
+ /**
25
+ * Handle backup to spaces
26
+ * @param {Object} manager - BlockletManager instance
27
+ * @param {Object} params
28
+ * @param {string} params.did - Blocklet DID
29
+ * @param {Object} params.context - Context
30
+ * @param {Object} params.backupState - Backup state
31
+ * @returns {Promise<void>}
32
+ */
33
+ async function _onBackupToSpaces(
34
+ manager,
35
+ {
36
+ did,
37
+ context,
38
+ backupState = {
39
+ strategy: BACKUPS.STRATEGY.AUTO,
40
+ },
41
+ }
42
+ ) {
43
+ const blocklet = await states.blocklet.getBlocklet(did);
44
+ const {
45
+ appDid,
46
+ meta: { did: appPid },
47
+ } = blocklet;
48
+
49
+ if (shouldJobBackoff()) {
50
+ const backup = await states.backup.findOne({ appPid }, {}, { createdAt: -1 });
51
+ const message = 'Backup to spaces is not available when blocklet server is starting.';
52
+
53
+ if (backup.status === BACKUPS.STATUS.PROGRESS) {
54
+ await states.backup.fail(backup.id, {
55
+ message,
56
+ });
57
+ }
58
+
59
+ manager.emit(BlockletEvents.backupProgress, {
60
+ appDid,
61
+ meta: { did: appPid },
62
+ completed: true,
63
+ progress: -1,
64
+ message,
65
+ backup,
66
+ context,
67
+ blocklet,
68
+ });
69
+
70
+ logger.warn(message);
71
+ return;
72
+ }
73
+
74
+ const spaceGateways = await manager.getBlockletSpaceGateways({ did });
75
+ const backupEndpoint = getBackupEndpoint(blocklet.environments);
76
+ if (isEmpty(spaceGateways) || isEmpty(backupEndpoint)) {
77
+ logger.warn('Canceled automatic backups because there is no space gateway.', { appPid });
78
+
79
+ const jobId = getBackupJobId(did);
80
+ await manager.backupQueue.delete(jobId);
81
+ await manager.updateAutoBackup({ did, autoBackup: { enabled: false } }, context);
82
+
83
+ return;
84
+ }
85
+
86
+ const {
87
+ user: { did: userDid, locale },
88
+ } = context;
89
+
90
+ let { referrer } = context;
91
+ if (isEmpty(referrer)) {
92
+ // fixes: https://github.com/ArcBlock/did-spaces/issues/908, referrer 在某些情况下会不存在
93
+ /**
94
+ * @type {import('@blocklet/server-js').NodeState}
95
+ */
96
+ const node = await states.node.read();
97
+ referrer = joinURL(
98
+ `https://${encode(node.did)}.${DEFAULT_DID_DOMAIN}`,
99
+ node.routing.adminPath,
100
+ `/blocklets/${did}/didSpaces`
101
+ );
102
+ }
103
+
104
+ const verifyBackup = () => {
105
+ // @note: 自动备份时,应用停止运行就跳过本次备份
106
+ if (backupState?.strategy === BACKUPS.STRATEGY.AUTO && blocklet.status === BlockletStatus.stopped) {
107
+ const message =
108
+ 'Automatic backup is only available to running blocklets, please start your blocklet before enable automatic backup.';
109
+ logger.error(message, { appPid });
110
+
111
+ return message;
112
+ }
113
+
114
+ if (backupState?.strategy === BACKUPS.STRATEGY.AUTO && shouldJobBackoff()) {
115
+ const message = 'Automatic backup is not available when blocklet server is starting.';
116
+ logger.error(message, { appPid });
117
+
118
+ return message;
119
+ }
120
+
121
+ return null;
122
+ };
123
+
124
+ let backup = await states.backup.findOne({ appPid }, {}, { createdAt: -1 });
125
+ if (backup?.status !== BACKUPS.STATUS.PROGRESS) {
126
+ const message = verifyBackup();
127
+ if (message) {
128
+ return;
129
+ }
130
+
131
+ // 创建备份记录
132
+ backup = await states.backup.start({
133
+ appPid,
134
+ userDid,
135
+ strategy: backupState?.strategy,
136
+ sourceUrl: path.join(manager.dataDirs.tmp, 'backup', appDid),
137
+ targetName: await getSpaceNameByEndpoint(backupEndpoint, 'DID Space'),
138
+ });
139
+ }
140
+
141
+ const spacesBackup = new SpacesBackup({ appDid, appPid, event: manager, userDid, referrer, locale, backup });
142
+ try {
143
+ const message = verifyBackup();
144
+ if (message) {
145
+ throw new Error(message);
146
+ }
147
+
148
+ manager.emit(BlockletEvents.backupProgress, {
149
+ appDid,
150
+ meta: { did: appPid },
151
+ message: 'Start backup...',
152
+ progress: 10,
153
+ completed: false,
154
+ });
155
+ await states.backup.progress(backup.id, {
156
+ message: 'Start backup...',
157
+ progress: 10,
158
+ });
159
+ await spacesBackup.backup();
160
+
161
+ await states.backup.success(backup.id, {
162
+ targetUrl: getBackupFilesUrlFromEndpoint(getBackupEndpoint(blocklet?.environments)),
163
+ metadata: {
164
+ count: spacesBackup.backupOutput.data?.count,
165
+ size: spacesBackup.backupOutput.data?.size,
166
+ },
167
+ });
168
+
169
+ // 备份成功了
170
+ manager.emit(BlockletEvents.backupProgress, {
171
+ appDid,
172
+ meta: { did: appPid },
173
+ completed: true,
174
+ progress: 100,
175
+ backup,
176
+ context,
177
+ blocklet,
178
+ });
179
+ } catch (error) {
180
+ // @note: 主动抛出错误表明正在备份中,不做处理
181
+ if (error.cause?.status === BlockletEvents.backupProgress) {
182
+ logger.warn(error.message);
183
+ return;
184
+ }
185
+
186
+ await states.backup.fail(backup.id, {
187
+ message: error?.message,
188
+ });
189
+
190
+ manager.emit(BlockletEvents.backupProgress, {
191
+ appDid,
192
+ meta: { did: appPid },
193
+ completed: true,
194
+ progress: -1,
195
+ message: error?.message,
196
+ backup,
197
+ context,
198
+ blocklet,
199
+ });
200
+ throw error;
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Handle restore from spaces
206
+ * @param {Object} manager - BlockletManager instance
207
+ * @param {Object} params
208
+ * @param {Object} params.input - Restore input
209
+ * @param {Object} params.context - Context
210
+ * @returns {Promise<void>}
211
+ */
212
+ async function _onRestoreFromSpaces(manager, { input, context }) {
213
+ try {
214
+ if (input.delay) {
215
+ await sleep(input.delay);
216
+ }
217
+
218
+ manager.emit(BlockletEvents.restoreProgress, {
219
+ appDid: input.appDid,
220
+ meta: { did: input.appPid },
221
+ status: RESTORE_PROGRESS_STATUS.start,
222
+ });
223
+
224
+ const userDid = context.user.did;
225
+ const spacesRestore = new SpacesRestore({
226
+ ...input,
227
+ appPid: input.appPid,
228
+ event: manager,
229
+ userDid,
230
+ referrer: context.referrer,
231
+ });
232
+ const params = await spacesRestore.restore();
233
+
234
+ const removeRestoreDir = () => {
235
+ if (fs.existsSync(spacesRestore.restoreDir)) {
236
+ fs.remove(spacesRestore.restoreDir).catch((err) => {
237
+ logger.error('failed to remove restore dir', { error: err, dir: spacesRestore.restoreDir });
238
+ });
239
+ }
240
+ };
241
+
242
+ manager.emit(BlockletEvents.restoreProgress, {
243
+ appDid: input.appDid,
244
+ meta: { did: input.appPid },
245
+ status: RESTORE_PROGRESS_STATUS.installing,
246
+ });
247
+
248
+ try {
249
+ await installApplicationFromBackup({
250
+ url: `file://${spacesRestore.restoreDir}`,
251
+ moveDir: true,
252
+ ...merge(...params),
253
+ manager,
254
+ states,
255
+ controller: input.controller,
256
+ context: { ...context, startImmediately: true },
257
+ });
258
+
259
+ removeRestoreDir();
260
+ } catch (error) {
261
+ console.error(error);
262
+ removeRestoreDir();
263
+ throw error;
264
+ }
265
+
266
+ manager.emit(BlockletEvents.restoreProgress, {
267
+ appDid: input.appDid,
268
+ meta: { did: input.appPid },
269
+ status: RESTORE_PROGRESS_STATUS.completed,
270
+ });
271
+ } catch (error) {
272
+ console.error(error);
273
+
274
+ manager.emit(BlockletEvents.restoreProgress, {
275
+ appDid: input.appDid,
276
+ meta: { did: input.appPid },
277
+ status: RESTORE_PROGRESS_STATUS.error,
278
+ message: error.message,
279
+ });
280
+
281
+ throw error;
282
+ }
283
+ }
284
+
285
+ module.exports = {
286
+ _onBackupToSpaces,
287
+ _onRestoreFromSpaces,
288
+ };
@@ -0,0 +1,157 @@
1
+ const path = require('path');
2
+ const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager:cleanup');
3
+ const { INSTALL_ACTIONS } = require('@abtnode/constant');
4
+ const { BlockletEvents } = require('@blocklet/constant');
5
+
6
+ const states = require('../../../states');
7
+ const checkDockerRunHistory = require('../../../util/docker/check-docker-run-history');
8
+ const { dockerExecChown } = require('../../../util/docker/docker-exec-chown');
9
+
10
+ /**
11
+ * Rollback blocklet installation/upgrade
12
+ * @param {Object} manager - BlockletManager instance
13
+ * @param {string} action - install, upgrade, installComponent, upgradeComponent
14
+ * @param {string} did - Blocklet DID
15
+ * @param {Object} oldBlocklet - Previous blocklet state
16
+ * @returns {Promise<Object>}
17
+ */
18
+ async function _rollback(manager, action, did, oldBlocklet) {
19
+ if (action === INSTALL_ACTIONS.INSTALL) {
20
+ const extraState = oldBlocklet?.extraState;
21
+
22
+ // rollback blocklet extra state
23
+ if (extraState) {
24
+ await states.blockletExtras.update({ did }, extraState);
25
+ } else {
26
+ await states.blockletExtras.remove({ did });
27
+ }
28
+
29
+ // remove blocklet state
30
+ return _deleteBlocklet(manager, { did, keepData: true });
31
+ }
32
+
33
+ if (
34
+ [
35
+ INSTALL_ACTIONS.INSTALL_COMPONENT,
36
+ INSTALL_ACTIONS.UPGRADE_COMPONENT,
37
+ 'upgrade', // for backward compatibility
38
+ ].includes(action)
39
+ ) {
40
+ const { extraState, ...blocklet } = oldBlocklet;
41
+ // rollback blocklet state
42
+ const result = await states.blocklet.updateBlocklet(did, blocklet);
43
+
44
+ // rollback blocklet extra state
45
+ await states.blockletExtras.update({ did: blocklet.meta.did }, extraState);
46
+
47
+ logger.info('blocklet rollback successfully', { did, action });
48
+ manager.emit(BlockletEvents.updated, result);
49
+ return result;
50
+ }
51
+
52
+ logger.error('rollback action is invalid', { action });
53
+ throw new Error(`rollback action is invalid: ${action}`);
54
+ }
55
+
56
+ /**
57
+ * Delete blocklet
58
+ * @param {Object} manager - BlockletManager instance
59
+ * @param {Object} params
60
+ * @param {Object} context
61
+ * @returns {Promise<Object>}
62
+ */
63
+ async function _deleteBlocklet(manager, { did, keepData, keepLogsDir, keepConfigs }, context) {
64
+ const blocklet = await states.blocklet.getBlocklet(did);
65
+ const { name } = blocklet.meta;
66
+ const cacheDir = path.join(manager.dataDirs.cache, name);
67
+
68
+ // Cleanup db
69
+ await manager.teamManager.deleteTeam(blocklet.meta.did);
70
+
71
+ // Cleanup disk storage
72
+ await _cleanBlockletData(manager, { blocklet, keepData, keepLogsDir, keepConfigs, appCacheDir: cacheDir });
73
+
74
+ const result = await states.blocklet.deleteBlocklet(did);
75
+ logger.info('blocklet removed successfully', { did });
76
+
77
+ let keepRouting = true;
78
+ if (keepData === false || keepConfigs === false) {
79
+ keepRouting = false;
80
+ }
81
+
82
+ manager.runtimeMonitor.delete(blocklet.meta.did);
83
+
84
+ manager.emit(BlockletEvents.removed, {
85
+ blocklet: result,
86
+ context: {
87
+ ...context,
88
+ keepRouting,
89
+ },
90
+ });
91
+ return blocklet;
92
+ }
93
+
94
+ /**
95
+ * Clean blocklet data
96
+ * @param {Object} manager - BlockletManager instance
97
+ * @param {Object} params
98
+ * @returns {Promise<void>}
99
+ */
100
+ async function _cleanBlockletData(manager, { blocklet, keepData, keepLogsDir, keepConfigs, appCacheDir }) {
101
+ const { name } = blocklet.meta;
102
+
103
+ const dataDir = path.join(manager.dataDirs.data, name);
104
+ const logsDir = path.join(manager.dataDirs.logs, name);
105
+ const cacheDir = path.join(manager.dataDirs.cache, name);
106
+ const nodeInfo = await states.node.read();
107
+ if (checkDockerRunHistory(nodeInfo)) {
108
+ await dockerExecChown({
109
+ name: `${blocklet.meta.did}-clean-data`,
110
+ dirs: [dataDir, logsDir, cacheDir],
111
+ force: true,
112
+ });
113
+ }
114
+
115
+ logger.info(`clean blocklet ${blocklet.meta.did} data`, { keepData, keepLogsDir, keepConfigs });
116
+
117
+ if (keepData === false) {
118
+ await manager._safeRemoveDir(blocklet.meta.did, name, [appCacheDir, dataDir, logsDir, cacheDir]);
119
+ await states.blockletExtras.remove({ did: blocklet.meta.did });
120
+ logger.info(`removed blocklet ${blocklet.meta.did} extra data`);
121
+ } else {
122
+ if (keepLogsDir === false) {
123
+ await manager._safeRemoveDir(blocklet.meta.did, name, [appCacheDir, logsDir]);
124
+ } else {
125
+ await manager._safeRemoveDir(blocklet.meta.did, name, [appCacheDir]);
126
+ }
127
+
128
+ if (keepConfigs === false) {
129
+ await states.blockletExtras.remove({ did: blocklet.meta.did });
130
+ logger.info(`removed blocklet ${blocklet.meta.did} extra data`);
131
+ }
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Get blocklet for installation with extra state
137
+ * @param {string} did - Blocklet DID
138
+ * @returns {Promise<Object|null>}
139
+ */
140
+ async function _getBlockletForInstallation(did) {
141
+ const blocklet = await states.blocklet.getBlocklet(did, { decryptSk: false });
142
+ if (!blocklet) {
143
+ return null;
144
+ }
145
+
146
+ const extraState = await states.blockletExtras.findOne({ did: blocklet.meta.did });
147
+ blocklet.extraState = extraState;
148
+
149
+ return blocklet;
150
+ }
151
+
152
+ module.exports = {
153
+ _rollback,
154
+ _deleteBlocklet,
155
+ _cleanBlockletData,
156
+ _getBlockletForInstallation,
157
+ };
@@ -0,0 +1,83 @@
1
+ const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager:component');
2
+ const { BlockletGroup, BlockletEvents, BlockletInternalEvents, BlockletStatus } = require('@blocklet/constant');
3
+ const { updateMountPointSchema } = require('@blocklet/meta/lib/schema');
4
+ const { getComponentsInternalInfo } = require('@blocklet/meta/lib/blocklet');
5
+
6
+ const states = require('../../../states');
7
+ const { checkDuplicateMountPoint } = require('../../../util/blocklet');
8
+
9
+ /**
10
+ * Update component mount point
11
+ * @param {Object} manager - BlockletManager instance
12
+ * @param {Object} params
13
+ * @param {string} params.did - Component DID
14
+ * @param {string} params.rootDid - Root blocklet DID
15
+ * @param {string} params.mountPoint - New mount point
16
+ * @param {Object} context - Context
17
+ * @returns {Promise<Object>}
18
+ */
19
+ async function updateComponentMountPoint(manager, { did, rootDid: inputRootDid, mountPoint: tmpMountPoint }, context) {
20
+ const mountPoint = await updateMountPointSchema.validateAsync(tmpMountPoint);
21
+
22
+ const blocklet = await states.blocklet.getBlocklet(inputRootDid);
23
+
24
+ if (!blocklet) {
25
+ throw new Error('blocklet does not exist');
26
+ }
27
+
28
+ const rootDid = blocklet.meta.did;
29
+
30
+ const isRootComponent = !did;
31
+
32
+ const component = isRootComponent ? blocklet : blocklet.children.find((x) => x.meta.did === did);
33
+ if (!component) {
34
+ throw new Error('component does not exist');
35
+ }
36
+
37
+ if (isRootComponent && component.group === BlockletGroup.gateway) {
38
+ throw new Error('cannot update mountPoint of gateway blocklet');
39
+ }
40
+
41
+ checkDuplicateMountPoint(blocklet, mountPoint);
42
+
43
+ component.mountPoint = mountPoint;
44
+
45
+ await states.blocklet.updateBlocklet(rootDid, { mountPoint: blocklet.mountPoint, children: blocklet.children });
46
+
47
+ manager.emit(BlockletEvents.upgraded, { blocklet, context: { ...context, createAuditLog: false } }); // trigger router refresh
48
+ manager.emit(BlockletInternalEvents.componentUpdated, {
49
+ appDid: blocklet.appDid,
50
+ components: getComponentsInternalInfo(blocklet).filter((x) => x.did === component.meta.did),
51
+ });
52
+ manager.configSynchronizer.throttledSyncAppConfig(blocklet.meta.did);
53
+
54
+ // Restart blocklet if it's running to pick up the new mount point
55
+ // Only restart the component that was modified (did)
56
+ if (!isRootComponent && did) {
57
+ const updatedBlocklet = await manager.getBlocklet(rootDid);
58
+ const updatedComponent = updatedBlocklet.children.find((x) => x.meta.did === did);
59
+
60
+ if (
61
+ updatedComponent &&
62
+ (updatedComponent.status === BlockletStatus.running || updatedComponent.greenStatus === BlockletStatus.running)
63
+ ) {
64
+ try {
65
+ await manager.restart({ did: rootDid, componentDids: [did], operator: context?.user?.did }, context);
66
+ logger.info('restarted blocklet after mount point update', { rootDid, componentDid: did });
67
+ } catch (error) {
68
+ logger.error('failed to restart blocklet after mount point update', {
69
+ rootDid,
70
+ componentDid: did,
71
+ error,
72
+ });
73
+ // Don't throw error - mount point update succeeded, restart failure is logged but doesn't block the operation
74
+ }
75
+ }
76
+ }
77
+
78
+ return manager.getBlocklet(rootDid);
79
+ }
80
+
81
+ module.exports = {
82
+ updateComponentMountPoint,
83
+ };