@abtnode/core 1.17.8-beta-20260109-075740-5f484e08 → 1.17.8-beta-20260113-015027-32a1cec4

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,313 @@
1
+ /* eslint-disable no-await-in-loop */
2
+ const path = require('path');
3
+ const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager:install-download');
4
+ const promiseSpawn = require('@abtnode/util/lib/promise-spawn');
5
+ const { INSTALL_ACTIONS } = require('@abtnode/constant');
6
+ const { BlockletStatus, BlockletEvents, fromBlockletStatus } = require('@blocklet/constant');
7
+ const { forEachComponentV2 } = require('@blocklet/meta/lib/util');
8
+
9
+ const states = require('../../../states');
10
+ const { getDisplayName } = require('../../../util/blocklet');
11
+ const { dockerExecChown } = require('../../../util/docker/docker-exec-chown');
12
+ const checkDockerRunHistory = require('../../../util/docker/check-docker-run-history');
13
+ const { blueGreenGetComponentIds } = require('../helper/blue-green-get-componentids.js');
14
+ const { blueGreenUpgradeBlocklet } = require('../helper/blue-green-upgrade-blocklet.js');
15
+ const { statusLock } = require('../lock');
16
+
17
+ /**
18
+ * Download and install blocklet
19
+ */
20
+ async function _downloadAndInstall(manager, params, { _onInstall, _upgradeBlocklet }) {
21
+ const {
22
+ blocklet,
23
+ context,
24
+ postAction,
25
+ oldBlocklet,
26
+ throwOnError,
27
+ skipCheckStatusBeforeDownload,
28
+ componentDids,
29
+ skipCheckIntegrity,
30
+ shouldCleanUploadFile,
31
+ url,
32
+ } = params;
33
+
34
+ logger.info('_downloadAndInstall', { did: blocklet?.meta?.did });
35
+ const { meta } = blocklet;
36
+ const { name, did, version } = meta;
37
+
38
+ const nodeInfo = await states.node.read();
39
+ if (checkDockerRunHistory(nodeInfo)) {
40
+ const newBlocklet = await manager.getBlocklet(did);
41
+ // pull docker image
42
+ await forEachComponentV2(newBlocklet, async (component) => {
43
+ if (component?.meta?.docker?.image) {
44
+ await promiseSpawn(`docker pull ${component?.meta?.docker?.image}`, {}, { timeout: 120 * 1000, retry: 0 });
45
+ }
46
+ });
47
+ }
48
+
49
+ // check status
50
+ if (!skipCheckStatusBeforeDownload) {
51
+ await statusLock.acquire('skip-check-status-before-download');
52
+ try {
53
+ const b0 = await states.blocklet.getBlocklet(did);
54
+ if (!b0 || ![BlockletStatus.waiting].includes(b0.status)) {
55
+ if (!b0) {
56
+ throw new Error('blocklet does not exist before downloading');
57
+ } else {
58
+ logger.error(`blocklet status is invalid before downloading: ${fromBlockletStatus(b0.status)}`);
59
+ }
60
+ }
61
+ } catch (error) {
62
+ logger.error('Check blocklet status failed before downloading', {
63
+ name,
64
+ did,
65
+ error,
66
+ });
67
+ await manager._rollback(postAction, did, oldBlocklet);
68
+ return;
69
+ } finally {
70
+ await statusLock.releaseLock('skip-check-status-before-download');
71
+ }
72
+ }
73
+
74
+ // download bundle
75
+ const childrenToDownload = (blocklet.children || []).filter((x) => {
76
+ if (componentDids?.length) {
77
+ return componentDids.includes(x.meta.did);
78
+ }
79
+ return x;
80
+ });
81
+
82
+ const blueGreenComponentIds = blueGreenGetComponentIds(oldBlocklet || blocklet, componentDids);
83
+
84
+ try {
85
+ if (process.env.ABT_NODE_DISABLE_BLUE_GREEN === 'true' || process.env.ABT_NODE_DISABLE_BLUE_GREEN === '1') {
86
+ await states.blocklet.setBlockletStatus(did, BlockletStatus.downloading, {
87
+ componentDids,
88
+ isGreen: false,
89
+ });
90
+ } else {
91
+ await Promise.all(
92
+ blueGreenComponentIds.map(async (item) => {
93
+ if (item.componentDids.length === 0) {
94
+ return;
95
+ }
96
+ await states.blocklet.setBlockletStatus(did, BlockletStatus.downloading, {
97
+ componentDids: item.componentDids,
98
+ isGreen: item.changeToGreen,
99
+ });
100
+ })
101
+ );
102
+ }
103
+
104
+ const state = await states.blocklet.getBlocklet(did);
105
+ manager.emit(BlockletEvents.statusChange, state);
106
+ const { isCancelled } = await manager._downloadBlocklet(
107
+ {
108
+ ...blocklet,
109
+ children: childrenToDownload,
110
+ },
111
+ Object.assign({}, context, { skipCheckIntegrity })
112
+ );
113
+
114
+ if (isCancelled) {
115
+ logger.info('Download was canceled', { name, did, version });
116
+
117
+ // rollback on download cancelled
118
+ await statusLock.acquire('download-cancelled');
119
+ try {
120
+ if ((await states.blocklet.getBlockletStatus(did)) === BlockletStatus.downloading) {
121
+ await manager._rollback(postAction, did, oldBlocklet);
122
+ }
123
+ } catch (error) {
124
+ logger.error('Rollback blocklet failed on download canceled', { postAction, name, did, version, error });
125
+ } finally {
126
+ await statusLock.releaseLock('download-cancelled');
127
+ }
128
+ return;
129
+ }
130
+ } catch (err) {
131
+ if (err.message.includes('EACCES:')) {
132
+ if (checkDockerRunHistory(nodeInfo)) {
133
+ try {
134
+ await dockerExecChown({
135
+ name: `${did}-${blocklet.meta.name}-download`,
136
+ dirs: [
137
+ path.join(process.env.ABT_NODE_DATA_DIR, 'blocklets'),
138
+ path.join(process.env.ABT_NODE_DATA_DIR, 'tmp', 'pnpm-stores'),
139
+ ],
140
+ });
141
+ } catch (error) {
142
+ logger.error('Retry download blocklet failed on chown', { did, name, version, error });
143
+ }
144
+ }
145
+ }
146
+ logger.error('Download blocklet tarball failed', { name, did, version });
147
+
148
+ manager.emit(BlockletEvents.downloadFailed, {
149
+ meta: { did },
150
+ error: {
151
+ message: err.message,
152
+ },
153
+ });
154
+ manager._createNotification(did, {
155
+ title: `${childrenToDownload.length > 1 ? `${childrenToDownload.length} components` : 'Component'} failed to download for ${getDisplayName(blocklet)}`,
156
+ description: `${childrenToDownload
157
+ .map((x) => `${x.meta.title}@${x.meta.version}`)
158
+ .join(', ')} download failed for ${getDisplayName(blocklet)}: ${err.message}`,
159
+ entityType: 'blocklet',
160
+ entityId: did,
161
+ severity: 'error',
162
+ });
163
+
164
+ // rollback on download failed
165
+ await statusLock.acquire('rollback-on-download-failed');
166
+
167
+ try {
168
+ const status = await states.blocklet.getBlockletStatus(did);
169
+ if ([BlockletStatus.downloading, BlockletStatus.waiting].includes(status)) {
170
+ await manager._rollback(postAction, did, oldBlocklet);
171
+ }
172
+ } catch (error) {
173
+ logger.error('Rollback blocklet failed on download failed', { postAction, name, did, version, error });
174
+ } finally {
175
+ await statusLock.releaseLock('rollback-on-download-failed');
176
+ }
177
+
178
+ if (throwOnError) {
179
+ throw err;
180
+ }
181
+
182
+ return;
183
+ }
184
+
185
+ // update status
186
+ await statusLock.acquire('download-update-status');
187
+ try {
188
+ await Promise.all(
189
+ blueGreenComponentIds.map(async (item) => {
190
+ if (item.componentDids.length === 0) {
191
+ return;
192
+ }
193
+ if ((await states.blocklet.getBlockletStatus(did)) !== BlockletStatus.downloading) {
194
+ throw new Error('blocklet status changed durning download');
195
+ }
196
+
197
+ if (postAction === INSTALL_ACTIONS.INSTALL) {
198
+ const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.installing, {
199
+ componentDids: item.componentDids,
200
+ isGreen: item.changeToGreen,
201
+ });
202
+ manager.emit(BlockletEvents.statusChange, state);
203
+ }
204
+
205
+ if (
206
+ [
207
+ INSTALL_ACTIONS.INSTALL_COMPONENT,
208
+ INSTALL_ACTIONS.UPGRADE_COMPONENT,
209
+ 'upgrade', // for backward compatibility
210
+ ].includes(postAction)
211
+ ) {
212
+ const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.upgrading, {
213
+ componentDids: item.componentDids,
214
+ isGreen: item.changeToGreen,
215
+ });
216
+ manager.emit(BlockletEvents.statusChange, state);
217
+ }
218
+ })
219
+ );
220
+ } catch (error) {
221
+ logger.error(error.message);
222
+ } finally {
223
+ await statusLock.releaseLock('download-update-status');
224
+ }
225
+
226
+ // install
227
+ try {
228
+ // install blocklet
229
+ if (postAction === INSTALL_ACTIONS.INSTALL) {
230
+ await _onInstall(manager, { blocklet, componentDids, context, oldBlocklet });
231
+
232
+ return;
233
+ }
234
+
235
+ // upgrade blocklet
236
+ if (
237
+ [
238
+ INSTALL_ACTIONS.INSTALL_COMPONENT,
239
+ INSTALL_ACTIONS.UPGRADE_COMPONENT,
240
+ 'upgrade', // for backward compatibility
241
+ ].includes(postAction)
242
+ ) {
243
+ logger.info('do upgrade blocklet', { did, version, postAction, componentDids });
244
+
245
+ try {
246
+ if (postAction === INSTALL_ACTIONS.UPGRADE_COMPONENT) {
247
+ if (process.env.ABT_NODE_DISABLE_BLUE_GREEN === 'true' || process.env.ABT_NODE_DISABLE_BLUE_GREEN === '1') {
248
+ await _upgradeBlocklet(manager, {
249
+ newBlocklet: blocklet,
250
+ oldBlocklet,
251
+ componentDids,
252
+ context,
253
+ action: postAction,
254
+ shouldCleanUploadFile,
255
+ url,
256
+ });
257
+ } else {
258
+ await blueGreenUpgradeBlocklet(
259
+ {
260
+ newBlocklet: blocklet,
261
+ oldBlocklet,
262
+ componentDids,
263
+ action: postAction,
264
+ shouldCleanUploadFile,
265
+ url,
266
+ },
267
+ context,
268
+ manager,
269
+ states
270
+ );
271
+ }
272
+ } else {
273
+ await _upgradeBlocklet(manager, {
274
+ newBlocklet: blocklet,
275
+ oldBlocklet,
276
+ componentDids,
277
+ action: postAction,
278
+ shouldCleanUploadFile,
279
+ url,
280
+ });
281
+ }
282
+
283
+ const newBlocklet = await manager.getBlocklet(did);
284
+
285
+ if (newBlocklet.controller) {
286
+ const eventType = 'install';
287
+
288
+ manager.reportComponentUsageQueue.push({
289
+ entity: 'blocklet',
290
+ action: 'reportComponentUsage',
291
+ time: new Date().toISOString(),
292
+ did,
293
+ componentDids,
294
+ eventType: 'install',
295
+ context,
296
+ });
297
+
298
+ logger.info('pushed reporting install components event job to queue', { did, componentDids, eventType });
299
+ }
300
+ } catch (err) {
301
+ logger.error('blocklet onUpgrade error', { error: err });
302
+ }
303
+ }
304
+ } catch (error) {
305
+ if (throwOnError) {
306
+ throw error;
307
+ }
308
+ }
309
+ }
310
+
311
+ module.exports = {
312
+ _downloadAndInstall,
313
+ };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Install Manager - Orchestrates blocklet installation operations
3
+ *
4
+ * This module re-exports functionality from specialized sub-modules:
5
+ * - install-core-manager.js: Core installation functions (install, _installBlocklet, _addBlocklet, _onInstall)
6
+ * - install-download-manager.js: Download and install operations (_downloadAndInstall)
7
+ * - install-upgrade-manager.js: Upgrade operations (_upgradeBlocklet, _runMigration, _onRestart, etc.)
8
+ */
9
+
10
+ const { _onInstall, _installBlocklet, _addBlocklet, install } = require('./install-core-manager');
11
+ const { _downloadAndInstall: _downloadAndInstallBase } = require('./install-download-manager');
12
+ const {
13
+ _onRestart,
14
+ _runMigration,
15
+ _cleanUploadFile,
16
+ _ensureDeletedChildrenInSettings,
17
+ _upgradeBlocklet,
18
+ } = require('./install-upgrade-manager');
19
+
20
+ // Wrap _downloadAndInstall to inject dependencies
21
+ function _downloadAndInstall(manager, params) {
22
+ return _downloadAndInstallBase(manager, params, { _onInstall, _upgradeBlocklet });
23
+ }
24
+
25
+ module.exports = {
26
+ _downloadAndInstall,
27
+ _onInstall,
28
+ _onRestart,
29
+ _installBlocklet,
30
+ _runMigration,
31
+ _cleanUploadFile,
32
+ _upgradeBlocklet,
33
+ _ensureDeletedChildrenInSettings,
34
+ _addBlocklet,
35
+ install,
36
+ };
@@ -0,0 +1,340 @@
1
+ /* eslint-disable no-await-in-loop */
2
+ const path = require('path');
3
+ const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager:install-upgrade');
4
+ const { INSTALL_ACTIONS, WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
5
+ const { BlockletStatus, BlockletEvents, BlockletInternalEvents } = require('@blocklet/constant');
6
+ const {
7
+ forEachComponentV2,
8
+ forEachComponentV2Sync,
9
+ hasStartEngine,
10
+ isExternalBlocklet,
11
+ } = require('@blocklet/meta/lib/util');
12
+ const { getComponentsInternalInfo } = require('@blocklet/meta/lib/blocklet');
13
+
14
+ const states = require('../../../states');
15
+ const {
16
+ getRuntimeEnvironments,
17
+ getDisplayName,
18
+ getComponentNamesWithVersion,
19
+ getHookArgs,
20
+ updateBlockletFallbackLogo,
21
+ } = require('../../../util/blocklet');
22
+ const checkNeedRunDocker = require('../../../util/docker/check-need-run-docker');
23
+ const { dockerExec } = require('../../../util/docker/docker-exec');
24
+ const runBlockletMigrationScripts = require('../../migration');
25
+ const getMigrationScripts = require('../../../util/get-migration-scripts');
26
+ const { removeUploadFile } = require('../../downloader/bundle-downloader');
27
+ const { blueGreenStartBlocklet } = require('../helper/blue-green-start-blocklet.js');
28
+
29
+ /**
30
+ * On restart callback
31
+ */
32
+ async function _onRestart(manager, { did, componentDids, context, operator }) {
33
+ // 检查 blocklet 是否存在,如果不存在则跳过重启任务
34
+ // 这可以防止在 blocklet 被删除后,队列中待处理的重启任务执行时出错
35
+ if (!(await manager.hasBlocklet({ did }))) {
36
+ logger.warn('skip restart job: blocklet not found', { did });
37
+ return;
38
+ }
39
+
40
+ if (process.env.ABT_NODE_DISABLE_BLUE_GREEN) {
41
+ await manager.stop({ did, componentDids, context, operator });
42
+ await manager.start({ did, componentDids, checkHealthImmediately: true });
43
+ return;
44
+ }
45
+ await blueGreenStartBlocklet({ did, componentDids, operator }, context, manager, states);
46
+ }
47
+
48
+ /**
49
+ * Run migration scripts
50
+ */
51
+ async function _runMigration(manager, { parallel, did, blocklet, oldBlocklet, componentDids }) {
52
+ logger.info('start migration on upgrading', { did, componentDids });
53
+ const oldVersions = {};
54
+ forEachComponentV2Sync(oldBlocklet, (b) => {
55
+ if (componentDids.includes(b.meta.did)) {
56
+ oldVersions[b.meta.did] = b.meta.version;
57
+ }
58
+ });
59
+ const nodeInfo = await states.node.read();
60
+ // 等待磁盘映射完成
61
+
62
+ const nodeEnvironments = await states.node.getEnvironments(nodeInfo);
63
+
64
+ const runMigration = async (b) => {
65
+ if (!componentDids.includes(b.meta.did)) {
66
+ return;
67
+ }
68
+ const nextEnv = getRuntimeEnvironments(b, nodeEnvironments, [blocklet]);
69
+ const subNeedRunDocker = await checkNeedRunDocker(b.meta, nextEnv, nodeInfo, isExternalBlocklet(blocklet));
70
+ const hookArgs = getHookArgs(b);
71
+
72
+ const scriptsDir = path.join(b.env.appDir, 'migration');
73
+ const scripts = await getMigrationScripts(scriptsDir, oldVersions[b.meta.did]);
74
+ if (!scripts.length) {
75
+ return;
76
+ }
77
+ try {
78
+ if (subNeedRunDocker) {
79
+ if (scripts.length) {
80
+ await dockerExec({
81
+ blocklet,
82
+ meta: b.meta,
83
+ env: nextEnv,
84
+ script: ['node docker-exec/run-script.cjs'].filter(Boolean).join(' && '),
85
+ hookName: 'migration',
86
+ nodeInfo,
87
+ runScriptDir: path.join(__dirname, '../../migration-dist'),
88
+ runScriptParams: {
89
+ blocklet: b,
90
+ appDir: b.env.appDir,
91
+ env: nextEnv,
92
+ oldVersion: oldVersions[b.meta.did],
93
+ newVersion: b.meta.version,
94
+ ...getHookArgs(b),
95
+ },
96
+ ...hookArgs,
97
+ timeout: 1000 * 120,
98
+ retry: 0,
99
+ });
100
+ }
101
+ } else {
102
+ await runBlockletMigrationScripts({
103
+ blocklet: b,
104
+ appDir: b.env.appDir,
105
+ env: nextEnv,
106
+ oldVersion: oldVersions[b.meta.did],
107
+ newVersion: b.meta.version,
108
+ ...hookArgs,
109
+ });
110
+ }
111
+ } catch (error) {
112
+ logger.error('Failed to run migration scripts', { appDid: did, title: b.meta.title, error });
113
+ // dev 模式下,migration 失败不抛出错误,只打印日志
114
+ if (process.env.ABT_NODE_DEV_MODE !== 'true') {
115
+ error.message = `Failed to run migration scripts for ${b.meta.title}: ${error.message}`;
116
+ throw error;
117
+ }
118
+ }
119
+ };
120
+ await forEachComponentV2(blocklet, runMigration, {
121
+ parallel,
122
+ concurrencyLimit: parallel ? 4 : 1,
123
+ });
124
+ logger.info('done migration on upgrading', { did, componentDids });
125
+ }
126
+
127
+ /**
128
+ * Clean upload file
129
+ */
130
+ function _cleanUploadFile(url) {
131
+ setTimeout(() => {
132
+ removeUploadFile(url);
133
+ }, 10000); // 10s 后删除上传的文件
134
+ }
135
+
136
+ /**
137
+ * Ensure deleted children in settings
138
+ */
139
+ async function _ensureDeletedChildrenInSettings(manager, blocklet) {
140
+ const { did } = blocklet.meta;
141
+
142
+ // TODO 不从 settings 中取值, 直接存在 extra 中
143
+ let deletedChildren = await states.blockletExtras.getSettings(did, 'children', []);
144
+ deletedChildren = deletedChildren.filter(
145
+ (x) => x.status === BlockletStatus.deleted && !blocklet.children.some((y) => y.meta.did === x.meta.did)
146
+ );
147
+
148
+ await states.blockletExtras.setSettings(did, { children: deletedChildren });
149
+ }
150
+
151
+ /**
152
+ * Upgrade blocklet
153
+ */
154
+ async function _upgradeBlocklet(
155
+ manager,
156
+ { newBlocklet, oldBlocklet, componentDids, action, context = {}, shouldCleanUploadFile, url }
157
+ ) {
158
+ const { meta, source, deployedFrom, children } = newBlocklet;
159
+ const { did, version, name } = meta;
160
+ const title = getDisplayName(newBlocklet);
161
+
162
+ try {
163
+ // delete old process
164
+ try {
165
+ await manager.deleteProcess({ did, componentDids }, context);
166
+ logger.info('delete blocklet process for upgrading', { did, componentDids });
167
+ } catch (err) {
168
+ logger.error('delete blocklet process for upgrading', { did, componentDids, error: err });
169
+ }
170
+
171
+ // update state
172
+ await states.blocklet.upgradeBlocklet({ meta, source, deployedFrom, children });
173
+ logger.info('updated blocklet for upgrading', { did, componentDids, source, name });
174
+ // ensure component status is upgrading
175
+ await states.blocklet.setBlockletStatus(did, BlockletStatus.upgrading, { componentDids });
176
+ await manager._setConfigsFromMeta(did);
177
+
178
+ // should ensure blocklet integrity
179
+ let blocklet = await manager.ensureBlocklet(did);
180
+
181
+ // Add environments
182
+ await manager._updateBlockletEnvironment(did);
183
+ blocklet = await manager.getBlocklet(did);
184
+
185
+ const nodeInfo = await states.node.read();
186
+ const nodeEnvironments = await states.node.getEnvironments(nodeInfo);
187
+ const env = getRuntimeEnvironments(blocklet, nodeEnvironments, {});
188
+ const needRunDocker = await checkNeedRunDocker(blocklet.meta, env, nodeInfo, isExternalBlocklet(blocklet));
189
+
190
+ await manager._runUserHook('preInstall', blocklet, context);
191
+ // post install
192
+ await manager._runUserHook('postInstall', blocklet, context);
193
+
194
+ await manager._runUserHook('preFlight', blocklet, context);
195
+
196
+ await _runMigration(manager, { parallel: !needRunDocker, did, blocklet, oldBlocklet, componentDids });
197
+
198
+ // handle component status
199
+ const runningDids = [];
200
+ const stoppedDids = [];
201
+ if (action === INSTALL_ACTIONS.INSTALL_COMPONENT) {
202
+ for (const componentDid of componentDids) {
203
+ const component = blocklet.children.find((x) => x.meta.did === componentDid);
204
+ if (hasStartEngine(component.meta)) {
205
+ // NOT start immediately for runnable component
206
+ stoppedDids.push(componentDid);
207
+ } else {
208
+ // Start immediately for resource component
209
+ runningDids.push(componentDid);
210
+ }
211
+ }
212
+ } else {
213
+ for (const componentDid of componentDids) {
214
+ const old = oldBlocklet.children.find((x) => x.meta.did === componentDid);
215
+ if (old?.status === BlockletStatus.running) {
216
+ runningDids.push(componentDid);
217
+ } else {
218
+ stoppedDids.push(componentDid);
219
+ }
220
+ }
221
+ }
222
+ if (runningDids.length) {
223
+ const initialized = !!blocklet.settings?.initialized;
224
+ if (initialized) {
225
+ try {
226
+ await manager.start({ did, componentDids: runningDids }, context);
227
+ logger.info('started blocklet for upgrading', { did, version, runningDids });
228
+ } catch (error) {
229
+ logger.error('failed to start blocklet for upgrading', { did, version, runningDids, error });
230
+ }
231
+ } else {
232
+ await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, { componentDids: runningDids });
233
+ logger.info('set blocklet as stopped since not initialized', { did, version, runningDids });
234
+ }
235
+ }
236
+ if (stoppedDids.length) {
237
+ const status = action === INSTALL_ACTIONS.INSTALL_COMPONENT ? BlockletStatus.installed : BlockletStatus.stopped;
238
+ await states.blocklet.setBlockletStatus(did, status, { componentDids: stoppedDids });
239
+ }
240
+
241
+ blocklet = await manager.getBlocklet(did, context);
242
+
243
+ await updateBlockletFallbackLogo(blocklet);
244
+
245
+ await manager._updateDependents(did);
246
+
247
+ manager.refreshListCache();
248
+
249
+ try {
250
+ manager.emit(BlockletEvents.upgraded, { blocklet, context }); // trigger router refresh
251
+
252
+ const isInstallAction = action === INSTALL_ACTIONS.INSTALL_COMPONENT;
253
+ const notificationEvent = isInstallAction ? BlockletEvents.componentInstalled : BlockletEvents.componentUpgraded;
254
+ const actionName = isInstallAction ? 'installed' : 'upgraded';
255
+
256
+ manager.emit(notificationEvent, { ...blocklet, componentDids, oldBlocklet, context });
257
+
258
+ const upgradeLength = componentDids?.length || 0;
259
+
260
+ manager._createNotification(did, {
261
+ title: `${upgradeLength > 1 ? `${upgradeLength} components` : 'Component'} ${actionName} successfully for ${title}`,
262
+ description: `${getComponentNamesWithVersion(
263
+ newBlocklet,
264
+ componentDids
265
+ )} is ${actionName} successfully for ${title}`,
266
+ action: `/blocklets/${did}/overview`,
267
+ blockletDashboardAction: `${WELLKNOWN_SERVICE_PATH_PREFIX}/admin/blocklets`,
268
+ entityType: 'blocklet',
269
+ entityId: did,
270
+ severity: 'success',
271
+ });
272
+
273
+ if (shouldCleanUploadFile && url) {
274
+ _cleanUploadFile(url);
275
+ }
276
+
277
+ if (isInstallAction && process.env.NODE_ENV !== 'test') {
278
+ manager.start({ did, componentDids });
279
+ }
280
+ } catch (error) {
281
+ logger.error('emit upgrade notification failed', { name, version, error });
282
+ }
283
+
284
+ // Update dynamic component meta in blocklet settings
285
+ await _ensureDeletedChildrenInSettings(manager, blocklet);
286
+
287
+ await manager._rollbackCache.remove({ did: blocklet.meta.did });
288
+
289
+ if (oldBlocklet.status === BlockletStatus.running) {
290
+ manager.emit(
291
+ action === INSTALL_ACTIONS.INSTALL_COMPONENT
292
+ ? BlockletInternalEvents.componentInstalled
293
+ : BlockletInternalEvents.componentUpgraded,
294
+ {
295
+ appDid: blocklet.appDid,
296
+ components: getComponentsInternalInfo(blocklet).filter((c) => componentDids.includes(c.did)),
297
+ }
298
+ );
299
+ }
300
+ manager.configSynchronizer.throttledSyncAppConfig(blocklet);
301
+
302
+ return blocklet;
303
+ } catch (err) {
304
+ const b = await manager._rollback(action, did, oldBlocklet);
305
+ logger.error('failed to upgrade blocklet', { did, version, name, error: err });
306
+
307
+ manager.emit(BlockletEvents.updated, { ...b, context });
308
+
309
+ const actionName = action === INSTALL_ACTIONS.INSTALL_COMPONENT ? 'install' : 'upgrade';
310
+ const notificationEvent =
311
+ action === INSTALL_ACTIONS.INSTALL_COMPONENT
312
+ ? BlockletEvents.componentInstallFailed
313
+ : BlockletEvents.componentUpgradeFailed;
314
+
315
+ manager.emit(notificationEvent, {
316
+ blocklet: { ...newBlocklet, componentDids, error: { message: err.message } },
317
+ context,
318
+ });
319
+
320
+ const upgradeLength = componentDids?.length || 0;
321
+ manager._createNotification(did, {
322
+ title: `${upgradeLength > 1 ? `${upgradeLength} components` : 'Component'} failed to ${actionName} for ${title}`,
323
+ description: `${getComponentNamesWithVersion(newBlocklet, componentDids)} ${actionName} failed for ${title}: ${
324
+ err.message
325
+ }.`,
326
+ entityType: 'blocklet',
327
+ entityId: did,
328
+ severity: 'error',
329
+ });
330
+ throw err;
331
+ }
332
+ }
333
+
334
+ module.exports = {
335
+ _onRestart,
336
+ _runMigration,
337
+ _cleanUploadFile,
338
+ _ensureDeletedChildrenInSettings,
339
+ _upgradeBlocklet,
340
+ };