@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,311 @@
1
+ /* eslint-disable no-await-in-loop */
2
+ const omit = require('lodash/omit');
3
+ const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager:env-config');
4
+ const { sanitizeTag } = require('@abtnode/util/lib/sanitize');
5
+ const { getBlockletInfo } = require('@blocklet/meta/lib/info');
6
+ const { sendToUser } = require('@blocklet/sdk/lib/util/send-notification');
7
+ const { encrypt } = require('@blocklet/sdk/lib/security');
8
+ const { getComponentApiKey } = require('@abtnode/util/lib/blocklet');
9
+ const { environmentNameSchema } = require('@blocklet/meta/lib/schema');
10
+ const {
11
+ BlockletEvents,
12
+ BlockletInternalEvents,
13
+ BLOCKLET_CONFIGURABLE_KEY,
14
+ BLOCKLET_PREFERENCE_PREFIX,
15
+ } = require('@blocklet/constant');
16
+ const { isPreferenceKey } = require('@blocklet/meta/lib/util');
17
+ const { shouldSyncFederated } = require('@abtnode/auth/lib/util/federated');
18
+
19
+ const states = require('../../../states');
20
+ const {
21
+ getRuntimeEnvironments,
22
+ isExternalBlocklet,
23
+ validateAppConfig,
24
+ isRotatingAppSk,
25
+ getConfigsFromInput,
26
+ getHookArgs,
27
+ } = require('../../../util/blocklet');
28
+ const hooks = require('../../hooks');
29
+ const launcher = require('../../../util/launcher');
30
+ const checkNeedRunDocker = require('../../../util/docker/check-need-run-docker');
31
+ const { dockerExec } = require('../../../util/docker/docker-exec');
32
+ const { getWalletAppNotification } = require('../../../util/wallet-app-notification');
33
+
34
+ /**
35
+ * Config blocklet environment
36
+ * @param {Object} manager - BlockletManager instance
37
+ * @param {Object} params
38
+ * @param {Object} context
39
+ * @returns {Promise<Object>}
40
+ */
41
+ async function config(manager, { did, configs: newConfigs, skipHook, skipDidDocument, skipEmitEvents }, context) {
42
+ // todo: skipDidDocument will be deleted
43
+ if (!Array.isArray(newConfigs)) {
44
+ throw new Error('configs list is not an array');
45
+ }
46
+
47
+ // 对用户输入的配置进行 XSS 清理
48
+ const sanitizedConfigs = newConfigs
49
+ .map((c) => ({
50
+ ...c,
51
+ value: sanitizeTag(c.value),
52
+ }))
53
+ .filter((x) => (x.key.startsWith(BLOCKLET_PREFERENCE_PREFIX) ? omit(x, 'shared') : x));
54
+
55
+ const tmpDids = Array.isArray(did) ? did : [did];
56
+ const [rootDid, childDid] = tmpDids;
57
+ const rootMetaDid = await states.blocklet.getBlockletMetaDid(rootDid);
58
+
59
+ logger.info('config blocklet', { rootDid, rootMetaDid, childDid });
60
+
61
+ const ancestors = [];
62
+ let blocklet = await manager.getBlocklet(rootDid);
63
+ if (childDid) {
64
+ ancestors.push(blocklet);
65
+ blocklet = blocklet.children.find((x) => x.meta.did === childDid);
66
+ if (!blocklet) {
67
+ throw new Error('Child blocklet does not exist', { rootDid, childDid });
68
+ }
69
+ }
70
+
71
+ const configObj = {};
72
+ const ignoredKeys = [];
73
+ for (const x of sanitizedConfigs) {
74
+ if (['CHAIN_TYPE', 'BLOCKLET_APP_CHAIN_TYPE'].includes(x.key)) {
75
+ throw new Error(`${x.key} should not be changed`);
76
+ } else if (x.custom === true) {
77
+ // custom key
78
+ await environmentNameSchema.validateAsync(x.key);
79
+ } else if (BLOCKLET_CONFIGURABLE_KEY[x.key] && x.key.startsWith('BLOCKLET_')) {
80
+ // app key
81
+ if (childDid) {
82
+ logger.error(`Cannot set ${x.key} to child blocklet`, { rootDid, childDid });
83
+ throw new Error(`Cannot set ${x.key} to child blocklet`);
84
+ }
85
+ await validateAppConfig(x, states);
86
+ } else if (!BLOCKLET_CONFIGURABLE_KEY[x.key] && !isPreferenceKey(x)) {
87
+ const hasEnvInMeta = (b) => (b.meta.environments || []).some((y) => y.name === x.key);
88
+ if (!hasEnvInMeta(blocklet)) {
89
+ // forbid unknown environment items
90
+ if (
91
+ // config should in component.meta.environments
92
+ childDid ||
93
+ // config should in app.meta.environments and or in one of component.meta.environments
94
+ !(blocklet.children || []).some(hasEnvInMeta)
95
+ ) {
96
+ logger.warn(`unknown environment item: ${x.key}`, { rootDid, childDid, config: x });
97
+ ignoredKeys.push(x.key);
98
+ continue;
99
+ }
100
+ }
101
+ }
102
+
103
+ configObj[x.key] = x.value;
104
+ }
105
+
106
+ const finalConfigs = sanitizedConfigs.filter((x) => !ignoredKeys.includes(x.key));
107
+ const willAppSkChange = isRotatingAppSk(finalConfigs, blocklet.configs, blocklet.externalSk);
108
+
109
+ // NOTICE: cannot use appDid as did param because appDid will become old appDid in alsoKnownAs and cannot get blocklet by the old appDid
110
+ if (willAppSkChange && rootDid !== rootMetaDid) {
111
+ throw new Error(`Please use app meta did (${rootMetaDid}) as did param when config appSk`);
112
+ }
113
+
114
+ // run hook
115
+ if (!skipHook) {
116
+ // FIXME: we should also call preConfig for child blocklets
117
+ const nodeInfo = await states.node.read();
118
+ const nodeEnvironments = await states.node.getEnvironments(nodeInfo);
119
+ const nextEnv = { ...getRuntimeEnvironments(blocklet, nodeEnvironments, ancestors), ...configObj };
120
+ const needRunDocker = await checkNeedRunDocker(blocklet.meta, nextEnv, nodeInfo, isExternalBlocklet(blocklet));
121
+ const hookArgs = getHookArgs(blocklet);
122
+ if (needRunDocker) {
123
+ if (blocklet.meta.scripts?.preConfig) {
124
+ await dockerExec({
125
+ blocklet: {
126
+ status: blocklet.status,
127
+ meta: {
128
+ did: rootDid,
129
+ },
130
+ },
131
+ meta: blocklet.meta,
132
+ nodeInfo,
133
+ env: nextEnv,
134
+ script: blocklet.meta.scripts.preConfig,
135
+ retry: 0,
136
+ hookName: 'preConfig',
137
+ ...hookArgs,
138
+ });
139
+ }
140
+ } else {
141
+ await hooks.preConfig(blocklet.meta.title, {
142
+ appDir: blocklet.env.appDir,
143
+ hooks: Object.assign(blocklet.meta.hooks || {}, blocklet.meta.scripts || {}),
144
+ exitOnError: true,
145
+ env: nextEnv,
146
+ did,
147
+ context,
148
+ teamManager: manager.teamManager,
149
+ ...hookArgs,
150
+ });
151
+ }
152
+ }
153
+
154
+ Object.assign(blocklet.configObj, configObj);
155
+
156
+ // update db
157
+ if (childDid) {
158
+ const { sharedConfigs, selfConfigs } = getConfigsFromInput(finalConfigs, blocklet.configs);
159
+
160
+ if (sharedConfigs.length) {
161
+ await states.blockletExtras.setConfigs([rootMetaDid], sharedConfigs);
162
+ }
163
+ if (selfConfigs.length) {
164
+ await states.blockletExtras.setConfigs([rootMetaDid, childDid], selfConfigs);
165
+ }
166
+ } else {
167
+ await states.blockletExtras.setConfigs([rootMetaDid], finalConfigs);
168
+ }
169
+
170
+ if (willAppSkChange) {
171
+ const info = await states.node.read();
172
+ const { wallet } = getBlockletInfo(blocklet, info.sk);
173
+ const migratedFrom = Array.isArray(blocklet.migratedFrom) ? blocklet.migratedFrom : [];
174
+ await states.blocklet.updateBlocklet(rootDid, {
175
+ migratedFrom: [
176
+ ...migratedFrom,
177
+ { appSk: wallet.secretKey, appDid: wallet.address, at: new Date().toISOString() },
178
+ ],
179
+ });
180
+ }
181
+
182
+ // Reload nginx to make sure did-space can embed content from this app
183
+ if (finalConfigs.find((x) => x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_BACKUP_ENDPOINT)?.value) {
184
+ manager.emit(BlockletEvents.spaceConnected, blocklet);
185
+ }
186
+
187
+ // update blocklet meta
188
+ if (blocklet.structVersion && !childDid) {
189
+ const changedTitle = finalConfigs.find((x) => x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_NAME)?.value;
190
+ const changedDescription = finalConfigs.find(
191
+ (x) => x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_DESCRIPTION
192
+ )?.value;
193
+
194
+ if (changedTitle || changedDescription) {
195
+ await states.blocklet.updateBlocklet(rootDid, {
196
+ meta: {
197
+ ...blocklet.meta,
198
+ title: changedTitle || blocklet.meta.title,
199
+ description: changedDescription || blocklet.meta.description,
200
+ },
201
+ });
202
+ }
203
+ }
204
+
205
+ // chain config
206
+ await manager._ensureAppChainConfig(rootMetaDid, finalConfigs);
207
+
208
+ await manager._updateBlockletEnvironment(rootDid);
209
+
210
+ // response
211
+ const newState = await manager.getBlocklet(rootDid);
212
+
213
+ if (willAppSkChange && !skipDidDocument) {
214
+ await manager._updateDidDocument(newState);
215
+ }
216
+
217
+ if (!skipEmitEvents) {
218
+ if (!childDid && !finalConfigs.some((x) => x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK)) {
219
+ await manager.configSynchronizer.throttledSyncAppConfig(blocklet.meta.did);
220
+ manager.emit(BlockletInternalEvents.appConfigChanged, {
221
+ appDid: rootDid,
222
+ configs: finalConfigs.map((x) => ({ key: x.key, value: x.value })),
223
+ });
224
+ }
225
+
226
+ const serverSk = (await states.node.read()).sk;
227
+ if (childDid) {
228
+ const configs = JSON.stringify(finalConfigs.map((x) => ({ key: x.key, value: x.value })));
229
+ await manager.configSynchronizer.syncComponentConfig(childDid, rootDid, { serverSk });
230
+
231
+ manager.emit(BlockletInternalEvents.componentConfigChanged, {
232
+ appDid: rootDid,
233
+ componentDid: childDid,
234
+ configs: encrypt(configs, getComponentApiKey({ serverSk, app: ancestors[0], component: blocklet }), childDid),
235
+ });
236
+
237
+ // broadcast shared configs to all components through app channel
238
+ const sharedConfigs = finalConfigs.filter((x) => x.shared);
239
+ if (sharedConfigs.length) {
240
+ manager.emit(BlockletInternalEvents.appConfigChanged, {
241
+ appDid: rootDid,
242
+ configs: sharedConfigs.map((x) => ({ key: x.key, value: x.value })),
243
+ });
244
+ }
245
+ }
246
+
247
+ manager.emit(BlockletEvents.updated, { ...newState, context });
248
+
249
+ try {
250
+ const observableConfigs = [
251
+ BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_URL,
252
+ BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_NAME,
253
+ BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_DESCRIPTION,
254
+ BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_LOGO,
255
+ BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_LOGO_SQUARE,
256
+ BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_LOGO_SQUARE_DARK,
257
+ BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_LOGO_RECT,
258
+ BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_LOGO_RECT_DARK,
259
+ BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_LOGO_FAVICON,
260
+ ];
261
+
262
+ const shouldSendWalletNotification = observableConfigs.some((x) => finalConfigs.some((y) => y.key === x));
263
+
264
+ if (shouldSendWalletNotification) {
265
+ // notify launcher config changed
266
+ launcher.notifyBlockletUpdated(blocklet);
267
+ if (shouldSyncFederated(blocklet)) {
268
+ try {
269
+ // 通知站点群更新信息
270
+ manager.syncFederatedConfig({ did });
271
+ } catch (err) {
272
+ logger.error('Failed to sync federated config after updated config', { did, error: err });
273
+ }
274
+ }
275
+ }
276
+
277
+ const receiverUsers = await manager.teamManager.getOwnerAndAdminUsers(newState.appPid, true);
278
+ const receivers = receiverUsers.map((x) => x.did);
279
+
280
+ if (shouldSendWalletNotification && receivers.length) {
281
+ const nodeInfo = await states.node.read();
282
+ const blockletInfo = getBlockletInfo(newState, nodeInfo.sk);
283
+ const { actions, attachments } = await getWalletAppNotification(newState, blockletInfo);
284
+
285
+ const sender = {
286
+ appDid: blockletInfo.wallet.address,
287
+ appSk: blockletInfo.wallet.secretKey,
288
+ };
289
+
290
+ await sendToUser(
291
+ receivers,
292
+ {
293
+ title: 'Blocklet Config Changed',
294
+ body: `Blocklet ${blockletInfo.name} config changed`,
295
+ attachments,
296
+ actions,
297
+ },
298
+ sender
299
+ );
300
+ }
301
+ } catch (error) {
302
+ logger.error('Failed to send wallet notification after updated config', { did, error });
303
+ }
304
+ }
305
+
306
+ return newState;
307
+ }
308
+
309
+ module.exports = {
310
+ config,
311
+ };