@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,124 @@
1
+ const get = require('lodash/get');
2
+ const { forEachBlocklet, forEachComponentV2, isExternalBlocklet } = require('@blocklet/meta/lib/util');
3
+
4
+ const states = require('../../../states');
5
+ const {
6
+ getRuntimeEnvironments,
7
+ getConfigFromPreferences,
8
+ getAppConfigsFromComponent,
9
+ getHookArgs,
10
+ } = require('../../../util/blocklet');
11
+ const hooks = require('../../hooks');
12
+ const checkNeedRunDocker = require('../../../util/docker/check-need-run-docker');
13
+ const { dockerExec } = require('../../../util/docker/docker-exec');
14
+ const { installExternalDependencies } = require('../../../util/install-external-dependencies');
15
+ const { getBlocklet } = require('../../../util/blocklet');
16
+
17
+ const afterRunScripts = new Set(['postStart', 'preStop', 'preConfig', 'preUninstall']);
18
+
19
+ /**
20
+ * Run user hook
21
+ * @param {Object} manager - BlockletManager instance
22
+ * @param {string} name - Hook name
23
+ * @param {Object} blocklet - Blocklet state
24
+ * @param {Object} context - Context
25
+ * @returns {Promise<void>}
26
+ */
27
+ async function _runUserHook(manager, name, blocklet, context) {
28
+ const nodeInfo = await states.node.read();
29
+ const nodeEnvironments = await states.node.getEnvironments(nodeInfo);
30
+ const hookFn = async (b, { ancestors }) => {
31
+ const env = getRuntimeEnvironments(b, nodeEnvironments, ancestors);
32
+ const needRunDocker = await checkNeedRunDocker(b.meta, env, nodeInfo, isExternalBlocklet(blocklet));
33
+ const hookArgs = getHookArgs(b);
34
+
35
+ if (name === 'preInstall') {
36
+ await installExternalDependencies({
37
+ appDir: b.env.appDir,
38
+ nodeInfo,
39
+ });
40
+ }
41
+
42
+ if (needRunDocker) {
43
+ const script = b.meta.scripts?.[name];
44
+ if (script && afterRunScripts.has(name)) {
45
+ return dockerExec({
46
+ blocklet,
47
+ meta: b.meta,
48
+ script,
49
+ hookName: name,
50
+ nodeInfo,
51
+ env,
52
+ ...hookArgs,
53
+ });
54
+ }
55
+ return null;
56
+ }
57
+
58
+ return hooks[name](b.meta.title, {
59
+ hooks: Object.assign(b.meta.hooks || {}, b.meta.scripts || {}),
60
+ env,
61
+ appDir: b.env.appDir,
62
+ did: blocklet.meta.did, // root blocklet did
63
+ notification: states.notification,
64
+ context,
65
+ teamManager: manager.teamManager,
66
+ ...hookArgs,
67
+ });
68
+ };
69
+
70
+ await forEachBlocklet(blocklet, hookFn, { parallel: true, concurrencyLimit: 4 });
71
+ }
72
+
73
+ /**
74
+ * Set configs from meta
75
+ * @param {Object} manager - BlockletManager instance
76
+ * @param {string} did - Blocklet DID
77
+ * @param {string} [childDid] - Child blocklet DID
78
+ * @returns {Promise<void>}
79
+ */
80
+ async function _setConfigsFromMeta(manager, did, childDid) {
81
+ const blocklet = await getBlocklet({ states, dataDirs: manager.dataDirs, did });
82
+
83
+ const setConfig = async (app, component) => {
84
+ const b = component || app;
85
+ const environments = [...get(b.meta, 'environments', []), ...getConfigFromPreferences(b)];
86
+
87
+ // write configs to db
88
+ await states.blockletExtras.setConfigs([app.meta.did, component?.meta?.did].filter(Boolean), environments);
89
+
90
+ if (component) {
91
+ const envsInApp = await states.blockletExtras.getConfigs([blocklet.meta.did]);
92
+ const envsInComponent = await states.blockletExtras.getConfigs([app.meta.did, component.meta.did]);
93
+
94
+ const configs = getAppConfigsFromComponent({ environments }, envsInApp, envsInComponent);
95
+ if (configs.length) {
96
+ await states.blockletExtras.setConfigs(app.meta.did, configs);
97
+ }
98
+ }
99
+
100
+ // chain config
101
+ await manager._ensureAppChainConfig(
102
+ blocklet.meta.did,
103
+ environments.map((x) => ({ key: x.name, value: x.default })),
104
+ { force: false }
105
+ );
106
+ };
107
+
108
+ if (!childDid) {
109
+ await setConfig(blocklet);
110
+ await forEachComponentV2(blocklet, async (component) => {
111
+ await setConfig(blocklet, component);
112
+ });
113
+ } else {
114
+ const component = blocklet.children.find((x) => x.meta.did === childDid);
115
+
116
+ await setConfig(blocklet, component);
117
+ }
118
+ }
119
+
120
+ module.exports = {
121
+ _runUserHook,
122
+ _setConfigsFromMeta,
123
+ afterRunScripts,
124
+ };
@@ -0,0 +1,95 @@
1
+ const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager:install-component');
2
+ const { isInServerlessMode } = require('@abtnode/util/lib/serverless');
3
+ const { updateComponentDid } = require('@abtnode/util/lib/upload-component');
4
+
5
+ const states = require('../../../states');
6
+ const { installComponentFromUpload } = require('../helper/install-component-from-upload');
7
+ const { installComponentFromUrl } = require('../helper/install-component-from-url');
8
+
9
+ /**
10
+ * Install component
11
+ * @param {Object} manager - BlockletManager instance
12
+ * @param {Object} params
13
+ * @param {Object} context
14
+ * @returns {Promise<Object>}
15
+ */
16
+ async function installComponent(
17
+ manager,
18
+ {
19
+ rootDid,
20
+ mountPoint,
21
+ url,
22
+ file,
23
+ did,
24
+ diffVersion,
25
+ deleteSet,
26
+ title,
27
+ name,
28
+ configs,
29
+ sync,
30
+ downloadTokenList,
31
+ onlyRequired,
32
+ dist,
33
+ },
34
+ context = {}
35
+ ) {
36
+ logger.debug('start install component', { rootDid, mountPoint, url });
37
+ if (file) {
38
+ // TODO: 如何触发这种场景?
39
+ const info = await states.node.read();
40
+ if (isInServerlessMode(info)) {
41
+ throw new Error("Can't install component in serverless-mode server via upload");
42
+ }
43
+
44
+ return installComponentFromUpload({
45
+ rootDid,
46
+ mountPoint,
47
+ file,
48
+ did,
49
+ diffVersion,
50
+ deleteSet,
51
+ context,
52
+ states,
53
+ manager,
54
+ dist,
55
+ });
56
+ }
57
+
58
+ if (url) {
59
+ // 如果是 file 协议的 URI,那么就认为是上传的文件
60
+ const isUploadFile = url.startsWith('file://');
61
+ // 需要判断是否是上传的文件,如果是上传的文件,需要判断是否要更新 did,走升级的逻辑
62
+ // 不需要阻塞流程,如果失败了,就新建一个组件
63
+ try {
64
+ if (isUploadFile && did) {
65
+ await updateComponentDid(url, did);
66
+ }
67
+ } catch (error) {
68
+ logger.error('update component did failed', { error });
69
+ }
70
+
71
+ return installComponentFromUrl({
72
+ rootDid,
73
+ mountPoint,
74
+ url,
75
+ context,
76
+ title,
77
+ did,
78
+ name,
79
+ configs,
80
+ sync,
81
+ downloadTokenList,
82
+ states,
83
+ manager,
84
+ onlyRequired,
85
+ isUploadFile,
86
+ });
87
+ }
88
+
89
+ // should not be here
90
+ throw new Error('Unknown source');
91
+ }
92
+
93
+ module.exports = {
94
+ installComponent,
95
+ };
@@ -0,0 +1,448 @@
1
+ /* eslint-disable no-await-in-loop */
2
+ const fs = require('fs-extra');
3
+ const path = require('path');
4
+ const omit = require('lodash/omit');
5
+ const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager:install-core');
6
+ const { generateRandomString } = require('@abtnode/models/lib/util');
7
+ const { sign } = require('@arcblock/jwt');
8
+ const { BLOCKLET_INSTALL_TYPE, INSTALL_ACTIONS, WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
9
+ const { BlockletStatus, BlockletEvents, fromBlockletSource } = require('@blocklet/constant');
10
+ const { isGatewayBlocklet } = require('@blocklet/meta/lib/util');
11
+ const {
12
+ BlockletGroup,
13
+ BlockletSource,
14
+ BLOCKLET_MODES,
15
+ BLOCKLET_INTERFACE_TYPE_WEB,
16
+ BLOCKLET_INTERFACE_PUBLIC,
17
+ BLOCKLET_DYNAMIC_PATH_PREFIX,
18
+ BLOCKLET_DEFAULT_PORT_NAME,
19
+ BLOCKLET_INTERFACE_PROTOCOL_HTTP,
20
+ BLOCKLET_DEFAULT_PATH_REWRITE,
21
+ BLOCKLET_DEFAULT_VERSION,
22
+ BLOCKLET_LATEST_SPEC_VERSION,
23
+ BLOCKLET_META_FILE,
24
+ BLOCKLET_CONFIGURABLE_KEY,
25
+ } = require('@blocklet/constant');
26
+ const { update: updateMetaFile } = require('@blocklet/meta/lib/file');
27
+ const { validateBlocklet, filterRequiredComponents, getBundleDir } = require('../../../util/blocklet');
28
+
29
+ const states = require('../../../states');
30
+ const launcher = require('../../../util/launcher');
31
+ const {
32
+ ensureAppLogo,
33
+ updateBlockletFallbackLogo,
34
+ getPackConfig,
35
+ getPackComponent,
36
+ copyPackImages,
37
+ getTypeFromInstallParams,
38
+ } = require('../../../util/blocklet');
39
+ const { removeBackup } = require('../../storage/utils/disk');
40
+ const { getWalletAppNotification } = require('../../../util/wallet-app-notification');
41
+ const { installApplicationFromBackup } = require('../helper/install-application-from-backup');
42
+ const { installApplicationFromGeneral } = require('../helper/install-application-from-general');
43
+ const util = require('../../../util');
44
+
45
+ /**
46
+ * On install callback
47
+ */
48
+ async function _onInstall(manager, { blocklet, componentDids, context, oldBlocklet }) {
49
+ const { meta } = blocklet;
50
+ const { did, version } = meta;
51
+ logger.info('do install blocklet', { did, version });
52
+ try {
53
+ const installedBlocklet = await _installBlocklet(manager, {
54
+ did,
55
+ componentDids,
56
+ context,
57
+ oldBlocklet,
58
+ });
59
+
60
+ if (context.startImmediately && installedBlocklet?.settings.initialized) {
61
+ try {
62
+ logger.info('start blocklet after installed', { did });
63
+ await manager.start({ did, checkHealthImmediately: true, componentDids });
64
+ } catch (error) {
65
+ logger.warn('attempt to start immediately failed', { did, error });
66
+ }
67
+ }
68
+
69
+ return installedBlocklet;
70
+ } catch (error) {
71
+ logger.error('blocklet onInstall error', { error });
72
+ return null;
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Install blocklet
78
+ */
79
+ async function _installBlocklet(manager, { did, oldBlocklet, componentDids, context, createNotification = true }) {
80
+ try {
81
+ // should ensure blocklet integrity
82
+ let blocklet = await manager.ensureBlocklet(did);
83
+ const { meta, source, deployedFrom } = blocklet;
84
+
85
+ // ensure delete process
86
+ try {
87
+ await manager.deleteProcess({ did, shouldUpdateBlockletStatus: false }, context);
88
+ logger.info('ensure delete blocklet process for installing', { did, name: meta.name });
89
+ } catch (err) {
90
+ logger.error('ensure delete blocklet process failed for installing', { did, name: meta.name, error: err });
91
+ }
92
+
93
+ // Add environments
94
+ await manager._updateBlockletEnvironment(meta.did);
95
+ blocklet = await manager.getBlocklet(did);
96
+
97
+ // Add initialize authentication settings
98
+ await manager.migrateBlockletAuthentication({ did });
99
+
100
+ // pre install
101
+ await manager._runUserHook('preInstall', blocklet, context);
102
+
103
+ // post install
104
+ await manager._runUserHook('postInstall', blocklet, context);
105
+
106
+ // pre flight
107
+ await manager._runUserHook('preFlight', blocklet, context);
108
+
109
+ await states.blocklet.setInstalledAt(did);
110
+
111
+ await states.blocklet.setBlockletStatus(did, BlockletStatus.installed, { componentDids });
112
+ blocklet = await manager.getBlocklet(did);
113
+ logger.info('blocklet installed', { source, did: meta.did });
114
+
115
+ // logo
116
+ await ensureAppLogo(blocklet, manager.installDir);
117
+ await updateBlockletFallbackLogo(blocklet);
118
+
119
+ // Init db
120
+ await manager.teamManager.initTeam(blocklet.meta.did);
121
+
122
+ // Update dependents
123
+ await manager._updateDependents(did);
124
+ blocklet = await manager.getBlocklet(did);
125
+
126
+ // Inject pack configs
127
+ const packConfig = await getPackConfig(blocklet);
128
+ if (packConfig) {
129
+ const packComponent = getPackComponent(blocklet);
130
+
131
+ // 同步图片资源
132
+ await copyPackImages({
133
+ appDataDir: path.join(manager.dataDirs.data, blocklet.meta.name),
134
+ packDir: packComponent.env.appDir,
135
+ packConfig,
136
+ });
137
+
138
+ // 同步 App 导航配置
139
+ await manager.configNavigations({ did, navigations: packConfig.navigations });
140
+
141
+ // 同步 App 品牌相关图片配置
142
+ const configObj = packConfig.configObj || {};
143
+ for (const key of util.APP_CONFIG_IMAGE_KEYS) {
144
+ const value = configObj[key];
145
+ if (value) {
146
+ await manager.config({
147
+ did: [blocklet.meta.did],
148
+ configs: [{ key, value }],
149
+ skipHook: true,
150
+ skipDidDocument: true,
151
+ skipEmitEvents: true,
152
+ });
153
+ }
154
+ }
155
+
156
+ // 同步各子组件配置
157
+ for (const { did: componentDid, configs } of packConfig.components || []) {
158
+ if (blocklet.children.some((x) => x.meta.did === componentDid)) {
159
+ await manager.config({
160
+ did: [blocklet.meta.did, componentDid],
161
+ configs,
162
+ skipHook: true,
163
+ skipDidDocument: true,
164
+ skipEmitEvents: true,
165
+ });
166
+ } else {
167
+ logger.error('component not found', { did: componentDid });
168
+ }
169
+ }
170
+ }
171
+
172
+ manager.emit(BlockletEvents.installed, { blocklet, context });
173
+
174
+ // Update dynamic component meta in blocklet settings
175
+ await _ensureDeletedChildrenInSettings(manager, blocklet);
176
+
177
+ if (context?.downloadTokenList?.length) {
178
+ await states.blocklet.updateBlocklet(did, {
179
+ tokens: {
180
+ downloadTokenList: context.downloadTokenList,
181
+ },
182
+ });
183
+ }
184
+
185
+ if (blocklet.controller && process.env.NODE_ENV !== 'test') {
186
+ let isNFTConsumed = false;
187
+ if (blocklet.controller.launcherSessionId && blocklet.controller.launcherUrl) {
188
+ await launcher.consumeLauncherSession({ params: blocklet.controller, blocklet });
189
+ isNFTConsumed = true;
190
+ } else if (blocklet.controller.nftId) {
191
+ await launcher.consumeServerlessNFT({ nftId: blocklet.controller.nftId, blocklet });
192
+ isNFTConsumed = true;
193
+ }
194
+
195
+ if (isNFTConsumed) {
196
+ await states.blockletExtras.updateByDid(did, {
197
+ controller: { ...blocklet.controller, consumedAt: new Date().toISOString() },
198
+ });
199
+ manager.emit(BlockletEvents.nftConsumed, { blocklet, context });
200
+ }
201
+ }
202
+
203
+ if (createNotification) {
204
+ // 发送通知不阻塞后续安装流程
205
+ try {
206
+ const walletExtra = await getWalletAppNotification(blocklet);
207
+
208
+ manager._createNotification(did, {
209
+ title: 'Blocklet Installed',
210
+ description: `Blocklet ${meta.title} is installed successfully. (Source: ${
211
+ deployedFrom || fromBlockletSource(source)
212
+ })`,
213
+ action: `/blocklets/${did}/overview`,
214
+ blockletDashboardAction: `${WELLKNOWN_SERVICE_PATH_PREFIX}/admin/blocklets`,
215
+ entityType: 'blocklet',
216
+ entityId: did,
217
+ severity: 'success',
218
+ extra: { wallet: walletExtra },
219
+ });
220
+ } catch (error) {
221
+ logger.error('create installed notification failed', { error, did });
222
+ }
223
+ }
224
+
225
+ await manager._rollbackCache.remove({ did: blocklet.meta.did });
226
+ removeBackup(manager.dataDirs.data, blocklet.appDid);
227
+ return blocklet;
228
+ } catch (err) {
229
+ const { meta } = await states.blocklet.getBlocklet(did);
230
+ const { name, version } = meta;
231
+ logger.error('failed to install blocklet', { name, did, version, error: err });
232
+ try {
233
+ await manager._rollback(INSTALL_ACTIONS.INSTALL, did, oldBlocklet);
234
+ manager.emit(BlockletEvents.installFailed, {
235
+ meta: { did },
236
+ error: {
237
+ message: err.message,
238
+ },
239
+ });
240
+ manager._createNotification(did, {
241
+ title: 'Blocklet Install Failed',
242
+ description: `Blocklet ${meta.title} install failed with error: ${err.message}`,
243
+ entityType: 'blocklet',
244
+ entityId: did,
245
+ severity: 'error',
246
+ });
247
+ } catch (e) {
248
+ logger.error('failed to remove blocklet on install error', { did: meta.did, error: e });
249
+ }
250
+
251
+ throw err;
252
+ }
253
+ }
254
+
255
+ /**
256
+ * Ensure deleted children in settings (imported from install-upgrade-manager)
257
+ */
258
+ async function _ensureDeletedChildrenInSettings(manager, blocklet) {
259
+ const { did } = blocklet.meta;
260
+
261
+ // TODO 不从 settings 中取值, 直接存在 extra 中
262
+ let deletedChildren = await states.blockletExtras.getSettings(did, 'children', []);
263
+ deletedChildren = deletedChildren.filter(
264
+ (x) => x.status === BlockletStatus.deleted && !blocklet.children.some((y) => y.meta.did === x.meta.did)
265
+ );
266
+
267
+ await states.blockletExtras.setSettings(did, { children: deletedChildren });
268
+ }
269
+
270
+ /**
271
+ * Add blocklet
272
+ */
273
+ async function _addBlocklet(
274
+ manager,
275
+ {
276
+ component,
277
+ mode = BLOCKLET_MODES.PRODUCTION,
278
+ name,
279
+ did,
280
+ title,
281
+ description,
282
+ skSource = '',
283
+ folder,
284
+ onlyRequired,
285
+ requirements,
286
+ context,
287
+ }
288
+ ) {
289
+ const environments = component?.meta?.environments || [];
290
+
291
+ const meta = {
292
+ name,
293
+ did,
294
+ title: title || component?.meta?.title || '',
295
+ description: description || component?.meta?.description || '',
296
+ version: BLOCKLET_DEFAULT_VERSION,
297
+ group: BlockletGroup.gateway,
298
+ interfaces: [
299
+ {
300
+ type: BLOCKLET_INTERFACE_TYPE_WEB,
301
+ name: BLOCKLET_INTERFACE_PUBLIC,
302
+ path: BLOCKLET_DEFAULT_PATH_REWRITE,
303
+ prefix: BLOCKLET_DYNAMIC_PATH_PREFIX,
304
+ port: BLOCKLET_DEFAULT_PORT_NAME,
305
+ protocol: BLOCKLET_INTERFACE_PROTOCOL_HTTP,
306
+ },
307
+ ],
308
+ specVersion: BLOCKLET_LATEST_SPEC_VERSION,
309
+ environments,
310
+ timeout: {
311
+ start: process.env.NODE_ENV === 'test' ? 10 : 60,
312
+ },
313
+ ...(requirements ? { requirements } : {}),
314
+ };
315
+
316
+ let children;
317
+ if (mode === BLOCKLET_MODES.DEVELOPMENT) {
318
+ children = isGatewayBlocklet(component.meta) ? [] : [component];
319
+ } else {
320
+ children = component ? await manager._getChildrenForInstallation(component) : [];
321
+ if (onlyRequired) {
322
+ children = filterRequiredComponents(component, children);
323
+ }
324
+ }
325
+
326
+ // FIXME @linchen
327
+ // 当应用本身是容器时, 下载这个容器, 因为容器可能除 blocklet.yml 额外的文件
328
+ // 本身就是容器的应用, 在容器中添加额外文件可能不是合理的做法
329
+ // 容器只在安装时有效, 安装后容器无法升级
330
+ const containerSourceUrl =
331
+ component && component.meta.group === BlockletGroup.gateway && component.meta.dist && component.bundleSource?.url;
332
+
333
+ if (containerSourceUrl) {
334
+ meta.bundleDid = component.meta.did;
335
+ meta.bundleName = component.meta.name;
336
+ meta.version = component.meta.version;
337
+ meta.dist = component.meta.dist;
338
+ meta.logo = component.meta.logo;
339
+ } else if (children[0]?.meta?.logo) {
340
+ meta.logo = children[0].meta.logo;
341
+ } else if (mode === BLOCKLET_MODES.DEVELOPMENT && isGatewayBlocklet(component.meta)) {
342
+ meta.logo = component.meta.logo;
343
+ }
344
+
345
+ await validateBlocklet({ meta, children });
346
+
347
+ // fake install bundle
348
+ if (!containerSourceUrl) {
349
+ const bundleDir = getBundleDir(manager.installDir, meta);
350
+ fs.mkdirSync(bundleDir, { recursive: true });
351
+ updateMetaFile(path.join(bundleDir, BLOCKLET_META_FILE), meta);
352
+ }
353
+
354
+ // add blocklet to db
355
+ const params = {
356
+ meta,
357
+ source: BlockletSource.custom,
358
+ children,
359
+ mode,
360
+ externalSkSource: skSource,
361
+ };
362
+
363
+ if (containerSourceUrl) {
364
+ params.source = BlockletSource.url;
365
+ params.deployedFrom = containerSourceUrl;
366
+ }
367
+
368
+ if (mode === BLOCKLET_MODES.DEVELOPMENT && isGatewayBlocklet(component.meta)) {
369
+ params.source = BlockletSource.local;
370
+ params.deployedFrom = folder;
371
+ }
372
+
373
+ const blocklet = await states.blocklet.addBlocklet(params);
374
+
375
+ // set chain type
376
+ const chainTypeEnv = {
377
+ key: BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_CHAIN_TYPE,
378
+ value: 'arcblock',
379
+ shared: true,
380
+ };
381
+ const customChainType = environments.find((x) => x.name === 'CHAIN_TYPE')?.default;
382
+ if (['eth', 'ethereum'].includes(customChainType)) {
383
+ chainTypeEnv.value = 'ethereum';
384
+ }
385
+
386
+ await states.blockletExtras.setConfigs(blocklet.meta.did, [chainTypeEnv]);
387
+ await states.blockletExtras.setSettings(blocklet.meta.did, { session: { salt: generateRandomString(16) } });
388
+
389
+ manager.emit(BlockletEvents.added, { blocklet, context });
390
+
391
+ return blocklet;
392
+ }
393
+
394
+ /**
395
+ * Install blocklet
396
+ * @param {Object} manager - BlockletManager instance
397
+ * @param {Object} params - Install parameters
398
+ * @param {Object} context - Context
399
+ * @returns {Promise<Object>}
400
+ */
401
+ async function install(manager, params, context = {}) {
402
+ const type = getTypeFromInstallParams(params);
403
+ logger.info('install blocklet', { type, params: omit(params, ['appSk']), user: context.user });
404
+
405
+ const { appSk } = params;
406
+ if (!appSk) {
407
+ throw new Error('appSk is required');
408
+ }
409
+
410
+ if (!params.controller && context?.user?.controller) {
411
+ params.controller = context.user.controller;
412
+ }
413
+
414
+ const info = await states.node.read();
415
+
416
+ // Note: if you added new header here, please change core/state/lib/blocklet/downloader/bundle-downloader.js to use that header
417
+ context.headers = Object.assign(context?.headers || {}, {
418
+ 'x-server-did': info.did,
419
+ 'x-server-public-key': info.pk,
420
+ 'x-server-signature': await sign(info.did, info.sk, {
421
+ exp: (Date.now() + 5 * 60 * 1000) / 1000,
422
+ }),
423
+ });
424
+ context.downloadTokenList = params.downloadTokenList || [];
425
+
426
+ if (typeof context.startImmediately === 'undefined') {
427
+ context.startImmediately = !!params.startImmediately;
428
+ }
429
+
430
+ if (type === BLOCKLET_INSTALL_TYPE.RESTORE) {
431
+ const { url } = params;
432
+ return installApplicationFromBackup({ url, appSk, context, manager, states });
433
+ }
434
+
435
+ if ([BLOCKLET_INSTALL_TYPE.URL, BLOCKLET_INSTALL_TYPE.STORE, BLOCKLET_INSTALL_TYPE.CREATE].includes(type)) {
436
+ return installApplicationFromGeneral({ ...params, type, context, manager, states });
437
+ }
438
+
439
+ // should not be here
440
+ throw new Error(`install from ${type} is not supported`);
441
+ }
442
+
443
+ module.exports = {
444
+ _onInstall,
445
+ _installBlocklet,
446
+ _addBlocklet,
447
+ install,
448
+ };