@abtnode/core 1.17.8-beta-20260108-224855-28496abb → 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 (66) 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/router/helper.js +5 -1
  51. package/lib/util/blocklet/app-utils.js +192 -0
  52. package/lib/util/blocklet/blocklet-loader.js +258 -0
  53. package/lib/util/blocklet/config-manager.js +232 -0
  54. package/lib/util/blocklet/did-document.js +240 -0
  55. package/lib/util/blocklet/environment.js +555 -0
  56. package/lib/util/blocklet/health-check.js +449 -0
  57. package/lib/util/blocklet/install-utils.js +365 -0
  58. package/lib/util/blocklet/logo.js +57 -0
  59. package/lib/util/blocklet/meta-utils.js +269 -0
  60. package/lib/util/blocklet/port-manager.js +141 -0
  61. package/lib/util/blocklet/process-manager.js +504 -0
  62. package/lib/util/blocklet/runtime-info.js +105 -0
  63. package/lib/util/blocklet/validation.js +418 -0
  64. package/lib/util/blocklet.js +98 -3066
  65. package/lib/util/wallet-app-notification.js +40 -0
  66. package/package.json +22 -22
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Configuration Manager Module
3
+ *
4
+ * Functions for managing blocklet configurations, preferences, and pack settings
5
+ * Extracted from blocklet.js for better modularity
6
+ */
7
+
8
+ const fs = require('fs-extra');
9
+ const path = require('node:path');
10
+
11
+ const {
12
+ BlockletGroup,
13
+ BLOCKLET_PREFERENCE_FILE,
14
+ BLOCKLET_PREFERENCE_PREFIX,
15
+ BLOCKLET_RESOURCE_DIR,
16
+ } = require('@blocklet/constant');
17
+ const { isEnvShareable } = require('@blocklet/meta/lib/util');
18
+
19
+ const { APP_CONFIG_IMAGE_KEYS } = require('../index');
20
+
21
+ /**
22
+ * Helper to convert preference schema properties to config format
23
+ * @param {object} properties - Schema properties
24
+ * @param {Array} result - Result array to populate
25
+ */
26
+ const fromProperty2Config = (properties = {}, result) => {
27
+ Object.keys(properties).forEach((key) => {
28
+ const prop = properties[key];
29
+ if (prop.properties && ['ArrayTable', 'ArrayCards'].includes(prop['x-component']) === false) {
30
+ fromProperty2Config(prop.properties, result);
31
+ } else if (prop['x-decorator'] === 'FormItem') {
32
+ const secure = prop['x-component'] === 'Password';
33
+ result.push({
34
+ default: prop.default || '',
35
+ description: prop.title || key,
36
+ name: `${BLOCKLET_PREFERENCE_PREFIX}${key}`,
37
+ required: prop.required || false,
38
+ secure,
39
+ // eslint-disable-next-line no-nested-ternary
40
+ shared: secure ? false : typeof prop.shared === 'undefined' ? true : prop.shared,
41
+ });
42
+ }
43
+ });
44
+ };
45
+
46
+ /**
47
+ * Get config from blocklet preferences schema file
48
+ * @param {object} blocklet - Blocklet object with env.appDir
49
+ * @returns {Array} Array of config objects
50
+ */
51
+ const getConfigFromPreferences = (blocklet) => {
52
+ const result = [];
53
+ const schemaFile = path.join(blocklet.env.appDir, BLOCKLET_PREFERENCE_FILE);
54
+ if (fs.existsSync(schemaFile)) {
55
+ try {
56
+ const schema = JSON.parse(fs.readFileSync(schemaFile, 'utf8'));
57
+ fromProperty2Config(schema.schema?.properties, result);
58
+ } catch {
59
+ // do nothing
60
+ }
61
+ }
62
+
63
+ return result;
64
+ };
65
+
66
+ /**
67
+ * Get app configs from component that should be shared
68
+ * @param {object} meta - Component meta
69
+ * @param {Array} configsInApp - Configs already in app
70
+ * @param {Array} configsInComponent - Configs in component
71
+ * @returns {Array} Array of shared config objects
72
+ */
73
+ const getAppConfigsFromComponent = (meta, configsInApp = [], configsInComponent = []) => {
74
+ const configs = [];
75
+ for (const configInMeta of meta?.environments || []) {
76
+ if (isEnvShareable(configInMeta)) {
77
+ const configInApp = (configsInApp || []).find((x) => x.key === configInMeta.name);
78
+ if (!configInApp) {
79
+ const configInComponent = configsInComponent.find((y) => y.key === configInMeta.name);
80
+ if (configInComponent && isEnvShareable(configInComponent)) {
81
+ configs.push(configInComponent);
82
+ }
83
+ }
84
+ }
85
+ }
86
+ return configs;
87
+ };
88
+
89
+ /**
90
+ * Separate configs into shared and self configs
91
+ * @param {Array} configs - Input configs
92
+ * @param {Array} oldConfigs - Existing configs
93
+ * @returns {{ sharedConfigs: Array, selfConfigs: Array }}
94
+ */
95
+ const getConfigsFromInput = (configs = [], oldConfigs = []) => {
96
+ const sharedConfigs = [];
97
+ const selfConfigs = [];
98
+
99
+ configs.forEach((config) => {
100
+ const oldConfig = oldConfigs.find((y) => y.key === config.key);
101
+ if (!config.key.startsWith(BLOCKLET_PREFERENCE_PREFIX) && (isEnvShareable(config) || isEnvShareable(oldConfig))) {
102
+ sharedConfigs.push(config);
103
+ } else {
104
+ selfConfigs.push(config);
105
+ }
106
+ });
107
+
108
+ return { sharedConfigs, selfConfigs };
109
+ };
110
+
111
+ /**
112
+ * Remove app configs if no component uses them
113
+ * @param {Array} componentConfigs - Configs from removed component
114
+ * @param {object} app - App blocklet
115
+ * @param {object} blockletExtraState - Blocklet extra state manager
116
+ */
117
+ const removeAppConfigsFromComponent = async (componentConfigs, app, blockletExtraState) => {
118
+ const appConfigs = app.configs || [];
119
+ const remainedConfigs = [].concat(...(app.children || []).map((x) => x.configs || []));
120
+ const removedAppConfigs = [];
121
+
122
+ componentConfigs.forEach((config) => {
123
+ const appConfig = appConfigs.find((x) => x.key === config.key);
124
+ if (
125
+ appConfig &&
126
+ !appConfig.custom &&
127
+ !(app.meta.environments || []).find((x) => x.name === config.key) &&
128
+ !remainedConfigs.find((x) => x.key === config.key && isEnvShareable(x))
129
+ ) {
130
+ removedAppConfigs.push({ key: appConfig.key, value: undefined });
131
+ }
132
+ });
133
+
134
+ if (removedAppConfigs.length) {
135
+ await blockletExtraState.setConfigs(app.meta.did, removedAppConfigs);
136
+ }
137
+ };
138
+
139
+ /**
140
+ * Get pack component from app children
141
+ * @param {object} app - App blocklet
142
+ * @returns {object|undefined} Pack component
143
+ */
144
+ const getPackComponent = (app) => {
145
+ return (app?.children || []).find((x) => x.meta.group === BlockletGroup.pack);
146
+ };
147
+
148
+ /**
149
+ * Get pack config from pack component
150
+ * @param {object} app - App blocklet
151
+ * @returns {object|null} Pack config or null
152
+ */
153
+ const getPackConfig = (app) => {
154
+ const packComponent = getPackComponent(app);
155
+ if (!packComponent) {
156
+ return null;
157
+ }
158
+
159
+ const resource = (packComponent.meta.resource?.bundles || []).find(
160
+ (x) => x.did === packComponent.meta.did && x.type === 'config'
161
+ );
162
+
163
+ if (!resource) {
164
+ return null;
165
+ }
166
+
167
+ const { appDir } = packComponent.env;
168
+ const configFile = path.join(appDir, BLOCKLET_RESOURCE_DIR, resource.did, resource.type, 'config.json');
169
+
170
+ if (!fs.existsSync(configFile)) {
171
+ return null;
172
+ }
173
+
174
+ return fs.readJSON(configFile);
175
+ };
176
+
177
+ /**
178
+ * Copy pack images from pack dir to app data dir
179
+ * @param {object} options - Options
180
+ * @param {string} options.appDataDir - App data directory
181
+ * @param {string} options.packDir - Pack directory
182
+ * @param {object} options.packConfig - Pack config with navigations and configObj
183
+ */
184
+ const copyPackImages = async ({ appDataDir, packDir, packConfig = {} }) => {
185
+ const mediaDir = path.join(appDataDir, 'media', 'blocklet-service');
186
+ const { navigations = [], configObj = {} } = packConfig;
187
+ await fs.ensureDir(mediaDir);
188
+
189
+ // Filter bottom navigation items with icons
190
+ const bottomNavItems = navigations.filter((item) => {
191
+ const sections = Array.isArray(item.section) ? item.section : [item.section];
192
+ return sections.includes('bottomNavigation') && item.icon;
193
+ });
194
+
195
+ // Copy tabbar navigation icons
196
+ if (bottomNavItems.length > 0) {
197
+ await Promise.all(
198
+ bottomNavItems.map(async (item) => {
199
+ const iconFileName = path.basename(item.icon);
200
+ const iconInImages = path.join(packDir, 'images', iconFileName);
201
+
202
+ if (fs.existsSync(iconInImages)) {
203
+ await fs.copy(iconInImages, path.join(mediaDir, iconFileName));
204
+ }
205
+ })
206
+ );
207
+ }
208
+
209
+ // Copy brand-related images
210
+ await Promise.all(
211
+ APP_CONFIG_IMAGE_KEYS.map(async (key) => {
212
+ const value = configObj[key];
213
+ if (value) {
214
+ const imgFile = path.join(packDir, 'images', value);
215
+ if (fs.existsSync(imgFile)) {
216
+ await fs.copy(imgFile, path.join(appDataDir, value));
217
+ }
218
+ }
219
+ })
220
+ );
221
+ };
222
+
223
+ module.exports = {
224
+ fromProperty2Config,
225
+ getConfigFromPreferences,
226
+ getAppConfigsFromComponent,
227
+ getConfigsFromInput,
228
+ removeAppConfigsFromComponent,
229
+ getPackComponent,
230
+ getPackConfig,
231
+ copyPackImages,
232
+ };
@@ -0,0 +1,240 @@
1
+ /**
2
+ * DID Document Module
3
+ *
4
+ * Functions for managing blocklet DID documents
5
+ * Extracted from blocklet.js for better modularity
6
+ */
7
+
8
+ const isEmpty = require('lodash/isEmpty');
9
+
10
+ const { toDid, toBuffer } = require('@ocap/util');
11
+ const { fromPublicKey } = require('@ocap/wallet');
12
+ const logger = require('@abtnode/logger')('@abtnode/core:util:blocklet:did-document');
13
+ const { isCustomDomain } = require('@abtnode/util/lib/url-evaluation');
14
+ const { isInServerlessMode } = require('@abtnode/util/lib/serverless');
15
+ const md5 = require('@abtnode/util/lib/md5');
16
+ const didDocument = require('@abtnode/util/lib/did-document');
17
+ const { SLOT_FOR_IP_DNS_SITE } = require('@abtnode/constant');
18
+ const { BLOCKLET_CONFIGURABLE_KEY, fromBlockletStatus } = require('@blocklet/constant');
19
+ const { getBlockletInfo } = require('@blocklet/meta/lib/info');
20
+ const { fixAvatar } = require('@blocklet/sdk/lib/util/user');
21
+
22
+ const { getServerDidDomain, replaceDomainSlot } = require('../index');
23
+ const { getFromCache: getAccessibleExternalNodeIp } = require('../get-accessible-external-node-ip');
24
+
25
+ /**
26
+ * Check if SLP domain should be enabled based on mode
27
+ * @param {string} mode - Node mode
28
+ * @returns {boolean}
29
+ */
30
+ const shouldEnableSlpDomain = (mode) => {
31
+ if (process.env.ABT_NODE_ENABLE_SLP_DOMAIN === 'true') {
32
+ return true;
33
+ }
34
+
35
+ if (process.env.ABT_NODE_ENABLE_SLP_DOMAIN === 'false') {
36
+ return false;
37
+ }
38
+
39
+ return isInServerlessMode({ mode });
40
+ };
41
+
42
+ /**
43
+ * Get SLP DID from server DID and app permanent ID
44
+ * @param {string} serverDid - Server DID
45
+ * @param {string} appPid - App permanent ID
46
+ * @returns {string} SLP DID address
47
+ */
48
+ const getSlpDid = (serverDid, appPid) => {
49
+ if (!serverDid || !appPid) {
50
+ throw new Error('serverDid and appPid is required');
51
+ }
52
+
53
+ const buffer = Buffer.concat([toBuffer(serverDid), toBuffer(appPid)]);
54
+ const md5Str = md5(buffer);
55
+
56
+ const wallet = fromPublicKey(md5Str);
57
+ return wallet.address;
58
+ };
59
+
60
+ /**
61
+ * Get blocklet known as (alias DIDs)
62
+ * @param {object} blocklet - Blocklet object
63
+ * @returns {string[]} Array of DIDs
64
+ */
65
+ const getBlockletKnownAs = (blocklet) => {
66
+ const alsoKnownAs = [blocklet.appDid];
67
+ if (Array.isArray(blocklet.migratedFrom)) {
68
+ blocklet.migratedFrom.filter((x) => x.appDid !== blocklet.appPid).forEach((x) => alsoKnownAs.push(x.appDid));
69
+ }
70
+
71
+ return alsoKnownAs.filter(Boolean).map(toDid);
72
+ };
73
+
74
+ /**
75
+ * Publish DID document for a blocklet
76
+ * @param {object} options - Options
77
+ * @param {object} options.blocklet - Blocklet object
78
+ * @param {object} options.ownerInfo - Owner info
79
+ * @param {object} options.nodeInfo - Node info
80
+ */
81
+ // eslint-disable-next-line require-await
82
+ const publishDidDocument = async ({ blocklet, ownerInfo, nodeInfo }) => {
83
+ const alsoKnownAs = getBlockletKnownAs(blocklet);
84
+ logger.debug('updateDidDocument blocklet info', { blocklet });
85
+
86
+ const { wallet } = getBlockletInfo(blocklet, nodeInfo.sk);
87
+ const { mode, did: serverDid } = nodeInfo;
88
+
89
+ let slpDid = null;
90
+ const enableSlpDomain = shouldEnableSlpDomain(mode);
91
+ if (enableSlpDomain) {
92
+ slpDid = getSlpDid(serverDid, blocklet.appPid);
93
+
94
+ if (alsoKnownAs.indexOf(slpDid) === -1) {
95
+ alsoKnownAs.push(toDid(slpDid));
96
+ }
97
+ }
98
+
99
+ logger.info('update did document', {
100
+ blockletDid: blocklet.meta.did,
101
+ alsoKnownAs,
102
+ slpDid,
103
+ daemonDidDomain: getServerDidDomain(nodeInfo),
104
+ didRegistryUrl: nodeInfo.didRegistry,
105
+ domain: nodeInfo.didDomain,
106
+ slpDomain: nodeInfo.slpDomain,
107
+ });
108
+
109
+ const name = blocklet.meta?.title || blocklet.meta?.name;
110
+ const state = fromBlockletStatus(blocklet.status);
111
+
112
+ let launcher;
113
+ if (!isEmpty(blocklet.controller)) {
114
+ launcher = {
115
+ did: toDid(blocklet.controller.did || nodeInfo.registerInfo.appPid), // 目前 controller 没有 launcher 的元信息, 默认在 nodeInfo 中存储
116
+ name: blocklet.controller.launcherName || nodeInfo.registerInfo.appName || '',
117
+ url: blocklet.controller.launcherUrl || nodeInfo.registerInfo.appUrl || '',
118
+ userDid: toDid(blocklet.controller.nftOwner),
119
+ };
120
+ }
121
+
122
+ const isPrimaryDomain = (d) => {
123
+ const appUrl = blocklet.environments.find((x) => x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_URL)?.value;
124
+ try {
125
+ const url = new URL(appUrl);
126
+ return url.hostname === d;
127
+ } catch (error) {
128
+ logger.error('failed to get primary domain', { error, domain: d, appUrl });
129
+ return false;
130
+ }
131
+ };
132
+
133
+ const domains = await Promise.all(
134
+ (blocklet.site?.domainAliases || []).map(async (item) => {
135
+ let type = isCustomDomain(item.value) ? 'custom' : 'internal';
136
+ // 如果域名是 appUrl,则设置为 primary
137
+ if (isPrimaryDomain(item.value)) {
138
+ type = 'primary';
139
+ }
140
+
141
+ if (item.value.includes(SLOT_FOR_IP_DNS_SITE)) {
142
+ const nodeIp = await getAccessibleExternalNodeIp();
143
+ item.value = replaceDomainSlot({ domain: item.value, nodeIp });
144
+ }
145
+
146
+ return {
147
+ type,
148
+ host: item.value,
149
+ url: `https://${item.value}`,
150
+ source: 'dnsRecords', // 固定为 dnsRecords
151
+ };
152
+ })
153
+ );
154
+
155
+ let owner;
156
+ if (ownerInfo) {
157
+ owner = {
158
+ did: toDid(ownerInfo.did),
159
+ name: ownerInfo.fullName,
160
+ avatar: fixAvatar(ownerInfo.avatar),
161
+ };
162
+ }
163
+
164
+ return didDocument.updateBlockletDocument({
165
+ blocklet,
166
+ wallet,
167
+ alsoKnownAs,
168
+ slpDid,
169
+ daemonDidDomain: getServerDidDomain(nodeInfo),
170
+ didRegistryUrl: nodeInfo.didRegistry,
171
+ domain: nodeInfo.didDomain,
172
+ slpDomain: nodeInfo.slpDomain,
173
+ serverDid,
174
+ blockletServerVersion: nodeInfo.version,
175
+ name,
176
+ state,
177
+ owner,
178
+ launcher,
179
+ domains,
180
+ });
181
+ };
182
+
183
+ /**
184
+ * Update DID document for a blocklet
185
+ * @param {object} options - Options
186
+ * @param {string} options.did - Blocklet DID
187
+ * @param {object} options.nodeInfo - Node info
188
+ * @param {object} options.teamManager - Team manager
189
+ * @param {object} options.states - State managers
190
+ */
191
+ const updateDidDocument = async ({ did, nodeInfo, teamManager, states }) => {
192
+ const blocklet = await states.blocklet.getBlocklet(did);
193
+ const blockletExtra = await states.blockletExtras.findOne({ did });
194
+
195
+ blocklet.site = await states.site.findOneByBlocklet(did);
196
+ blocklet.settings = await states.blockletExtras.getSettings(did);
197
+ blocklet.controller = blockletExtra?.controller;
198
+
199
+ const ownerDid = blocklet.settings?.owner?.did;
200
+ let ownerInfo;
201
+ if (ownerDid) {
202
+ logger.info('get owner info', { ownerDid, teamDid: blocklet.appPid });
203
+ const userState = await teamManager.getUserState(blocklet.appPid);
204
+ ownerInfo = await userState.getUser(ownerDid);
205
+ }
206
+
207
+ return publishDidDocument({ blocklet, ownerInfo, nodeInfo });
208
+ };
209
+
210
+ /**
211
+ * Update DID document state only (e.g., to 'deleted') without fetching from database
212
+ * Used when blocklet is being removed and database data may not be available
213
+ * @param {object} options - Options
214
+ * @param {string} options.did - Blocklet DID
215
+ * @param {object} options.blocklet - Blocklet object
216
+ * @param {string} options.state - New state
217
+ * @param {object} options.nodeInfo - Node info
218
+ */
219
+ const updateDidDocumentStateOnly = ({ did, blocklet, state, nodeInfo }) => {
220
+ logger.debug('update did document state only', { did, state });
221
+
222
+ const { wallet } = getBlockletInfo(blocklet, nodeInfo.sk);
223
+
224
+ return didDocument.updateBlockletStateOnly({
225
+ did,
226
+ state,
227
+ didRegistryUrl: nodeInfo.didRegistry,
228
+ wallet,
229
+ blockletServerVersion: nodeInfo.version,
230
+ });
231
+ };
232
+
233
+ module.exports = {
234
+ shouldEnableSlpDomain,
235
+ getSlpDid,
236
+ getBlockletKnownAs,
237
+ publishDidDocument,
238
+ updateDidDocument,
239
+ updateDidDocumentStateOnly,
240
+ };