@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.
- package/lib/api/team/access-key-manager.js +104 -0
- package/lib/api/team/invitation-manager.js +461 -0
- package/lib/api/team/notification-manager.js +189 -0
- package/lib/api/team/oauth-manager.js +60 -0
- package/lib/api/team/org-crud-manager.js +202 -0
- package/lib/api/team/org-manager.js +56 -0
- package/lib/api/team/org-member-manager.js +403 -0
- package/lib/api/team/org-query-manager.js +126 -0
- package/lib/api/team/org-resource-manager.js +186 -0
- package/lib/api/team/passport-manager.js +670 -0
- package/lib/api/team/rbac-manager.js +335 -0
- package/lib/api/team/session-manager.js +540 -0
- package/lib/api/team/store-manager.js +198 -0
- package/lib/api/team/tag-manager.js +230 -0
- package/lib/api/team/user-auth-manager.js +132 -0
- package/lib/api/team/user-manager.js +78 -0
- package/lib/api/team/user-query-manager.js +299 -0
- package/lib/api/team/user-social-manager.js +354 -0
- package/lib/api/team/user-update-manager.js +224 -0
- package/lib/api/team/verify-code-manager.js +161 -0
- package/lib/api/team.js +439 -3287
- package/lib/blocklet/manager/disk/auth-manager.js +68 -0
- package/lib/blocklet/manager/disk/backup-manager.js +288 -0
- package/lib/blocklet/manager/disk/cleanup-manager.js +157 -0
- package/lib/blocklet/manager/disk/component-manager.js +83 -0
- package/lib/blocklet/manager/disk/config-manager.js +191 -0
- package/lib/blocklet/manager/disk/controller-manager.js +64 -0
- package/lib/blocklet/manager/disk/delete-reset-manager.js +328 -0
- package/lib/blocklet/manager/disk/download-manager.js +96 -0
- package/lib/blocklet/manager/disk/env-config-manager.js +311 -0
- package/lib/blocklet/manager/disk/federated-manager.js +651 -0
- package/lib/blocklet/manager/disk/hook-manager.js +124 -0
- package/lib/blocklet/manager/disk/install-component-manager.js +95 -0
- package/lib/blocklet/manager/disk/install-core-manager.js +448 -0
- package/lib/blocklet/manager/disk/install-download-manager.js +313 -0
- package/lib/blocklet/manager/disk/install-manager.js +36 -0
- package/lib/blocklet/manager/disk/install-upgrade-manager.js +340 -0
- package/lib/blocklet/manager/disk/job-manager.js +467 -0
- package/lib/blocklet/manager/disk/lifecycle-manager.js +26 -0
- package/lib/blocklet/manager/disk/notification-manager.js +343 -0
- package/lib/blocklet/manager/disk/query-manager.js +562 -0
- package/lib/blocklet/manager/disk/settings-manager.js +507 -0
- package/lib/blocklet/manager/disk/start-manager.js +611 -0
- package/lib/blocklet/manager/disk/stop-restart-manager.js +292 -0
- package/lib/blocklet/manager/disk/update-manager.js +153 -0
- package/lib/blocklet/manager/disk.js +669 -5796
- package/lib/blocklet/manager/helper/blue-green-start-blocklet.js +5 -0
- package/lib/blocklet/manager/lock.js +18 -0
- package/lib/event/index.js +28 -24
- package/lib/util/blocklet/app-utils.js +192 -0
- package/lib/util/blocklet/blocklet-loader.js +258 -0
- package/lib/util/blocklet/config-manager.js +232 -0
- package/lib/util/blocklet/did-document.js +240 -0
- package/lib/util/blocklet/environment.js +555 -0
- package/lib/util/blocklet/health-check.js +449 -0
- package/lib/util/blocklet/install-utils.js +365 -0
- package/lib/util/blocklet/logo.js +57 -0
- package/lib/util/blocklet/meta-utils.js +269 -0
- package/lib/util/blocklet/port-manager.js +141 -0
- package/lib/util/blocklet/process-manager.js +504 -0
- package/lib/util/blocklet/runtime-info.js +105 -0
- package/lib/util/blocklet/validation.js +418 -0
- package/lib/util/blocklet.js +98 -3066
- package/lib/util/wallet-app-notification.js +40 -0
- package/package.json +22 -22
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment Module
|
|
3
|
+
*
|
|
4
|
+
* Functions for managing blocklet environment variables and configuration
|
|
5
|
+
* Extracted from blocklet.js for better modularity
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs-extra');
|
|
9
|
+
const path = require('node:path');
|
|
10
|
+
const get = require('lodash/get');
|
|
11
|
+
const isNil = require('lodash/isNil');
|
|
12
|
+
const toLower = require('lodash/toLower');
|
|
13
|
+
const { slugify } = require('transliteration');
|
|
14
|
+
|
|
15
|
+
const { toHex } = require('@ocap/util');
|
|
16
|
+
const { fromSecretKey } = require('@ocap/wallet');
|
|
17
|
+
const { urlPathFriendly } = require('@blocklet/meta/lib/url-path-friendly');
|
|
18
|
+
const logger = require('@abtnode/logger')('@abtnode/core:util:blocklet:environment');
|
|
19
|
+
const { formatEnv } = require('@abtnode/util/lib/security');
|
|
20
|
+
const { AIGNE_CONFIG_ENCRYPT_SALT } = require('@abtnode/constant');
|
|
21
|
+
const { decrypt } = require('@abtnode/util/lib/security');
|
|
22
|
+
const {
|
|
23
|
+
BlockletSource,
|
|
24
|
+
BLOCKLET_MODES,
|
|
25
|
+
BLOCKLET_ENTRY_FILE,
|
|
26
|
+
BLOCKLET_DEFAULT_PORT_NAME,
|
|
27
|
+
BLOCKLET_CONFIGURABLE_KEY,
|
|
28
|
+
BLOCKLET_TENANT_MODES,
|
|
29
|
+
PROJECT,
|
|
30
|
+
} = require('@blocklet/constant');
|
|
31
|
+
const { getBlockletInfo } = require('@blocklet/meta/lib/info');
|
|
32
|
+
const { getApplicationWallet: getBlockletWallet } = require('@blocklet/meta/lib/wallet');
|
|
33
|
+
const {
|
|
34
|
+
forEachBlockletSync,
|
|
35
|
+
findWebInterface,
|
|
36
|
+
getSharedConfigObj,
|
|
37
|
+
getComponentName,
|
|
38
|
+
getBlockletAppIdList,
|
|
39
|
+
hasStartEngine,
|
|
40
|
+
} = require('@blocklet/meta/lib/util');
|
|
41
|
+
const { getComponentsInternalInfo } = require('@blocklet/meta/lib/blocklet');
|
|
42
|
+
const { getBlockletEngine } = require('@blocklet/meta/lib/engine');
|
|
43
|
+
const { getDidDomainForBlocklet } = require('@abtnode/util/lib/get-domain-for-blocklet');
|
|
44
|
+
const { getComponentApiKey } = require('@abtnode/util/lib/blocklet');
|
|
45
|
+
const { get: getEngine } = require('../../blocklet/manager/engine');
|
|
46
|
+
|
|
47
|
+
const { templateReplace, prettyURL } = require('../index');
|
|
48
|
+
const { getBundleDir } = require('./install-utils');
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Private node environment variables that should not be passed to blocklets
|
|
52
|
+
*/
|
|
53
|
+
const PRIVATE_NODE_ENVS = [
|
|
54
|
+
'ABT_NODE_UPDATER_PORT',
|
|
55
|
+
'ABT_NODE_SESSION_TTL',
|
|
56
|
+
'ABT_NODE_ROUTER_PROVIDER',
|
|
57
|
+
'ABT_NODE_DATA_DIR',
|
|
58
|
+
'ABT_NODE_TOKEN_SECRET',
|
|
59
|
+
'ABT_NODE_SK',
|
|
60
|
+
'ABT_NODE_SESSION_SECRET',
|
|
61
|
+
'ABT_NODE_BASE_URL',
|
|
62
|
+
'ABT_NODE_LOG_LEVEL',
|
|
63
|
+
'ABT_NODE_LOG_DIR',
|
|
64
|
+
// in /core/cli/bin/blocklet.js
|
|
65
|
+
'CLI_MODE',
|
|
66
|
+
'ABT_NODE_HOME',
|
|
67
|
+
'PM2_HOME',
|
|
68
|
+
'ABT_NODE_CONFIG_FILE',
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @returns { dataDir, logsDir, cacheDir, appDir }
|
|
73
|
+
* dataDir: dataDirs.data/name (root component) or dataDirs.data/name/childName (child component)
|
|
74
|
+
* logsDir: dataDirs.log/name
|
|
75
|
+
* cacheDir: dataDirs.cache/name (root component) or dataDirs.cache/name/childName (child component)
|
|
76
|
+
* appDir: component bundle dir
|
|
77
|
+
*/
|
|
78
|
+
const getComponentDirs = (component, { dataDirs, ensure = false, ancestors = [] } = {}) => {
|
|
79
|
+
const componentName = getComponentName(component, ancestors);
|
|
80
|
+
|
|
81
|
+
const logsDir = path.join(dataDirs.logs, componentName);
|
|
82
|
+
const dataDir = path.join(dataDirs.data, componentName);
|
|
83
|
+
const cacheDir = path.join(dataDirs.cache, componentName);
|
|
84
|
+
|
|
85
|
+
let appDir = null;
|
|
86
|
+
if (component.source === BlockletSource.local) {
|
|
87
|
+
appDir = component.deployedFrom;
|
|
88
|
+
} else {
|
|
89
|
+
appDir = getBundleDir(dataDirs.blocklets, component.meta);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!appDir) {
|
|
93
|
+
throw new Error('Can not determine blocklet directory, maybe invalid deployment from local blocklets');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (ensure) {
|
|
97
|
+
try {
|
|
98
|
+
fs.mkdirSync(dataDir, { recursive: true });
|
|
99
|
+
fs.mkdirSync(path.join(dataDir, PROJECT.DIR), { recursive: true });
|
|
100
|
+
fs.mkdirSync(logsDir, { recursive: true });
|
|
101
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
102
|
+
fs.mkdirSync(appDir, { recursive: true }); // prevent getDiskInfo failed from custom blocklet
|
|
103
|
+
} catch (err) {
|
|
104
|
+
logger.error('make blocklet dir failed', { error: err });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return { dataDir, logsDir, cacheDir, appDir };
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* @param component {import('@blocklet/server-js').ComponentState & { environmentObj: {[key: string]: string } } }
|
|
113
|
+
* @returns {{cwd, script, args, environmentObj, interpreter, interpreterArgs}: { args: []}}
|
|
114
|
+
* @return {*}
|
|
115
|
+
*/
|
|
116
|
+
const getComponentStartEngine = (component, { e2eMode = false } = {}) => {
|
|
117
|
+
if (!hasStartEngine(component.meta)) {
|
|
118
|
+
return {};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const { appDir } = component.env;
|
|
122
|
+
|
|
123
|
+
const cwd = appDir;
|
|
124
|
+
|
|
125
|
+
// get app dirs
|
|
126
|
+
const { group } = component.meta;
|
|
127
|
+
|
|
128
|
+
let startFromDevEntry = '';
|
|
129
|
+
if (component.mode === BLOCKLET_MODES.DEVELOPMENT && component.meta.scripts) {
|
|
130
|
+
startFromDevEntry = component.meta.scripts.dev;
|
|
131
|
+
if (e2eMode && component.meta.scripts.e2eDev) {
|
|
132
|
+
startFromDevEntry = component.meta.scripts.e2eDev;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const blockletEngineInfo = getBlockletEngine(component.meta);
|
|
137
|
+
if (blockletEngineInfo.interpreter === 'blocklet') {
|
|
138
|
+
return {};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let script = null;
|
|
142
|
+
let interpreter;
|
|
143
|
+
let interpreterArgs = [];
|
|
144
|
+
const environmentObj = {};
|
|
145
|
+
let args = [];
|
|
146
|
+
|
|
147
|
+
if (startFromDevEntry) {
|
|
148
|
+
script = startFromDevEntry;
|
|
149
|
+
} else if (group === 'dapp') {
|
|
150
|
+
script = blockletEngineInfo.source || BLOCKLET_ENTRY_FILE;
|
|
151
|
+
args = blockletEngineInfo.args || [];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (component.mode !== BLOCKLET_MODES.DEVELOPMENT) {
|
|
155
|
+
const engine = getEngine(blockletEngineInfo.interpreter);
|
|
156
|
+
interpreter = engine.interpreter === 'node' ? undefined : engine.interpreter;
|
|
157
|
+
interpreterArgs = interpreterArgs.concat(engine.args ? [engine.args] : []);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
cwd,
|
|
162
|
+
script,
|
|
163
|
+
args,
|
|
164
|
+
environmentObj,
|
|
165
|
+
interpreter,
|
|
166
|
+
interpreterArgs: interpreterArgs.join(' ').trim(),
|
|
167
|
+
};
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Get blocklet config object from configs array
|
|
172
|
+
* @param {object} blocklet - Blocklet object with configs
|
|
173
|
+
* @param {object} options - Options
|
|
174
|
+
* @param {boolean} options.excludeSecure - Exclude secure configs
|
|
175
|
+
* @returns {object} Config object
|
|
176
|
+
*/
|
|
177
|
+
const getBlockletConfigObj = (blocklet, { excludeSecure } = {}) => {
|
|
178
|
+
const obj = (blocklet?.configs || [])
|
|
179
|
+
.filter((x) => {
|
|
180
|
+
if (excludeSecure) {
|
|
181
|
+
return !x.secure;
|
|
182
|
+
}
|
|
183
|
+
return true;
|
|
184
|
+
})
|
|
185
|
+
.reduce((acc, x) => {
|
|
186
|
+
acc[x.key] = templateReplace(x.value, blocklet);
|
|
187
|
+
return acc;
|
|
188
|
+
}, {});
|
|
189
|
+
|
|
190
|
+
return obj;
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Get app-level system environment variables
|
|
195
|
+
* @param {object} blocklet - Blocklet object
|
|
196
|
+
* @param {object} nodeInfo - Node info with sk
|
|
197
|
+
* @param {object} dataDirs - Data directories
|
|
198
|
+
* @returns {object} Environment variables
|
|
199
|
+
*/
|
|
200
|
+
const getAppSystemEnvironments = (blocklet, nodeInfo, dataDirs) => {
|
|
201
|
+
const { did, name, title, description } = blocklet.meta;
|
|
202
|
+
const keys = Object.keys(BLOCKLET_CONFIGURABLE_KEY);
|
|
203
|
+
const result = getBlockletInfo(
|
|
204
|
+
{
|
|
205
|
+
meta: blocklet.meta,
|
|
206
|
+
environments: keys.map((key) => ({ key, value: blocklet.configObj[key] })).filter((x) => x.value),
|
|
207
|
+
},
|
|
208
|
+
nodeInfo.sk
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
const { wallet } = result;
|
|
212
|
+
const appSk = toHex(wallet.secretKey);
|
|
213
|
+
const appPk = toHex(wallet.publicKey);
|
|
214
|
+
|
|
215
|
+
const appId = wallet.address;
|
|
216
|
+
const appName = title || name || result.name;
|
|
217
|
+
const appDescription = description || result.description;
|
|
218
|
+
|
|
219
|
+
const isMigrated = Array.isArray(blocklet.migratedFrom) && blocklet.migratedFrom.length > 0;
|
|
220
|
+
const appPid = blocklet.appPid || appId;
|
|
221
|
+
const appPsk = toHex(isMigrated ? blocklet.migratedFrom[0].appSk : appSk);
|
|
222
|
+
|
|
223
|
+
/* 获取 did domain 方式:
|
|
224
|
+
* 1. 先从 site 里读
|
|
225
|
+
* 2. 如果没有,再拼接
|
|
226
|
+
*/
|
|
227
|
+
|
|
228
|
+
const pidDomain = getDidDomainForBlocklet({ did: appPid, didDomain: nodeInfo.didDomain });
|
|
229
|
+
const domainAliases = get(blocklet, 'site.domainAliases') || [];
|
|
230
|
+
|
|
231
|
+
let didDomain = domainAliases.find((item) => toLower(item.value) === toLower(pidDomain));
|
|
232
|
+
|
|
233
|
+
if (!didDomain) {
|
|
234
|
+
didDomain = domainAliases.find(
|
|
235
|
+
(item) => item.value.endsWith(nodeInfo.didDomain) || item.value.endsWith('did.staging.arcblock.io') // did.staging.arcblock.io 是旧 did domain, 但主要存在于比较旧的节点中, 需要做兼容
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const appUrl = didDomain ? prettyURL(didDomain.value, true) : `https://${pidDomain}`;
|
|
240
|
+
|
|
241
|
+
return {
|
|
242
|
+
BLOCKLET_DID: did, // BLOCKLET_DID is always same as BLOCKLET_APP_PID in structV2 application
|
|
243
|
+
BLOCKLET_APP_PK: appPk,
|
|
244
|
+
BLOCKLET_APP_SK: appSk,
|
|
245
|
+
BLOCKLET_APP_ID: appId,
|
|
246
|
+
BLOCKLET_APP_PSK: appPsk, // permanent sk even the blocklet has been migrated
|
|
247
|
+
BLOCKLET_APP_PID: appPid, // permanent did even the blocklet has been migrated
|
|
248
|
+
BLOCKLET_APP_NAME: appName,
|
|
249
|
+
BLOCKLET_APP_NAME_SLUG: urlPathFriendly(slugify(appName)),
|
|
250
|
+
BLOCKLET_APP_DESCRIPTION: appDescription,
|
|
251
|
+
BLOCKLET_APP_URL: appUrl,
|
|
252
|
+
BLOCKLET_APP_DATA_DIR: path.join(dataDirs.data, blocklet.meta.name),
|
|
253
|
+
BLOCKLET_APP_TENANT_MODE: result.tenantMode || BLOCKLET_TENANT_MODES.SINGLE,
|
|
254
|
+
BLOCKLET_APP_SALT: blocklet.settings?.session?.salt || '',
|
|
255
|
+
};
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Get app-level overwritten environment variables from config
|
|
260
|
+
* @param {object} blocklet - Blocklet object with configObj
|
|
261
|
+
* @param {object} nodeInfo - Node info with sk
|
|
262
|
+
* @returns {object} Environment variables
|
|
263
|
+
*/
|
|
264
|
+
const getAppOverwrittenEnvironments = (blocklet, nodeInfo) => {
|
|
265
|
+
const result = {};
|
|
266
|
+
if (!blocklet || !blocklet.configObj) {
|
|
267
|
+
return result;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
Object.keys(BLOCKLET_CONFIGURABLE_KEY).forEach((x) => {
|
|
271
|
+
if (!blocklet.configObj[x]) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
result[x] = blocklet.configObj[x];
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
const keys = ['BLOCKLET_APP_SK', 'BLOCKLET_APP_CHAIN_TYPE', 'BLOCKLET_WALLET_TYPE'];
|
|
279
|
+
const isAppDidRewritten = keys.some((key) => blocklet.configObj[key]);
|
|
280
|
+
if (!isAppDidRewritten) {
|
|
281
|
+
return result;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// We use user configuration here without any validation since the validation is done during update phase
|
|
285
|
+
const { wallet } = getBlockletInfo(
|
|
286
|
+
{
|
|
287
|
+
meta: blocklet.meta,
|
|
288
|
+
environments: keys.map((key) => ({ key, value: blocklet.configObj[key] })).filter((x) => x.value),
|
|
289
|
+
},
|
|
290
|
+
nodeInfo.sk
|
|
291
|
+
);
|
|
292
|
+
result.BLOCKLET_APP_ID = wallet.address;
|
|
293
|
+
|
|
294
|
+
return result;
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Get component-level system environment variables
|
|
299
|
+
* @param {object} blocklet - Blocklet object with env and ports
|
|
300
|
+
* @returns {object} Environment variables
|
|
301
|
+
*/
|
|
302
|
+
const getComponentSystemEnvironments = (blocklet) => {
|
|
303
|
+
const { port, ports } = blocklet;
|
|
304
|
+
const portEnvironments = {};
|
|
305
|
+
if (port) {
|
|
306
|
+
portEnvironments[BLOCKLET_DEFAULT_PORT_NAME] = port;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (ports) {
|
|
310
|
+
Object.assign(portEnvironments, ports);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return {
|
|
314
|
+
BLOCKLET_REAL_DID: blocklet.env.id, // <appDid>/componentDid> e.g. xxxxx/xxxxx
|
|
315
|
+
BLOCKLET_REAL_NAME: blocklet.env.name,
|
|
316
|
+
BLOCKLET_COMPONENT_DID: blocklet.meta.did, // component meta did e.g. xxxxxx
|
|
317
|
+
BLOCKLET_COMPONENT_VERSION: blocklet.meta.version,
|
|
318
|
+
BLOCKLET_DATA_DIR: blocklet.env.dataDir,
|
|
319
|
+
BLOCKLET_LOG_DIR: blocklet.env.logsDir,
|
|
320
|
+
BLOCKLET_CACHE_DIR: blocklet.env.cacheDir,
|
|
321
|
+
BLOCKLET_APP_DIR: blocklet.env.appDir,
|
|
322
|
+
...portEnvironments,
|
|
323
|
+
};
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Set 'configs', 'configObj', 'environmentObj' to blocklet
|
|
328
|
+
* @param {object} blocklet - Blocklet to fill configs for
|
|
329
|
+
* @param {Array} configs - Configs array
|
|
330
|
+
* @param {object} options - Optional: { rootBlocklet, nodeInfo, dataDirs }
|
|
331
|
+
*/
|
|
332
|
+
const fillBlockletConfigs = (blocklet, configs, options = {}) => {
|
|
333
|
+
blocklet.configs = configs || [];
|
|
334
|
+
blocklet.configObj = getBlockletConfigObj(blocklet);
|
|
335
|
+
blocklet.environments = blocklet.environments || [];
|
|
336
|
+
blocklet.environmentObj = blocklet.environments.reduce((acc, x) => {
|
|
337
|
+
acc[x.key] = templateReplace(x.value, blocklet);
|
|
338
|
+
return acc;
|
|
339
|
+
}, {});
|
|
340
|
+
|
|
341
|
+
// After migration: ensure all component system environments are set from blocklet.env if available
|
|
342
|
+
// This ensures children loaded from blocklet_children table have all required env vars in environmentObj
|
|
343
|
+
if (blocklet.env) {
|
|
344
|
+
try {
|
|
345
|
+
const componentSystemEnvs = getComponentSystemEnvironments(blocklet);
|
|
346
|
+
// Only set env vars that are not already set
|
|
347
|
+
Object.entries(componentSystemEnvs).forEach(([key, value]) => {
|
|
348
|
+
if (value !== undefined && value !== null && !blocklet.environmentObj[key]) {
|
|
349
|
+
blocklet.environmentObj[key] = value;
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
} catch (error) {
|
|
353
|
+
// If getting component system environments fails, log warning but continue
|
|
354
|
+
logger.warn('fillBlockletConfigs: failed to get component system environments', {
|
|
355
|
+
blockletDid: blocklet.meta?.did,
|
|
356
|
+
error: error.message,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// For children: also set app-level environment variables from root blocklet
|
|
362
|
+
// This ensures children have app-level env vars like BLOCKLET_APP_ID
|
|
363
|
+
const { rootBlocklet, nodeInfo, dataDirs } = options;
|
|
364
|
+
if (rootBlocklet && nodeInfo && dataDirs && blocklet !== rootBlocklet) {
|
|
365
|
+
try {
|
|
366
|
+
const appSystemEnvs = getAppSystemEnvironments(rootBlocklet, nodeInfo, dataDirs);
|
|
367
|
+
const appOverwrittenEnvs = getAppOverwrittenEnvironments(rootBlocklet, nodeInfo);
|
|
368
|
+
// Only set env vars that are not already set
|
|
369
|
+
Object.entries({ ...appSystemEnvs, ...appOverwrittenEnvs }).forEach(([key, value]) => {
|
|
370
|
+
if (value !== undefined && value !== null && !blocklet.environmentObj[key]) {
|
|
371
|
+
blocklet.environmentObj[key] = value;
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
} catch (error) {
|
|
375
|
+
// If getting app system environments fails, log warning but continue
|
|
376
|
+
logger.warn('fillBlockletConfigs: failed to get app system environments', {
|
|
377
|
+
blockletDid: blocklet.meta?.did,
|
|
378
|
+
rootDid: rootBlocklet.meta?.did,
|
|
379
|
+
error: error.message,
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Get runtime environment variables for a blocklet process
|
|
387
|
+
* @param {object} blocklet - Blocklet object
|
|
388
|
+
* @param {object} nodeEnvironments - Node environment variables
|
|
389
|
+
* @param {Array} ancestors - Ancestor blocklets
|
|
390
|
+
* @param {boolean} isGreen - Whether this is a green deployment
|
|
391
|
+
* @returns {object} Environment variables
|
|
392
|
+
*/
|
|
393
|
+
const getRuntimeEnvironments = (blocklet, nodeEnvironments, ancestors, isGreen = false) => {
|
|
394
|
+
const root = (ancestors || [])[0] || blocklet;
|
|
395
|
+
|
|
396
|
+
const initialized = root?.settings?.initialized;
|
|
397
|
+
|
|
398
|
+
const environmentObj = { ...(blocklet.environmentObj || {}) };
|
|
399
|
+
if (isGreen && blocklet.greenPorts) {
|
|
400
|
+
Object.entries(blocklet.greenPorts).forEach(([key, value]) => {
|
|
401
|
+
if (!value) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
environmentObj[key] = value;
|
|
405
|
+
if (key === BLOCKLET_DEFAULT_PORT_NAME || key === 'BLOCKLET_PORT') {
|
|
406
|
+
environmentObj[BLOCKLET_DEFAULT_PORT_NAME] = value;
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// pm2 will force inject env variables of daemon process to blocklet process
|
|
412
|
+
// we can only rewrite these private env variables to empty
|
|
413
|
+
const safeNodeEnvironments = PRIVATE_NODE_ENVS.reduce((o, x) => {
|
|
414
|
+
o[x] = '';
|
|
415
|
+
return o;
|
|
416
|
+
}, {});
|
|
417
|
+
|
|
418
|
+
// get devEnvironments, when blocklet is in dev mode
|
|
419
|
+
const devEnvironments =
|
|
420
|
+
blocklet.mode === BLOCKLET_MODES.DEVELOPMENT
|
|
421
|
+
? {
|
|
422
|
+
BLOCKLET_DEV_MOUNT_POINT: blocklet?.mountPoint || '',
|
|
423
|
+
}
|
|
424
|
+
: {};
|
|
425
|
+
|
|
426
|
+
// BLOCKLET_DEV_PORT should NOT in components of production mode
|
|
427
|
+
if (process.env.BLOCKLET_DEV_PORT) {
|
|
428
|
+
devEnvironments.BLOCKLET_DEV_PORT =
|
|
429
|
+
blocklet.mode === BLOCKLET_MODES.DEVELOPMENT ? process.env.BLOCKLET_DEV_PORT : '';
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const ports = {};
|
|
433
|
+
forEachBlockletSync(root, (x) => {
|
|
434
|
+
const webInterface = findWebInterface(x);
|
|
435
|
+
const envObj = x.meta?.did === blocklet.meta?.did ? environmentObj : x.environmentObj;
|
|
436
|
+
if (webInterface && envObj?.[webInterface.port]) {
|
|
437
|
+
ports[envObj.BLOCKLET_REAL_NAME] = envObj[webInterface.port];
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
const componentsInternalInfo = getComponentsInternalInfo(root);
|
|
442
|
+
|
|
443
|
+
// use index 1 as the path to derive deterministic encryption key for blocklet
|
|
444
|
+
const tmp = get(nodeEnvironments, 'ABT_NODE_SK')
|
|
445
|
+
? getBlockletWallet(blocklet.meta.did, nodeEnvironments.ABT_NODE_SK, undefined, 1)
|
|
446
|
+
: null;
|
|
447
|
+
|
|
448
|
+
// For Access Key authentication, components should use root app's wallet
|
|
449
|
+
// This ensures consistent accessKeyId across parent and child components
|
|
450
|
+
const accessWallet = get(nodeEnvironments, 'ABT_NODE_SK')
|
|
451
|
+
? getBlockletWallet(root.appDid || root.meta.did, nodeEnvironments.ABT_NODE_SK, undefined, 2)
|
|
452
|
+
: null;
|
|
453
|
+
|
|
454
|
+
const BLOCKLET_APP_IDS = getBlockletAppIdList(root).join(',');
|
|
455
|
+
|
|
456
|
+
const componentApiKey = getComponentApiKey({
|
|
457
|
+
serverSk: nodeEnvironments.ABT_NODE_SK,
|
|
458
|
+
app: root,
|
|
459
|
+
component: blocklet,
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
const blockletInfo = getBlockletInfo(blocklet, nodeEnvironments.ABT_NODE_SK, { returnWallet: true });
|
|
463
|
+
|
|
464
|
+
const rootBlockletInfo =
|
|
465
|
+
blocklet === root ? blockletInfo : getBlockletInfo(root, nodeEnvironments.ABT_NODE_SK, { returnWallet: true });
|
|
466
|
+
|
|
467
|
+
const { wallet } = rootBlockletInfo;
|
|
468
|
+
const appSk = toHex(wallet.secretKey);
|
|
469
|
+
const appPk = toHex(wallet.publicKey);
|
|
470
|
+
|
|
471
|
+
const ethWallet = fromSecretKey(appSk.slice(0, 66), 'ethereum');
|
|
472
|
+
const ethPk = toHex(ethWallet.publicKey);
|
|
473
|
+
|
|
474
|
+
const isMigrated = Array.isArray(root.migratedFrom) && root.migratedFrom.length > 0;
|
|
475
|
+
const appPsk = toHex(isMigrated ? root.migratedFrom[0].appSk : appSk);
|
|
476
|
+
|
|
477
|
+
// Calculate permanent public key (PPK)
|
|
478
|
+
const appPpk = isMigrated ? toHex(fromSecretKey(appPsk, wallet.type).publicKey) : appPk;
|
|
479
|
+
const ethPermanentWallet = fromSecretKey(appPsk.slice(0, 66), 'ethereum');
|
|
480
|
+
const appPpkEth = toHex(ethPermanentWallet.publicKey);
|
|
481
|
+
|
|
482
|
+
const env = {
|
|
483
|
+
...blocklet.configObj,
|
|
484
|
+
...getSharedConfigObj((ancestors || [])[0], blocklet, true),
|
|
485
|
+
...environmentObj,
|
|
486
|
+
...devEnvironments,
|
|
487
|
+
BLOCKLET_MOUNT_POINTS: JSON.stringify(componentsInternalInfo),
|
|
488
|
+
BLOCKLET_MODE: blocklet.mode || BLOCKLET_MODES.PRODUCTION,
|
|
489
|
+
BLOCKLET_APP_EK: tmp?.secretKey,
|
|
490
|
+
// for login token authentication
|
|
491
|
+
BLOCKLET_SESSION_SECRET: rootBlockletInfo.secret,
|
|
492
|
+
BLOCKLET_APP_VERSION: root.meta.version,
|
|
493
|
+
BLOCKLET_APP_IDS,
|
|
494
|
+
BLOCKLET_COMPONENT_API_KEY: componentApiKey,
|
|
495
|
+
BLOCKLET_APP_ASK: accessWallet?.secretKey,
|
|
496
|
+
...nodeEnvironments,
|
|
497
|
+
...safeNodeEnvironments,
|
|
498
|
+
BLOCKLET_APP_PPK: appPpk, // permanent pk corresponding to PSK
|
|
499
|
+
BLOCKLET_APP_PPK_ETH: appPpkEth, // permanent pk corresponding to PSK for ethereum
|
|
500
|
+
BLOCKLET_APP_PK: appPk,
|
|
501
|
+
BLOCKLET_APP_PK_ETH: ethPk,
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
const aigne = get(root, 'settings.aigne', {});
|
|
505
|
+
const salt = root.meta.did || AIGNE_CONFIG_ENCRYPT_SALT;
|
|
506
|
+
if (!isNil(aigne) && aigne.provider) {
|
|
507
|
+
const { key, accessKeyId, secretAccessKey, provider } = aigne;
|
|
508
|
+
const selectedModel = !aigne.model || aigne.model === 'auto' ? undefined : aigne.model;
|
|
509
|
+
env.BLOCKLET_AIGNE_API_MODEL = selectedModel;
|
|
510
|
+
env.BLOCKLET_AIGNE_API_PROVIDER = aigne.provider;
|
|
511
|
+
const credential = {
|
|
512
|
+
apiKey: key ? decrypt(key, salt, '') : key || '',
|
|
513
|
+
accessKeyId: accessKeyId && provider === 'bedrock' ? decrypt(accessKeyId, salt, '') : accessKeyId || '',
|
|
514
|
+
secretAccessKey:
|
|
515
|
+
secretAccessKey && provider === 'bedrock' ? decrypt(secretAccessKey, salt, '') : secretAccessKey || '',
|
|
516
|
+
};
|
|
517
|
+
env.BLOCKLET_AIGNE_API_CREDENTIAL = JSON.stringify(credential);
|
|
518
|
+
env.BLOCKLET_AIGNE_API_URL = aigne.url || '';
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
if (root?.environmentObj?.BLOCKLET_APP_DATA_DIR) {
|
|
522
|
+
env.BLOCKLET_APP_SHARE_DIR = path.join(root.environmentObj.BLOCKLET_APP_DATA_DIR, '.share');
|
|
523
|
+
env.BLOCKLET_SHARE_DIR = path.join(root.environmentObj.BLOCKLET_APP_DATA_DIR, '.share', blocklet.meta.did);
|
|
524
|
+
if (!fs.existsSync(env.BLOCKLET_APP_SHARE_DIR) && process.env.ABT_NODE_DATA_DIR) {
|
|
525
|
+
fs.mkdirSync(env.BLOCKLET_APP_SHARE_DIR, { recursive: true });
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (initialized) {
|
|
530
|
+
env.initialized = initialized;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
if (isGreen && blocklet.greenPorts?.[BLOCKLET_DEFAULT_PORT_NAME]) {
|
|
534
|
+
env[BLOCKLET_DEFAULT_PORT_NAME] = blocklet.greenPorts[BLOCKLET_DEFAULT_PORT_NAME];
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// ensure all envs are literals and do not contain line breaks
|
|
538
|
+
Object.keys(env).forEach((key) => {
|
|
539
|
+
env[key] = formatEnv(env[key]);
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
return env;
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
module.exports = {
|
|
546
|
+
PRIVATE_NODE_ENVS,
|
|
547
|
+
getComponentDirs,
|
|
548
|
+
getComponentStartEngine,
|
|
549
|
+
getBlockletConfigObj,
|
|
550
|
+
getAppSystemEnvironments,
|
|
551
|
+
getAppOverwrittenEnvironments,
|
|
552
|
+
getComponentSystemEnvironments,
|
|
553
|
+
fillBlockletConfigs,
|
|
554
|
+
getRuntimeEnvironments,
|
|
555
|
+
};
|