@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 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
- let params = {};
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 = { ...params, ...gateway };
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
- for (let attempt = 0; attempt < 10; attempt++) {
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: 6000,
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.60","@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.66","@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
+ 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
 
@@ -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),
@@ -1444,7 +1444,7 @@ module.exports = function getRouterHelpers({
1444
1444
  entityType: 'server',
1445
1445
  severity: 'error',
1446
1446
  source: 'system',
1447
- sticky: true,
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');
@@ -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
- getGatewayPorts,
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 { httpPort, httpsPort } = getGatewayPorts(info);
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
  }
@@ -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 userState.getUsersByDids({
226
- dids: userDids,
227
- query: {
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;
@@ -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
@@ -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
  };
@@ -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,
@@ -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').required(),
63
- primary: paletteColorSchema.required(),
64
- secondary: paletteColorSchema.required(),
65
- error: paletteColorSchema.required(),
66
- warning: paletteColorSchema.required(),
67
- info: paletteColorSchema.required(),
68
- success: paletteColorSchema.required(),
69
- grey: greySchema.required(),
70
- text: textSchema.required(),
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-20250612-231219-481217be",
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-20250612-231219-481217be",
23
- "@abtnode/auth": "1.16.45-beta-20250612-231219-481217be",
24
- "@abtnode/certificate-manager": "1.16.45-beta-20250612-231219-481217be",
25
- "@abtnode/client": "1.16.45-beta-20250612-231219-481217be",
26
- "@abtnode/constant": "1.16.45-beta-20250612-231219-481217be",
27
- "@abtnode/cron": "1.16.45-beta-20250612-231219-481217be",
28
- "@abtnode/db-cache": "1.16.45-beta-20250612-231219-481217be",
29
- "@abtnode/docker-utils": "1.16.45-beta-20250612-231219-481217be",
30
- "@abtnode/logger": "1.16.45-beta-20250612-231219-481217be",
31
- "@abtnode/models": "1.16.45-beta-20250612-231219-481217be",
32
- "@abtnode/queue": "1.16.45-beta-20250612-231219-481217be",
33
- "@abtnode/rbac": "1.16.45-beta-20250612-231219-481217be",
34
- "@abtnode/router-provider": "1.16.45-beta-20250612-231219-481217be",
35
- "@abtnode/static-server": "1.16.45-beta-20250612-231219-481217be",
36
- "@abtnode/timemachine": "1.16.45-beta-20250612-231219-481217be",
37
- "@abtnode/util": "1.16.45-beta-20250612-231219-481217be",
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-20250612-231219-481217be",
49
- "@blocklet/did-space-js": "^1.0.60",
50
- "@blocklet/env": "1.16.45-beta-20250612-231219-481217be",
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-20250612-231219-481217be",
53
- "@blocklet/resolver": "1.16.45-beta-20250612-231219-481217be",
54
- "@blocklet/sdk": "1.16.45-beta-20250612-231219-481217be",
55
- "@blocklet/store": "1.16.45-beta-20250612-231219-481217be",
56
- "@blocklet/theme": "^2.13.66",
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": "01a3fb21f16a515e2565d12797baeda5fdf585b0"
119
+ "gitHead": "afbde3291aeb63211f12b73b6c8cf5dc422cbf7f"
120
120
  }