@abtnode/core 1.16.45-beta-20250612-231219-481217be → 1.16.45-beta-20250618-073451-6e48fb62
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/node.js +11 -1
- package/lib/blocklet/manager/disk.js +2 -2
- package/lib/blocklet/manager/ensure-blocklet-running.js +8 -2
- package/lib/blocklet/migration-dist/migration.cjs +2 -1
- package/lib/event/index.js +24 -2
- package/lib/event/util.js +71 -0
- package/lib/index.js +4 -2
- package/lib/router/helper.js +11 -2
- package/lib/router/manager.js +10 -5
- package/lib/router/security/limiter.js +4 -1
- package/lib/team/manager.js +3 -9
- package/lib/util/blocklet.js +2 -2
- package/lib/util/cache.js +12 -0
- package/lib/util/launcher.js +66 -2
- package/lib/util/router.js +19 -0
- package/lib/validators/theme.js +10 -9
- package/package.json +26 -26
package/lib/api/node.js
CHANGED
|
@@ -8,7 +8,7 @@ const isGitpod = require('@abtnode/util/lib/is-gitpod');
|
|
|
8
8
|
const getFolderSize = require('@abtnode/util/lib/get-folder-size');
|
|
9
9
|
const canPackageReadWrite = require('@abtnode/util/lib/can-pkg-rw');
|
|
10
10
|
const { toDelegateAddress } = require('@arcblock/did-util');
|
|
11
|
-
const { MONITOR_RECORD_INTERVAL_SEC } = require('@abtnode/constant');
|
|
11
|
+
const { MONITOR_RECORD_INTERVAL_SEC, NODE_MODES } = require('@abtnode/constant');
|
|
12
12
|
|
|
13
13
|
const logger = require('@abtnode/logger')('@abtnode/core:api:node');
|
|
14
14
|
|
|
@@ -70,6 +70,16 @@ class NodeAPI {
|
|
|
70
70
|
|
|
71
71
|
async getDiskInfo() {
|
|
72
72
|
let diskInfo = { app: 0, cache: 0, log: 0, data: 0, blocklets: 0 };
|
|
73
|
+
if (process.env.ABT_NODE_FAKE_DISK_INFO === '1') {
|
|
74
|
+
return diskInfo;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Do not get real disk info for serverless mode to avoid too much resource usage
|
|
78
|
+
const info = await this.state.read();
|
|
79
|
+
if (info.mode === NODE_MODES.SERVERLESS) {
|
|
80
|
+
return diskInfo;
|
|
81
|
+
}
|
|
82
|
+
|
|
73
83
|
try {
|
|
74
84
|
const [app, cache, log, data, blocklets] = await Promise.all([
|
|
75
85
|
getFolderSize(this.state.dataDirs.core),
|
|
@@ -2772,7 +2772,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
2772
2772
|
}
|
|
2773
2773
|
|
|
2774
2774
|
async updateBlockletSettings({ did, enableSessionHardening, invite, gateway }, context) {
|
|
2775
|
-
|
|
2775
|
+
const params = {};
|
|
2776
2776
|
if (!isNil(enableSessionHardening)) {
|
|
2777
2777
|
params.enableSessionHardening = enableSessionHardening;
|
|
2778
2778
|
}
|
|
@@ -2782,7 +2782,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
2782
2782
|
}
|
|
2783
2783
|
|
|
2784
2784
|
if (!isNil(gateway)) {
|
|
2785
|
-
params =
|
|
2785
|
+
params.gateway = gateway;
|
|
2786
2786
|
}
|
|
2787
2787
|
|
|
2788
2788
|
const keys = Object.keys(params);
|
|
@@ -12,6 +12,8 @@ const inProgressStatuses = [BlockletStatus.stopping, BlockletStatus.restarting,
|
|
|
12
12
|
class EnsureBlockletRunning {
|
|
13
13
|
initialized = false;
|
|
14
14
|
|
|
15
|
+
firstChecked = false;
|
|
16
|
+
|
|
15
17
|
// 每次任务的最小间隔时间
|
|
16
18
|
checkInterval = +process.env.ABT_NODE_ENSURE_RUNNING_CHECK_INTERVAL || 60 * 1000;
|
|
17
19
|
|
|
@@ -52,12 +54,15 @@ class EnsureBlockletRunning {
|
|
|
52
54
|
|
|
53
55
|
isBlockletPortHealthyWithRetries = async (blocklet, isDoing = false) => {
|
|
54
56
|
let error;
|
|
55
|
-
|
|
57
|
+
const maxAttempts = this.firstChecked ? 10 : 2;
|
|
58
|
+
const timeout = this.firstChecked ? 6000 : 3000;
|
|
59
|
+
|
|
60
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
56
61
|
try {
|
|
57
62
|
// eslint-disable-next-line no-await-in-loop
|
|
58
63
|
await this.isBlockletPortHealthy(blocklet, {
|
|
59
64
|
minConsecutiveTime: 3000,
|
|
60
|
-
timeout
|
|
65
|
+
timeout,
|
|
61
66
|
});
|
|
62
67
|
return true;
|
|
63
68
|
} catch (e) {
|
|
@@ -141,6 +146,7 @@ class EnsureBlockletRunning {
|
|
|
141
146
|
logger.error('ensure blocklet status failed', e);
|
|
142
147
|
}
|
|
143
148
|
this.runningRootBlocklets = {};
|
|
149
|
+
this.firstChecked = true;
|
|
144
150
|
};
|
|
145
151
|
|
|
146
152
|
getRunningBlocklets = async () => {
|
|
@@ -762,6 +762,7 @@ module.exports = Object.freeze({
|
|
|
762
762
|
readNotifications: true,
|
|
763
763
|
unreadNotifications: true,
|
|
764
764
|
launchBlockletByLauncher: true,
|
|
765
|
+
launchBlockletWithoutWallet: true,
|
|
765
766
|
},
|
|
766
767
|
NOTIFICATION_SEND_STATUS: {
|
|
767
768
|
PENDING: 0, // 待发送
|
|
@@ -38888,7 +38889,7 @@ module.exports = require("zlib");
|
|
|
38888
38889
|
/***/ ((module) => {
|
|
38889
38890
|
|
|
38890
38891
|
"use strict";
|
|
38891
|
-
module.exports = /*#__PURE__*/JSON.parse('{"name":"@abtnode/core","publishConfig":{"access":"public"},"version":"1.16.44","description":"","main":"lib/index.js","files":["lib"],"scripts":{"lint":"eslint tests lib --ignore-pattern \'tests/assets/*\'","lint:fix":"eslint --fix tests lib","test":"node tools/jest.js","coverage":"npm run test -- --coverage"},"keywords":[],"author":"wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)","license":"Apache-2.0","dependencies":{"@abtnode/analytics":"1.16.44","@abtnode/auth":"1.16.44","@abtnode/certificate-manager":"1.16.44","@abtnode/client":"1.16.44","@abtnode/constant":"1.16.44","@abtnode/cron":"1.16.44","@abtnode/db-cache":"1.16.44","@abtnode/docker-utils":"1.16.44","@abtnode/logger":"1.16.44","@abtnode/models":"1.16.44","@abtnode/queue":"1.16.44","@abtnode/rbac":"1.16.44","@abtnode/router-provider":"1.16.44","@abtnode/static-server":"1.16.44","@abtnode/timemachine":"1.16.44","@abtnode/util":"1.16.44","@arcblock/did":"1.20.14","@arcblock/did-auth":"1.20.14","@arcblock/did-ext":"1.20.14","@arcblock/did-motif":"^1.1.13","@arcblock/did-util":"1.20.14","@arcblock/event-hub":"1.20.14","@arcblock/jwt":"1.20.14","@arcblock/pm2-events":"^0.0.5","@arcblock/validator":"1.20.14","@arcblock/vc":"1.20.14","@blocklet/constant":"1.16.44","@blocklet/did-space-js":"^1.0.
|
|
38892
|
+
module.exports = /*#__PURE__*/JSON.parse('{"name":"@abtnode/core","publishConfig":{"access":"public"},"version":"1.16.44","description":"","main":"lib/index.js","files":["lib"],"scripts":{"lint":"eslint tests lib --ignore-pattern \'tests/assets/*\'","lint:fix":"eslint --fix tests lib","test":"node tools/jest.js","coverage":"npm run test -- --coverage"},"keywords":[],"author":"wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)","license":"Apache-2.0","dependencies":{"@abtnode/analytics":"1.16.44","@abtnode/auth":"1.16.44","@abtnode/certificate-manager":"1.16.44","@abtnode/client":"1.16.44","@abtnode/constant":"1.16.44","@abtnode/cron":"1.16.44","@abtnode/db-cache":"1.16.44","@abtnode/docker-utils":"1.16.44","@abtnode/logger":"1.16.44","@abtnode/models":"1.16.44","@abtnode/queue":"1.16.44","@abtnode/rbac":"1.16.44","@abtnode/router-provider":"1.16.44","@abtnode/static-server":"1.16.44","@abtnode/timemachine":"1.16.44","@abtnode/util":"1.16.44","@arcblock/did":"1.20.14","@arcblock/did-auth":"1.20.14","@arcblock/did-ext":"1.20.14","@arcblock/did-motif":"^1.1.13","@arcblock/did-util":"1.20.14","@arcblock/event-hub":"1.20.14","@arcblock/jwt":"1.20.14","@arcblock/pm2-events":"^0.0.5","@arcblock/validator":"1.20.14","@arcblock/vc":"1.20.14","@blocklet/constant":"1.16.44","@blocklet/did-space-js":"^1.0.62","@blocklet/env":"1.16.44","@blocklet/error":"^0.2.5","@blocklet/meta":"1.16.44","@blocklet/resolver":"1.16.44","@blocklet/sdk":"1.16.44","@blocklet/store":"1.16.44","@blocklet/theme":"^2.13.70","@fidm/x509":"^1.2.1","@ocap/mcrypto":"1.20.14","@ocap/util":"1.20.14","@ocap/wallet":"1.20.14","@slack/webhook":"^5.0.4","archiver":"^7.0.1","axios":"^1.7.9","axon":"^2.0.3","chalk":"^4.1.2","cross-spawn":"^7.0.3","dayjs":"^1.11.13","deep-diff":"^1.0.2","detect-port":"^1.5.1","envfile":"^7.1.0","escape-string-regexp":"^4.0.0","fast-glob":"^3.3.2","filesize":"^10.1.1","flat":"^5.0.2","fs-extra":"^11.2.0","get-port":"^5.1.1","hasha":"^5.2.2","is-base64":"^1.1.0","is-cidr":"4","is-ip":"3","is-url":"^1.2.4","joi":"17.12.2","joi-extension-semver":"^5.0.0","js-yaml":"^4.1.0","kill-port":"^2.0.1","lodash":"^4.17.21","node-stream-zip":"^1.15.0","p-all":"^3.0.0","p-limit":"^3.1.0","p-map":"^4.0.0","p-retry":"^4.6.2","p-wait-for":"^3.2.0","rate-limiter-flexible":"^5.0.5","read-last-lines":"^1.8.0","semver":"^7.6.3","sequelize":"^6.35.0","shelljs":"^0.8.5","slugify":"^1.6.6","ssri":"^8.0.1","stream-throttle":"^0.1.3","stream-to-promise":"^3.0.0","systeminformation":"^5.23.3","tail":"^2.2.4","tar":"^6.1.11","transliteration":"^2.3.5","ua-parser-js":"^1.0.2","ufo":"^1.5.3","uuid":"^9.0.1","valid-url":"^1.0.9","which":"^2.0.2","xbytes":"^1.8.0"},"devDependencies":{"expand-tilde":"^2.0.2","express":"^4.18.2","jest":"^29.7.0","unzipper":"^0.10.11"},"gitHead":"e5764f753181ed6a7c615cd4fc6682aacf0cb7cd"}');
|
|
38892
38893
|
|
|
38893
38894
|
/***/ }),
|
|
38894
38895
|
|
package/lib/event/index.js
CHANGED
|
@@ -3,7 +3,6 @@ const { EventEmitter } = require('events');
|
|
|
3
3
|
const { wipeSensitiveData, getDisplayName } = require('@blocklet/meta/lib/util');
|
|
4
4
|
const logger = require('@abtnode/logger')('@abtnode/core:event');
|
|
5
5
|
const {
|
|
6
|
-
BLOCKLET_MODES,
|
|
7
6
|
BlockletStatus,
|
|
8
7
|
BlockletSource,
|
|
9
8
|
BlockletEvents,
|
|
@@ -35,9 +34,14 @@ const states = require('../states');
|
|
|
35
34
|
const { getBackupEndpoint, getBackupFilesUrlFromEndpoint, getDIDSpacesUrlFromEndpoint } = require('../util/spaces');
|
|
36
35
|
const { autoBackupHandlerFactory, autoBackupHandler } = require('./auto-backup-handler');
|
|
37
36
|
|
|
38
|
-
const routingSnapshotPrefix = (blocklet) => (blocklet.mode === BLOCKLET_MODES.DEVELOPMENT ? '[DEV] ' : '');
|
|
39
37
|
const eventBusHandler = require('../blocklet/webhook/event-bus');
|
|
40
38
|
const { isDevelopmentMode, deleteBlockletCache } = require('../util/blocklet');
|
|
39
|
+
const {
|
|
40
|
+
backupBlockletSites,
|
|
41
|
+
cleanBlockletSitesBackup,
|
|
42
|
+
rollbackBlockletSites,
|
|
43
|
+
routingSnapshotPrefix,
|
|
44
|
+
} = require('./util');
|
|
41
45
|
|
|
42
46
|
/**
|
|
43
47
|
*
|
|
@@ -212,7 +216,12 @@ module.exports = ({
|
|
|
212
216
|
};
|
|
213
217
|
|
|
214
218
|
const handleBlockletUpgrade = async (name, { blocklet, context }) => {
|
|
219
|
+
let sites;
|
|
220
|
+
let backupFile = null;
|
|
215
221
|
try {
|
|
222
|
+
({ sites, backupFile } = await backupBlockletSites(blocklet));
|
|
223
|
+
logger.info('sites before update blocklet routing', { event: name, did: blocklet.meta.did, sites });
|
|
224
|
+
|
|
216
225
|
const changed = await ensureBlockletRoutingForUpgrade(blocklet, context);
|
|
217
226
|
if (changed) {
|
|
218
227
|
const hash = await takeRoutingSnapshot(
|
|
@@ -223,8 +232,21 @@ module.exports = ({
|
|
|
223
232
|
}
|
|
224
233
|
|
|
225
234
|
await teamAPI.refreshBlockletInterfacePermissions(blocklet.meta);
|
|
235
|
+
|
|
236
|
+
// clean backup sites after success
|
|
237
|
+
sites = null;
|
|
238
|
+
cleanBlockletSitesBackup(backupFile);
|
|
226
239
|
} catch (error) {
|
|
227
240
|
logger.error('upgrade blocklet routing rules error', { event: name, error });
|
|
241
|
+
|
|
242
|
+
rollbackBlockletSites({ blocklet, sites, backupFile, takeRoutingSnapshot, context })
|
|
243
|
+
.then(() => {
|
|
244
|
+
logger.info('rollback blocklet routing rules success', { event: name, did: blocklet.meta.did });
|
|
245
|
+
})
|
|
246
|
+
.catch((err) => {
|
|
247
|
+
logger.error('rollback blocklet routing rules error', { event: name, error: err });
|
|
248
|
+
});
|
|
249
|
+
|
|
228
250
|
teamManager.createNotification({
|
|
229
251
|
title: 'Blocklet URL Mapping Error',
|
|
230
252
|
// eslint-disable-next-line max-len
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const logger = require('@abtnode/logger')('@abtnode/core:event');
|
|
4
|
+
const { BLOCKLET_MODES } = require('@blocklet/constant');
|
|
5
|
+
|
|
6
|
+
const states = require('../states');
|
|
7
|
+
|
|
8
|
+
const routingSnapshotPrefix = (blocklet) => (blocklet.mode === BLOCKLET_MODES.DEVELOPMENT ? '[DEV] ' : '');
|
|
9
|
+
|
|
10
|
+
const backupBlockletSites = async (blocklet) => {
|
|
11
|
+
const sites = await states.site.getSitesByBlocklet(blocklet.meta.did);
|
|
12
|
+
let backupFile = null;
|
|
13
|
+
try {
|
|
14
|
+
backupFile = path.join(blocklet.env.cacheDir, `sites-backup-${Date.now()}.json`);
|
|
15
|
+
logger.info(`backup blocklet sites to ${backupFile}`, { did: blocklet.meta.did, sites });
|
|
16
|
+
fs.writeFileSync(backupFile, JSON.stringify(sites));
|
|
17
|
+
logger.info('backup blocklet sites', { did: blocklet.meta.did, backupFile });
|
|
18
|
+
} catch (error) {
|
|
19
|
+
logger.error('backup blocklet sites error', { error });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return { sites, backupFile };
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const rollbackBlockletSites = async ({ blocklet, sites, backupFile, takeRoutingSnapshot, context }) => {
|
|
26
|
+
try {
|
|
27
|
+
let sitesToRestore = sites;
|
|
28
|
+
logger.info('rollback blocklet sites', { did: blocklet.meta.did, sites, backupFile });
|
|
29
|
+
if (!sitesToRestore && backupFile && fs.existsSync(backupFile)) {
|
|
30
|
+
sitesToRestore = JSON.parse(fs.readFileSync(backupFile, 'utf8'));
|
|
31
|
+
logger.info('rollback sites from backup file', { did: blocklet.meta.did, backupFile });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!sitesToRestore) {
|
|
35
|
+
logger.info('no blocklet sites to rollback', { did: blocklet.meta.did, sites, backupFile });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const promises = sitesToRestore.map((site) => states.site.update({ id: site.id }, { $set: { rules: site.rules } }));
|
|
39
|
+
await Promise.all(promises);
|
|
40
|
+
logger.info('rollback blocklet sites rules success to database', { did: blocklet.meta.did, sites, backupFile });
|
|
41
|
+
|
|
42
|
+
const hash = await takeRoutingSnapshot(
|
|
43
|
+
{
|
|
44
|
+
message: `${routingSnapshotPrefix(blocklet)}Roll back sites rules for blocklet ${blocklet.meta.name}`,
|
|
45
|
+
dryRun: false,
|
|
46
|
+
},
|
|
47
|
+
context
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
logger.info('rollback blocklet sites success to snapshot', { did: blocklet.meta.did, hash });
|
|
51
|
+
} catch (error) {
|
|
52
|
+
logger.error('rollback blocklet sites error', { error, blocklet, sites, backupFile });
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const cleanBlockletSitesBackup = (backupFile) => {
|
|
57
|
+
if (!backupFile || !fs.existsSync(backupFile)) {
|
|
58
|
+
logger.info('no backup file to clean', { backupFile });
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
fs.unlinkSync(backupFile);
|
|
63
|
+
logger.info('clean backup sites', { backupFile });
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
module.exports = {
|
|
67
|
+
routingSnapshotPrefix,
|
|
68
|
+
backupBlockletSites,
|
|
69
|
+
cleanBlockletSitesBackup,
|
|
70
|
+
rollbackBlockletSites,
|
|
71
|
+
};
|
package/lib/index.js
CHANGED
|
@@ -23,6 +23,7 @@ const {
|
|
|
23
23
|
isLauncherSessionConsumed,
|
|
24
24
|
setupAppOwner,
|
|
25
25
|
launchBlockletByLauncher,
|
|
26
|
+
launchBlockletWithoutWallet,
|
|
26
27
|
} = require('./util/launcher');
|
|
27
28
|
const WebHook = require('./webhook');
|
|
28
29
|
const states = require('./states');
|
|
@@ -252,7 +253,7 @@ function ABTNode(options) {
|
|
|
252
253
|
});
|
|
253
254
|
|
|
254
255
|
// 4. init routing manager
|
|
255
|
-
const routerManager = new RouterManager({ certManager });
|
|
256
|
+
const routerManager = new RouterManager({ certManager, dataDirs });
|
|
256
257
|
const routingSnapshot = new RoutingSnapshot({
|
|
257
258
|
baseDir: dataDirs.core,
|
|
258
259
|
getRoutingData: async () => {
|
|
@@ -503,7 +504,7 @@ function ABTNode(options) {
|
|
|
503
504
|
getConnectedAccount: teamAPI.getConnectedAccount.bind(teamAPI),
|
|
504
505
|
getOwner: teamAPI.getOwner.bind(teamAPI),
|
|
505
506
|
getNodeUsers: () => teamAPI.getUsers({ teamDid: options.nodeDid }),
|
|
506
|
-
getNodeUser: (user) => teamAPI.getUser({ teamDid: options.nodeDid, user }),
|
|
507
|
+
getNodeUser: (user, _options) => teamAPI.getUser({ teamDid: options.nodeDid, user, options: _options }),
|
|
507
508
|
addUser: teamAPI.addUser.bind(teamAPI),
|
|
508
509
|
loginUser: teamAPI.loginUser.bind(teamAPI),
|
|
509
510
|
disconnectUserAccount: teamAPI.disconnectUserAccount.bind(teamAPI),
|
|
@@ -716,6 +717,7 @@ function ABTNode(options) {
|
|
|
716
717
|
isLauncherSessionConsumed,
|
|
717
718
|
setupAppOwner,
|
|
718
719
|
launchBlockletByLauncher: (params, context) => launchBlockletByLauncher(instance, params, context),
|
|
720
|
+
launchBlockletWithoutWallet: (params, context) => launchBlockletWithoutWallet(instance, params, context),
|
|
719
721
|
|
|
720
722
|
// init security default data
|
|
721
723
|
initializeSecurityDefaultData: securityAPI.initializeDefaultData.bind(securityAPI),
|
package/lib/router/helper.js
CHANGED
|
@@ -1444,7 +1444,7 @@ module.exports = function getRouterHelpers({
|
|
|
1444
1444
|
entityType: 'server',
|
|
1445
1445
|
severity: 'error',
|
|
1446
1446
|
source: 'system',
|
|
1447
|
-
sticky:
|
|
1447
|
+
sticky: false,
|
|
1448
1448
|
});
|
|
1449
1449
|
});
|
|
1450
1450
|
}
|
|
@@ -1459,7 +1459,16 @@ module.exports = function getRouterHelpers({
|
|
|
1459
1459
|
if (daemon && info.routing.blockPolicy?.autoBlocking?.enabled && providers[providerName].supportsModSecurity()) {
|
|
1460
1460
|
scanner.stopLogWatcher();
|
|
1461
1461
|
const logs = providers[providerName].getLogFilesForToday();
|
|
1462
|
-
const limiter = createLimiter(info.routing.blockPolicy.autoBlocking)
|
|
1462
|
+
const limiter = createLimiter(info.routing.blockPolicy.autoBlocking, (entry) => {
|
|
1463
|
+
teamManager.createNotification({
|
|
1464
|
+
title: 'User IP Blocked',
|
|
1465
|
+
description: `User IP blocked: ${JSON.stringify(entry, null, 2)}`,
|
|
1466
|
+
entityType: 'server',
|
|
1467
|
+
severity: 'error',
|
|
1468
|
+
source: 'system',
|
|
1469
|
+
sticky: false,
|
|
1470
|
+
});
|
|
1471
|
+
});
|
|
1463
1472
|
scanner.startLogWatcher(logs.error, async (logEntries) => {
|
|
1464
1473
|
const blocked = [];
|
|
1465
1474
|
const grouped = countBy(logEntries, 'ip');
|
package/lib/router/manager.js
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
*/
|
|
7
7
|
const https = require('https');
|
|
8
8
|
const path = require('path');
|
|
9
|
-
const os = require('os');
|
|
10
9
|
const dns = require('dns');
|
|
11
10
|
const fse = require('fs-extra');
|
|
12
11
|
const get = require('lodash/get');
|
|
@@ -55,7 +54,7 @@ const {
|
|
|
55
54
|
getDidFromDomainGroupName,
|
|
56
55
|
updateNFTDomainRecord,
|
|
57
56
|
revokeAndDeleteNFTDomainRecord,
|
|
58
|
-
|
|
57
|
+
getAvailableGatewayPorts,
|
|
59
58
|
} = require('../util/router');
|
|
60
59
|
const checkDNS = require('../util/check-dns.js');
|
|
61
60
|
|
|
@@ -117,8 +116,9 @@ const isMockedDidDomain = (domain) => {
|
|
|
117
116
|
};
|
|
118
117
|
|
|
119
118
|
class RouterManager extends EventEmitter {
|
|
120
|
-
constructor({ certManager }) {
|
|
119
|
+
constructor({ certManager, dataDirs }) {
|
|
121
120
|
super();
|
|
121
|
+
this.dataDirs = dataDirs;
|
|
122
122
|
this.certManager = certManager;
|
|
123
123
|
|
|
124
124
|
// HACK: do not emit any events from CLI
|
|
@@ -818,9 +818,14 @@ class RouterManager extends EventEmitter {
|
|
|
818
818
|
// get provider
|
|
819
819
|
const providerName = getProviderFromNodeInfo(info);
|
|
820
820
|
const Provider = getProvider(providerName);
|
|
821
|
-
const tmpDir = path.join(os.tmpdir(), `${providerName}-${Date.now()}`);
|
|
822
821
|
|
|
823
|
-
const
|
|
822
|
+
const tmpDir = path.join(this.dataDirs.tmp, `${providerName}-${Date.now()}`);
|
|
823
|
+
fse.ensureDirSync(tmpDir);
|
|
824
|
+
logger.info('validateRouterConfig.tmpDir', { tmpDir });
|
|
825
|
+
|
|
826
|
+
const { httpPort, httpsPort } = await getAvailableGatewayPorts();
|
|
827
|
+
logger.info('validateRouterConfig.gatewayPorts', { httpPort, httpsPort });
|
|
828
|
+
|
|
824
829
|
// disable cache to reduce nginx reload time and memory consumption
|
|
825
830
|
const provider = new Provider({
|
|
826
831
|
configDir: tmpDir,
|
|
@@ -16,9 +16,10 @@ const states = require('../../states');
|
|
|
16
16
|
* windowQuota: number;
|
|
17
17
|
* blockDuration: number;
|
|
18
18
|
* }} options
|
|
19
|
+
* @param {(entry: { scope: string, key: string, expiresAt: number }) => void} onBlocked
|
|
19
20
|
* @returns {{ check: (ip: string, points?: number) => Promise<number> }}
|
|
20
21
|
*/
|
|
21
|
-
function createLimiter(options) {
|
|
22
|
+
function createLimiter(options, onBlocked = () => {}) {
|
|
22
23
|
const limiter = new RateLimiterMemory({
|
|
23
24
|
points: options.windowQuota,
|
|
24
25
|
duration: options.windowSize,
|
|
@@ -35,8 +36,10 @@ function createLimiter(options) {
|
|
|
35
36
|
logger.info('Rate limit has exceeded', { ip, err });
|
|
36
37
|
const expiresAt = dayjs().add(err.msBeforeNext, 'ms').unix();
|
|
37
38
|
const added = await states.blacklist.addItem(BLACKLIST_SCOPE.ROUTER, ip, expiresAt);
|
|
39
|
+
|
|
38
40
|
if (added) {
|
|
39
41
|
logger.info('User IP blocked', { ip, expiresAt });
|
|
42
|
+
onBlocked({ scope: BLACKLIST_SCOPE.ROUTER, key: ip, expiresAt });
|
|
40
43
|
return err.msBeforeNext;
|
|
41
44
|
}
|
|
42
45
|
}
|
package/lib/team/manager.js
CHANGED
|
@@ -222,15 +222,9 @@ class TeamManager extends EventEmitter {
|
|
|
222
222
|
users = users.concat(roleUsers);
|
|
223
223
|
}
|
|
224
224
|
if (userDids.length > 0) {
|
|
225
|
-
const queryUsers = await
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
approved: true,
|
|
229
|
-
selection,
|
|
230
|
-
includeConnectedAccounts,
|
|
231
|
-
includePassports: true,
|
|
232
|
-
},
|
|
233
|
-
});
|
|
225
|
+
const queryUsers = await Promise.all(
|
|
226
|
+
userDids.map((did) => userState.getUser(did, { includePassports: true, enableConnectedAccount: true }))
|
|
227
|
+
);
|
|
234
228
|
|
|
235
229
|
const validUsers = queryUsers.filter((user) => {
|
|
236
230
|
const { passports = [] } = user;
|
package/lib/util/blocklet.js
CHANGED
|
@@ -1577,8 +1577,8 @@ const _getBlocklet = async ({
|
|
|
1577
1577
|
blocklet.settings.storeList = blocklet.settings.storeList || [];
|
|
1578
1578
|
blocklet.settings.theme = mergeWith(
|
|
1579
1579
|
cloneDeep({
|
|
1580
|
-
light: BLOCKLET_THEME_LIGHT,
|
|
1581
|
-
dark: BLOCKLET_THEME_DARK,
|
|
1580
|
+
light: { palette: BLOCKLET_THEME_LIGHT.palette },
|
|
1581
|
+
dark: { palette: BLOCKLET_THEME_DARK.palette },
|
|
1582
1582
|
prefer: 'light',
|
|
1583
1583
|
}),
|
|
1584
1584
|
blocklet.settings.theme,
|
package/lib/util/cache.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fs = require('fs-extra');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { getAppImageCacheDir, getAppOgCacheDir } = require('@abtnode/util/lib/blocklet');
|
|
4
|
+
const { DBCache, getAbtNodeRedisAndSQLiteUrl } = require('@abtnode/db-cache');
|
|
4
5
|
|
|
5
6
|
const clearCacheDir = (dir) => {
|
|
6
7
|
if (fs.existsSync(dir)) {
|
|
@@ -9,6 +10,15 @@ const clearCacheDir = (dir) => {
|
|
|
9
10
|
}
|
|
10
11
|
};
|
|
11
12
|
|
|
13
|
+
const clearDBCache = async () => {
|
|
14
|
+
const dbCache = new DBCache(() => ({
|
|
15
|
+
prefix: '*',
|
|
16
|
+
ttl: 1000 * 60,
|
|
17
|
+
...getAbtNodeRedisAndSQLiteUrl(),
|
|
18
|
+
}));
|
|
19
|
+
await dbCache.flushAll();
|
|
20
|
+
};
|
|
21
|
+
|
|
12
22
|
/**
|
|
13
23
|
* The `clearCache` function is used to clear cache files based on different
|
|
14
24
|
* parameters and directories.
|
|
@@ -18,6 +28,8 @@ const clearCacheDir = (dir) => {
|
|
|
18
28
|
* @returns String[] list of files removed from the cache.
|
|
19
29
|
*/
|
|
20
30
|
const clearCache = async (params, node, blockletManager) => {
|
|
31
|
+
await clearDBCache();
|
|
32
|
+
|
|
21
33
|
const baseDir = path.dirname(node.dataDirs.core);
|
|
22
34
|
|
|
23
35
|
// reset cache by pattern
|
package/lib/util/launcher.js
CHANGED
|
@@ -15,6 +15,7 @@ const {
|
|
|
15
15
|
getAvatarByUrl,
|
|
16
16
|
getAppAvatarUrl,
|
|
17
17
|
} = require('@abtnode/util/lib/user');
|
|
18
|
+
const { CustomError } = require('@blocklet/error');
|
|
18
19
|
const { createLaunchBlockletHandler, createServerlessInstallGuard } = require('@abtnode/auth/lib/server');
|
|
19
20
|
const { getDisplayName } = require('@blocklet/meta/lib/util');
|
|
20
21
|
const { fromAppDid } = require('@arcblock/did-ext');
|
|
@@ -24,7 +25,7 @@ const { getBlockletLogos } = require('@abtnode/util/lib/blocklet');
|
|
|
24
25
|
const { PASSPORT_SOURCE, PASSPORT_LOG_ACTION, PASSPORT_ISSUE_ACTION } = require('@abtnode/constant');
|
|
25
26
|
|
|
26
27
|
const { getLauncherUser, getLauncherSession: getLauncherSessionRaw, doRequest } = require('@abtnode/auth/lib/launcher');
|
|
27
|
-
const { createAuthToken, getPassportStatusEndpoint } = require('@abtnode/auth/lib/auth');
|
|
28
|
+
const { createAuthToken, getPassportStatusEndpoint, messages } = require('@abtnode/auth/lib/auth');
|
|
28
29
|
const { createPassportVC, createPassport, createUserPassport } = require('@abtnode/auth/lib/passport');
|
|
29
30
|
|
|
30
31
|
const { LOGIN_PROVIDER, fromBlockletStatus } = require('@blocklet/constant');
|
|
@@ -245,7 +246,7 @@ const setupAppOwner = async ({ node, sessionId, justCreate = false, context, pro
|
|
|
245
246
|
appOwnerProfile.avatar = await extractUserAvatar(avatarBase64, { dataDir });
|
|
246
247
|
logger.info('Create owner from launcher for blocklet', { appDid, ownerDid, ownerPk, sessionId, appOwnerProfile });
|
|
247
248
|
} else {
|
|
248
|
-
user = await node.getUser({ teamDid: info.did, user: { did: userDid } });
|
|
249
|
+
user = await node.getUser({ teamDid: info.did, user: { did: userDid }, options: { enableConnectedAccount: true } });
|
|
249
250
|
if (!user) {
|
|
250
251
|
throw new Error(`Owner user not found in server: ${userDid}`);
|
|
251
252
|
}
|
|
@@ -537,6 +538,68 @@ const launchBlockletByLauncher = async (node, extraParams, context) => {
|
|
|
537
538
|
return data;
|
|
538
539
|
};
|
|
539
540
|
|
|
541
|
+
const launchBlockletWithoutWallet = async (node, extraParams, context) => {
|
|
542
|
+
logger.debug('launchBlockletWithoutWallet', { extraParams, context });
|
|
543
|
+
|
|
544
|
+
extraParams.locale = context.locale || 'en';
|
|
545
|
+
|
|
546
|
+
const { blockletMetaUrl, title, description, locale } = extraParams;
|
|
547
|
+
|
|
548
|
+
if (!blockletMetaUrl && !title && !description) {
|
|
549
|
+
logger.error('blockletMetaUrl | title + description must be provided');
|
|
550
|
+
throw new CustomError(400, messages.missingBlockletUrl[locale]);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
const blocklet = blockletMetaUrl
|
|
554
|
+
? await node.getBlockletMetaFromUrl({ url: blockletMetaUrl, checkPrice: true })
|
|
555
|
+
: {
|
|
556
|
+
meta: {
|
|
557
|
+
title,
|
|
558
|
+
description,
|
|
559
|
+
},
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
if (!blocklet) {
|
|
563
|
+
throw new Error('Blocklet not found');
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// generate app key pair
|
|
567
|
+
const type = (blocklet?.meta?.environments || []).find((x) => x.name === 'CHAIN_TYPE')?.default || 'default';
|
|
568
|
+
const appWallet = fromRandom(type);
|
|
569
|
+
|
|
570
|
+
// use the server user to set up the app owner, this account only allows using the passkey to login
|
|
571
|
+
const { user } = context;
|
|
572
|
+
|
|
573
|
+
let data = {};
|
|
574
|
+
|
|
575
|
+
const handler = createLaunchBlockletHandler(node, 'session');
|
|
576
|
+
await handler(
|
|
577
|
+
{
|
|
578
|
+
extraParams,
|
|
579
|
+
userDid: user.did,
|
|
580
|
+
claims: [
|
|
581
|
+
{
|
|
582
|
+
type: 'keyPair',
|
|
583
|
+
secret: appWallet.secretKey,
|
|
584
|
+
userDid: user.did,
|
|
585
|
+
userPk: user.pk,
|
|
586
|
+
},
|
|
587
|
+
],
|
|
588
|
+
didwallet: {
|
|
589
|
+
os: 'managed',
|
|
590
|
+
version,
|
|
591
|
+
},
|
|
592
|
+
updateSession: (updates) => {
|
|
593
|
+
data = merge(data, updates);
|
|
594
|
+
},
|
|
595
|
+
provider: LOGIN_PROVIDER.EMAIL,
|
|
596
|
+
},
|
|
597
|
+
context
|
|
598
|
+
);
|
|
599
|
+
|
|
600
|
+
return data;
|
|
601
|
+
};
|
|
602
|
+
|
|
540
603
|
module.exports = {
|
|
541
604
|
consumeServerlessNFT,
|
|
542
605
|
consumeLauncherSession,
|
|
@@ -550,4 +613,5 @@ module.exports = {
|
|
|
550
613
|
isBlockletTerminated,
|
|
551
614
|
notifyBlockletUpdated,
|
|
552
615
|
launchBlockletByLauncher,
|
|
616
|
+
launchBlockletWithoutWallet,
|
|
553
617
|
};
|
package/lib/util/router.js
CHANGED
|
@@ -11,6 +11,7 @@ const uniq = require('lodash/uniq');
|
|
|
11
11
|
const isUrl = require('is-url');
|
|
12
12
|
const isIp = require('is-ip');
|
|
13
13
|
const isCidr = require('is-cidr');
|
|
14
|
+
const getPort = require('get-port');
|
|
14
15
|
|
|
15
16
|
const logger = require('@abtnode/logger')('@abtnode/core:router:util');
|
|
16
17
|
|
|
@@ -104,6 +105,23 @@ const getGatewayPorts = (info) => {
|
|
|
104
105
|
};
|
|
105
106
|
};
|
|
106
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Get available gateway ports, if the preferred ports are not available, it will return a random port
|
|
110
|
+
* This is used to avoid port conflict when validating router config
|
|
111
|
+
* Preferred ports are 8080-8085, because these ports are not used by other services
|
|
112
|
+
* @returns {Promise<{httpPort: number, httpsPort: number}>}
|
|
113
|
+
*/
|
|
114
|
+
const getAvailableGatewayPorts = async () => {
|
|
115
|
+
const preferredPorts = [8080, 8081, 8082, 8083, 8084, 8085];
|
|
116
|
+
const httpPort = await getPort({ port: preferredPorts });
|
|
117
|
+
const httpsPort = await getPort({ port: preferredPorts });
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
httpPort,
|
|
121
|
+
httpsPort,
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
|
|
107
125
|
const createProviderInstance = ({ nodeInfo, routerDataDir }) => {
|
|
108
126
|
const providerName = nodeInfo.routing.provider;
|
|
109
127
|
const Provider = getProvider(providerName);
|
|
@@ -149,6 +167,7 @@ module.exports = {
|
|
|
149
167
|
getBlockletDomainGroupName,
|
|
150
168
|
getDidFromDomainGroupName,
|
|
151
169
|
getGatewayPorts,
|
|
170
|
+
getAvailableGatewayPorts,
|
|
152
171
|
updateNFTDomainRecord,
|
|
153
172
|
revokeAndDeleteNFTDomainRecord,
|
|
154
173
|
createProviderInstance,
|
package/lib/validators/theme.js
CHANGED
|
@@ -59,15 +59,15 @@ const actionSchema = Joi.object({
|
|
|
59
59
|
});
|
|
60
60
|
|
|
61
61
|
const paletteSchema = Joi.object({
|
|
62
|
-
mode: Joi.string().valid('light', 'dark').
|
|
63
|
-
primary: paletteColorSchema.
|
|
64
|
-
secondary: paletteColorSchema.
|
|
65
|
-
error: paletteColorSchema.
|
|
66
|
-
warning: paletteColorSchema.
|
|
67
|
-
info: paletteColorSchema.
|
|
68
|
-
success: paletteColorSchema.
|
|
69
|
-
grey: greySchema.
|
|
70
|
-
text: textSchema.
|
|
62
|
+
mode: Joi.string().valid('light', 'dark').optional(),
|
|
63
|
+
primary: paletteColorSchema.optional(),
|
|
64
|
+
secondary: paletteColorSchema.optional(),
|
|
65
|
+
error: paletteColorSchema.optional(),
|
|
66
|
+
warning: paletteColorSchema.optional(),
|
|
67
|
+
info: paletteColorSchema.optional(),
|
|
68
|
+
success: paletteColorSchema.optional(),
|
|
69
|
+
grey: greySchema.optional(),
|
|
70
|
+
text: textSchema.optional(),
|
|
71
71
|
divider: colorString.optional(),
|
|
72
72
|
background: backgroundSchema.optional(),
|
|
73
73
|
common: commonSchema.optional(),
|
|
@@ -148,6 +148,7 @@ const muiThemeSchema = Joi.object({
|
|
|
148
148
|
}).options({ allowUnknown: true, stripUnknown: true });
|
|
149
149
|
|
|
150
150
|
const blockletThemeSchema = Joi.object({
|
|
151
|
+
common: muiThemeSchema.required(),
|
|
151
152
|
light: muiThemeSchema.required(),
|
|
152
153
|
dark: muiThemeSchema.required(),
|
|
153
154
|
prefer: Joi.string().valid('light', 'dark', 'system').default('light'),
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.16.45-beta-
|
|
6
|
+
"version": "1.16.45-beta-20250618-073451-6e48fb62",
|
|
7
7
|
"description": "",
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"files": [
|
|
@@ -19,22 +19,22 @@
|
|
|
19
19
|
"author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
|
|
20
20
|
"license": "Apache-2.0",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@abtnode/analytics": "1.16.45-beta-
|
|
23
|
-
"@abtnode/auth": "1.16.45-beta-
|
|
24
|
-
"@abtnode/certificate-manager": "1.16.45-beta-
|
|
25
|
-
"@abtnode/client": "1.16.45-beta-
|
|
26
|
-
"@abtnode/constant": "1.16.45-beta-
|
|
27
|
-
"@abtnode/cron": "1.16.45-beta-
|
|
28
|
-
"@abtnode/db-cache": "1.16.45-beta-
|
|
29
|
-
"@abtnode/docker-utils": "1.16.45-beta-
|
|
30
|
-
"@abtnode/logger": "1.16.45-beta-
|
|
31
|
-
"@abtnode/models": "1.16.45-beta-
|
|
32
|
-
"@abtnode/queue": "1.16.45-beta-
|
|
33
|
-
"@abtnode/rbac": "1.16.45-beta-
|
|
34
|
-
"@abtnode/router-provider": "1.16.45-beta-
|
|
35
|
-
"@abtnode/static-server": "1.16.45-beta-
|
|
36
|
-
"@abtnode/timemachine": "1.16.45-beta-
|
|
37
|
-
"@abtnode/util": "1.16.45-beta-
|
|
22
|
+
"@abtnode/analytics": "1.16.45-beta-20250618-073451-6e48fb62",
|
|
23
|
+
"@abtnode/auth": "1.16.45-beta-20250618-073451-6e48fb62",
|
|
24
|
+
"@abtnode/certificate-manager": "1.16.45-beta-20250618-073451-6e48fb62",
|
|
25
|
+
"@abtnode/client": "1.16.45-beta-20250618-073451-6e48fb62",
|
|
26
|
+
"@abtnode/constant": "1.16.45-beta-20250618-073451-6e48fb62",
|
|
27
|
+
"@abtnode/cron": "1.16.45-beta-20250618-073451-6e48fb62",
|
|
28
|
+
"@abtnode/db-cache": "1.16.45-beta-20250618-073451-6e48fb62",
|
|
29
|
+
"@abtnode/docker-utils": "1.16.45-beta-20250618-073451-6e48fb62",
|
|
30
|
+
"@abtnode/logger": "1.16.45-beta-20250618-073451-6e48fb62",
|
|
31
|
+
"@abtnode/models": "1.16.45-beta-20250618-073451-6e48fb62",
|
|
32
|
+
"@abtnode/queue": "1.16.45-beta-20250618-073451-6e48fb62",
|
|
33
|
+
"@abtnode/rbac": "1.16.45-beta-20250618-073451-6e48fb62",
|
|
34
|
+
"@abtnode/router-provider": "1.16.45-beta-20250618-073451-6e48fb62",
|
|
35
|
+
"@abtnode/static-server": "1.16.45-beta-20250618-073451-6e48fb62",
|
|
36
|
+
"@abtnode/timemachine": "1.16.45-beta-20250618-073451-6e48fb62",
|
|
37
|
+
"@abtnode/util": "1.16.45-beta-20250618-073451-6e48fb62",
|
|
38
38
|
"@arcblock/did": "1.20.14",
|
|
39
39
|
"@arcblock/did-auth": "1.20.14",
|
|
40
40
|
"@arcblock/did-ext": "1.20.14",
|
|
@@ -45,15 +45,15 @@
|
|
|
45
45
|
"@arcblock/pm2-events": "^0.0.5",
|
|
46
46
|
"@arcblock/validator": "1.20.14",
|
|
47
47
|
"@arcblock/vc": "1.20.14",
|
|
48
|
-
"@blocklet/constant": "1.16.45-beta-
|
|
49
|
-
"@blocklet/did-space-js": "^1.0.
|
|
50
|
-
"@blocklet/env": "1.16.45-beta-
|
|
48
|
+
"@blocklet/constant": "1.16.45-beta-20250618-073451-6e48fb62",
|
|
49
|
+
"@blocklet/did-space-js": "^1.0.62",
|
|
50
|
+
"@blocklet/env": "1.16.45-beta-20250618-073451-6e48fb62",
|
|
51
51
|
"@blocklet/error": "^0.2.5",
|
|
52
|
-
"@blocklet/meta": "1.16.45-beta-
|
|
53
|
-
"@blocklet/resolver": "1.16.45-beta-
|
|
54
|
-
"@blocklet/sdk": "1.16.45-beta-
|
|
55
|
-
"@blocklet/store": "1.16.45-beta-
|
|
56
|
-
"@blocklet/theme": "^2.13.
|
|
52
|
+
"@blocklet/meta": "1.16.45-beta-20250618-073451-6e48fb62",
|
|
53
|
+
"@blocklet/resolver": "1.16.45-beta-20250618-073451-6e48fb62",
|
|
54
|
+
"@blocklet/sdk": "1.16.45-beta-20250618-073451-6e48fb62",
|
|
55
|
+
"@blocklet/store": "1.16.45-beta-20250618-073451-6e48fb62",
|
|
56
|
+
"@blocklet/theme": "^2.13.70",
|
|
57
57
|
"@fidm/x509": "^1.2.1",
|
|
58
58
|
"@ocap/mcrypto": "1.20.14",
|
|
59
59
|
"@ocap/util": "1.20.14",
|
|
@@ -116,5 +116,5 @@
|
|
|
116
116
|
"jest": "^29.7.0",
|
|
117
117
|
"unzipper": "^0.10.11"
|
|
118
118
|
},
|
|
119
|
-
"gitHead": "
|
|
119
|
+
"gitHead": "afbde3291aeb63211f12b73b6c8cf5dc422cbf7f"
|
|
120
120
|
}
|