@abtnode/core 1.8.67 → 1.8.68-beta-500af7e5
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/blocklet/downloader/bundle-downloader.js +6 -1
- package/lib/blocklet/manager/disk.js +158 -64
- package/lib/blocklet/manager/helper/install-from-backup.js +4 -4
- package/lib/blocklet/storage/backup/blocklet-extras.js +15 -19
- package/lib/blocklet/storage/backup/spaces.js +39 -11
- package/lib/blocklet/storage/restore/base.js +10 -9
- package/lib/blocklet/storage/restore/blocklet-extras.js +21 -16
- package/lib/blocklet/storage/restore/blocklets.js +5 -2
- package/lib/blocklet/storage/restore/spaces.js +45 -38
- package/lib/event.js +18 -2
- package/lib/index.js +21 -3
- package/lib/router/helper.js +7 -5
- package/lib/router/index.js +31 -0
- package/lib/states/audit-log.js +5 -3
- package/lib/states/blocklet.js +21 -5
- package/lib/states/node.js +5 -2
- package/lib/util/blocklet.js +111 -19
- package/lib/util/index.js +24 -0
- package/lib/validators/node.js +1 -0
- package/lib/webhook/index.js +1 -1
- package/package.json +26 -27
- /package/lib/{queue.js → util/queue.js} +0 -0
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef {{
|
|
3
|
-
* did
|
|
3
|
+
* did: string
|
|
4
|
+
* event: import('events').EventEmitter,
|
|
5
|
+
* userDid: string,
|
|
6
|
+
* referrer: string,
|
|
4
7
|
* }} SpaceBackupInput
|
|
5
8
|
*/
|
|
6
9
|
|
|
7
10
|
const { isValid } = require('@arcblock/did');
|
|
8
|
-
const { BLOCKLET_CONFIGURABLE_KEY } = require('@blocklet/constant');
|
|
11
|
+
const { BLOCKLET_CONFIGURABLE_KEY, BlockletEvents } = require('@blocklet/constant');
|
|
9
12
|
const { getBlockletInfo } = require('@blocklet/meta');
|
|
10
|
-
const { SpaceClient,
|
|
13
|
+
const { SpaceClient, BackupBlockletCommand } = require('@did-space/client');
|
|
11
14
|
const { ensureDirSync } = require('fs-extra');
|
|
12
15
|
const { isEmpty } = require('lodash');
|
|
13
|
-
const { join } = require('path');
|
|
16
|
+
const { join, basename } = require('path');
|
|
17
|
+
|
|
18
|
+
const logger = require('@abtnode/logger')('@abtnode/core:storage:backup');
|
|
19
|
+
|
|
14
20
|
const states = require('../../../states');
|
|
15
21
|
const { AuditLogBackup } = require('./audit-log');
|
|
16
22
|
const { BlockletBackup } = require('./blocklet');
|
|
@@ -131,12 +137,22 @@ class SpacesBackup {
|
|
|
131
137
|
}
|
|
132
138
|
|
|
133
139
|
async export() {
|
|
140
|
+
this.input.event.emit(BlockletEvents.backupProgress, {
|
|
141
|
+
did: this.input.did,
|
|
142
|
+
message: 'Preparing data for backup...',
|
|
143
|
+
progress: 15,
|
|
144
|
+
});
|
|
134
145
|
await Promise.all(
|
|
135
146
|
this.storages.map((storage) => {
|
|
136
147
|
storage.ensureParams(this);
|
|
137
148
|
return storage.export();
|
|
138
149
|
})
|
|
139
150
|
);
|
|
151
|
+
this.input.event.emit(BlockletEvents.backupProgress, {
|
|
152
|
+
did: this.input.did,
|
|
153
|
+
message: 'Data ready, start backup...',
|
|
154
|
+
progress: 20,
|
|
155
|
+
});
|
|
140
156
|
}
|
|
141
157
|
|
|
142
158
|
async syncToSpaces() {
|
|
@@ -146,22 +162,34 @@ class SpacesBackup {
|
|
|
146
162
|
wallet,
|
|
147
163
|
});
|
|
148
164
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
165
|
+
const { errorCount, message } = await spaceClient.send(
|
|
166
|
+
new BackupBlockletCommand({
|
|
167
|
+
appDid: this.blocklet.appDid,
|
|
152
168
|
source: join(this.blockletBackupDir, '/'),
|
|
153
|
-
target: join('.did-objects', this.blocklet.appDid),
|
|
154
169
|
debug: true,
|
|
155
|
-
concurrency:
|
|
156
|
-
retryCount:
|
|
170
|
+
concurrency: 32,
|
|
171
|
+
retryCount: 10,
|
|
157
172
|
filter: (object) => {
|
|
173
|
+
// FIXME: @yejianchao 这里需要更完整的黑名单
|
|
158
174
|
return object.name !== '.DS_Store';
|
|
159
175
|
},
|
|
176
|
+
onProgress: (data) => {
|
|
177
|
+
logger.info('backup progress', { appDid: this.input.did, data });
|
|
178
|
+
const percent = (data.completed * 100) / data.total;
|
|
179
|
+
this.input.event.emit(BlockletEvents.backupProgress, {
|
|
180
|
+
did: this.input.did,
|
|
181
|
+
message: `Uploaded file ${basename(data.key)} (${data.completed}/${data.total})`,
|
|
182
|
+
// 0.8 是因为上传文件到 spaces 占进度的 80%,+ 20 是因为需要累加之前的进度
|
|
183
|
+
progress: +Math.ceil(percent * 0.8).toFixed(2) + 20,
|
|
184
|
+
});
|
|
185
|
+
},
|
|
186
|
+
userDid: this.input.userDid,
|
|
187
|
+
referrer: this.input.referrer,
|
|
160
188
|
})
|
|
161
189
|
);
|
|
162
190
|
|
|
163
191
|
if (errorCount !== 0) {
|
|
164
|
-
throw new Error(`Sync to spaces encountered ${errorCount} error`);
|
|
192
|
+
throw new Error(`Sync to spaces encountered ${errorCount} error: ${message}`);
|
|
165
193
|
}
|
|
166
194
|
}
|
|
167
195
|
|
|
@@ -13,14 +13,6 @@ class BaseRestore {
|
|
|
13
13
|
*/
|
|
14
14
|
blockletRestoreDir;
|
|
15
15
|
|
|
16
|
-
/**
|
|
17
|
-
*
|
|
18
|
-
* @description spaces 的 endpoint
|
|
19
|
-
* @type {import('@ocap/wallet').WalletObject}
|
|
20
|
-
* @memberof BaseRestore
|
|
21
|
-
*/
|
|
22
|
-
blockletWallet;
|
|
23
|
-
|
|
24
16
|
/**
|
|
25
17
|
*
|
|
26
18
|
* @description server 的数据目录
|
|
@@ -41,13 +33,22 @@ class BaseRestore {
|
|
|
41
33
|
*/
|
|
42
34
|
ensureParams(spaces) {
|
|
43
35
|
this.blockletRestoreDir = spaces.blockletRestoreDir;
|
|
44
|
-
this.blockletWallet = spaces.blockletWallet;
|
|
45
36
|
this.serverDataDir = spaces.serverDataDir;
|
|
46
37
|
}
|
|
47
38
|
|
|
48
39
|
async import() {
|
|
49
40
|
throw new Error('not implemented');
|
|
50
41
|
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Generate params for BlockletManager to install
|
|
45
|
+
*
|
|
46
|
+
* @return {object}
|
|
47
|
+
* @memberof BaseRestore
|
|
48
|
+
*/
|
|
49
|
+
getInstallParams() {
|
|
50
|
+
return {};
|
|
51
|
+
}
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
module.exports = {
|
|
@@ -2,6 +2,7 @@ const { removeSync, outputJsonSync, readJSONSync } = require('fs-extra');
|
|
|
2
2
|
const { cloneDeep, isArray } = require('lodash');
|
|
3
3
|
const { join } = require('path');
|
|
4
4
|
const security = require('@abtnode/util/lib/security');
|
|
5
|
+
const { BLOCKLET_CONFIGURABLE_KEY } = require('@blocklet/constant');
|
|
5
6
|
const { BaseRestore } = require('./base');
|
|
6
7
|
|
|
7
8
|
class BlockletExtrasRestore extends BaseRestore {
|
|
@@ -24,7 +25,6 @@ class BlockletExtrasRestore extends BaseRestore {
|
|
|
24
25
|
* @type {import('@abtnode/client').BlockletState}
|
|
25
26
|
*/
|
|
26
27
|
const blockletExtra = readJSONSync(join(this.blockletRestoreDir, this.filename));
|
|
27
|
-
|
|
28
28
|
return this.cleanData(blockletExtra);
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -32,7 +32,6 @@ class BlockletExtrasRestore extends BaseRestore {
|
|
|
32
32
|
*
|
|
33
33
|
* @description 清理数据并加密
|
|
34
34
|
* @param {import('@abtnode/client').BlockletState} blockletExtraInput
|
|
35
|
-
* @return {Promise<void>}
|
|
36
35
|
* @memberof BlockletExtrasRestore
|
|
37
36
|
*/
|
|
38
37
|
async cleanData(blockletExtraInput) {
|
|
@@ -43,7 +42,7 @@ class BlockletExtrasRestore extends BaseRestore {
|
|
|
43
42
|
const currentBlockletExtra = queue.pop();
|
|
44
43
|
|
|
45
44
|
// 加解密
|
|
46
|
-
this.
|
|
45
|
+
this.decrypt(currentBlockletExtra.configs);
|
|
47
46
|
|
|
48
47
|
if (currentBlockletExtra?.children) {
|
|
49
48
|
queue.push(...currentBlockletExtra.children);
|
|
@@ -55,31 +54,37 @@ class BlockletExtrasRestore extends BaseRestore {
|
|
|
55
54
|
|
|
56
55
|
/**
|
|
57
56
|
*
|
|
58
|
-
* @description
|
|
57
|
+
* @description 解密加密的数据
|
|
59
58
|
* @param {import('@abtnode/client').ConfigEntry[]} configs
|
|
60
59
|
* @return {void}
|
|
61
60
|
* @memberof BlockletExtrasRestore
|
|
62
61
|
*/
|
|
63
|
-
|
|
62
|
+
decrypt(configs) {
|
|
64
63
|
if (!configs || !isArray(configs)) {
|
|
65
64
|
return;
|
|
66
65
|
}
|
|
67
66
|
|
|
67
|
+
const { appDid, password } = this.input;
|
|
68
68
|
for (const config of configs) {
|
|
69
|
-
// secure 为 true 的配置才需要被加密保存上次到 did spaces
|
|
70
69
|
if (config.secure) {
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const decryptByBlocklet = security.decrypt(
|
|
75
|
-
encryptByBlocklet,
|
|
76
|
-
this.blockletWallet.address,
|
|
77
|
-
Buffer.from(this.blockletWallet.secretKey)
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
config.value = decryptByBlocklet;
|
|
70
|
+
const encrypted = config.value;
|
|
71
|
+
const decrypted = security.decrypt(encrypted, appDid, Buffer.from(password));
|
|
72
|
+
config.value = decrypted;
|
|
81
73
|
}
|
|
82
74
|
}
|
|
75
|
+
|
|
76
|
+
const config = configs.find((x) => x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK);
|
|
77
|
+
if (!config) {
|
|
78
|
+
throw new Error(`Invalid blocklet backup file, no ${BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK} found`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
this.appSk = config.value;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getInstallParams() {
|
|
85
|
+
return {
|
|
86
|
+
appSk: this.appSk,
|
|
87
|
+
};
|
|
83
88
|
}
|
|
84
89
|
}
|
|
85
90
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { existsSync, remove } = require('fs-extra');
|
|
1
|
+
const { existsSync, remove, ensureDirSync } = require('fs-extra');
|
|
2
2
|
const { join } = require('path');
|
|
3
3
|
const fg = require('fast-glob');
|
|
4
4
|
const { BaseRestore } = require('./base');
|
|
@@ -10,8 +10,11 @@ class BlockletsRestore extends BaseRestore {
|
|
|
10
10
|
async import() {
|
|
11
11
|
const blockletsDir = join(this.blockletRestoreDir, this.filename);
|
|
12
12
|
|
|
13
|
+
// blockletsDir 可以不存在, 因为还原的 blocklet 可能所有的组件都是来自 store 的
|
|
13
14
|
if (!existsSync(blockletsDir)) {
|
|
14
|
-
|
|
15
|
+
// FIXME: 如果文件夹不存在,需要创建文件夹,因为 installFromBackup 未做容错处理
|
|
16
|
+
ensureDirSync(blockletsDir);
|
|
17
|
+
return;
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
const paths = await fg('**/*.zip', {
|
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef {{
|
|
3
|
+
* appDid: string;
|
|
3
4
|
* endpoint: string;
|
|
4
|
-
*
|
|
5
|
+
* password: Buffer;
|
|
6
|
+
* delegation: string;
|
|
7
|
+
* wallet: import('@ocap/wallet').WalletObject;
|
|
8
|
+
* event: import('events').EventEmitter,
|
|
9
|
+
* userDid: string,
|
|
10
|
+
* referrer: string,
|
|
5
11
|
* }} SpaceRestoreInput
|
|
6
12
|
*/
|
|
7
13
|
|
|
8
|
-
const { SpaceClient, SyncFolderPullCommand } = require('@did-space/client');
|
|
9
|
-
const { types } = require('@ocap/mcrypto');
|
|
10
|
-
const { fromSecretKey, WalletType } = require('@ocap/wallet');
|
|
11
|
-
const { ensureDirSync, existsSync, rmdirSync } = require('fs-extra');
|
|
12
|
-
const { join } = require('path');
|
|
13
14
|
const validUrl = require('valid-url');
|
|
15
|
+
const { BlockletEvents } = require('@blocklet/constant');
|
|
16
|
+
const { SpaceClient, RestoreBlockletCommand } = require('@did-space/client');
|
|
17
|
+
const { ensureDirSync, existsSync, rmdirSync } = require('fs-extra');
|
|
18
|
+
const { join, basename } = require('path');
|
|
19
|
+
|
|
20
|
+
const logger = require('@abtnode/logger')('@abtnode/core:storage:restore');
|
|
21
|
+
|
|
14
22
|
const { BlockletExtrasRestore } = require('./blocklet-extras');
|
|
15
23
|
const { BlockletsRestore } = require('./blocklets');
|
|
16
24
|
|
|
@@ -37,14 +45,6 @@ class SpacesRestore {
|
|
|
37
45
|
*/
|
|
38
46
|
serverDataDir;
|
|
39
47
|
|
|
40
|
-
/**
|
|
41
|
-
*
|
|
42
|
-
* @description spaces 的 endpoint
|
|
43
|
-
* @type {import('@ocap/wallet').WalletObject}
|
|
44
|
-
* @memberof SpacesRestore
|
|
45
|
-
*/
|
|
46
|
-
blockletWallet;
|
|
47
|
-
|
|
48
48
|
storages;
|
|
49
49
|
|
|
50
50
|
/**
|
|
@@ -72,68 +72,75 @@ class SpacesRestore {
|
|
|
72
72
|
|
|
73
73
|
async initialize() {
|
|
74
74
|
this.serverDataDir = process.env.ABT_NODE_DATA_DIR;
|
|
75
|
-
this.blockletWallet = await this.getBlockletWallet();
|
|
76
75
|
|
|
77
|
-
if (!this.input.endpoint.includes(this.
|
|
78
|
-
throw new Error(`endpoint and blocklet.appDid(${this.
|
|
76
|
+
if (!this.input.endpoint.includes(this.input.appDid)) {
|
|
77
|
+
throw new Error(`endpoint and blocklet.appDid(${this.input.appDid}) do not match`);
|
|
79
78
|
}
|
|
80
79
|
|
|
81
|
-
this.blockletRestoreDir = join(process.env.ABT_NODE_DATA_DIR, 'tmp/restore', this.
|
|
80
|
+
this.blockletRestoreDir = join(process.env.ABT_NODE_DATA_DIR, 'tmp/restore', this.input.appDid);
|
|
82
81
|
if (existsSync(this.blockletRestoreDir)) {
|
|
83
82
|
rmdirSync(this.blockletRestoreDir, { recursive: true });
|
|
84
83
|
}
|
|
85
84
|
ensureDirSync(this.blockletRestoreDir);
|
|
86
85
|
}
|
|
87
86
|
|
|
88
|
-
/**
|
|
89
|
-
*
|
|
90
|
-
* @returns {Promise<void>}
|
|
91
|
-
* @memberof SpacesRestore
|
|
92
|
-
*/
|
|
93
87
|
async restore() {
|
|
94
88
|
await this.initialize();
|
|
95
89
|
await this.syncFromSpaces();
|
|
96
90
|
await this.import();
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
async getBlockletWallet() {
|
|
100
|
-
// @FIXME: blocklet 钱包类型如何得知呢?
|
|
101
|
-
const wallet = fromSecretKey(this.input.blockletSecretKey, WalletType({ role: types.RoleType.ROLE_APPLICATION }));
|
|
102
91
|
|
|
103
|
-
return
|
|
92
|
+
return this.storages.map((x) => x.getInstallParams());
|
|
104
93
|
}
|
|
105
94
|
|
|
106
95
|
async syncFromSpaces() {
|
|
107
|
-
const { endpoint } = this.input;
|
|
108
|
-
const wallet = await this.getBlockletWallet();
|
|
96
|
+
const { endpoint, wallet, delegation } = this.input;
|
|
109
97
|
|
|
110
98
|
const spaceClient = new SpaceClient({
|
|
111
99
|
endpoint,
|
|
100
|
+
delegation,
|
|
112
101
|
wallet,
|
|
113
102
|
});
|
|
114
103
|
|
|
115
|
-
const { errorCount } = await spaceClient.send(
|
|
116
|
-
new
|
|
117
|
-
|
|
118
|
-
target: this.blockletRestoreDir,
|
|
104
|
+
const { errorCount, message } = await spaceClient.send(
|
|
105
|
+
new RestoreBlockletCommand({
|
|
106
|
+
appDid: this.input.appDid,
|
|
107
|
+
target: join(this.blockletRestoreDir, '/'),
|
|
119
108
|
debug: true,
|
|
120
|
-
concurrency:
|
|
121
|
-
retryCount:
|
|
109
|
+
concurrency: 32,
|
|
110
|
+
retryCount: 10,
|
|
111
|
+
onProgress: (data) => {
|
|
112
|
+
logger.info('restore progress', { appDid: this.input.appDid, data });
|
|
113
|
+
this.input.event.emit(BlockletEvents.restoreProgress, {
|
|
114
|
+
did: this.input.appDid,
|
|
115
|
+
message: `Downloaded file ${basename(data.key)} (${data.completed}/${data.total})`,
|
|
116
|
+
});
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
userDid: this.input.userDid,
|
|
120
|
+
referrer: this.input.referrer,
|
|
122
121
|
})
|
|
123
122
|
);
|
|
124
123
|
|
|
125
124
|
if (errorCount !== 0) {
|
|
126
|
-
throw new Error(`Sync from spaces encountered ${errorCount} error`);
|
|
125
|
+
throw new Error(`Sync from spaces encountered ${errorCount} error: ${message}`);
|
|
127
126
|
}
|
|
128
127
|
}
|
|
129
128
|
|
|
130
129
|
async import() {
|
|
130
|
+
this.input.event.emit(BlockletEvents.restoreProgress, {
|
|
131
|
+
did: this.input.appDid,
|
|
132
|
+
message: 'Preparing to import data...',
|
|
133
|
+
});
|
|
131
134
|
await Promise.all(
|
|
132
135
|
this.storages.map((storage) => {
|
|
133
136
|
storage.ensureParams(this);
|
|
134
137
|
return storage.import();
|
|
135
138
|
})
|
|
136
139
|
);
|
|
140
|
+
this.input.event.emit(BlockletEvents.restoreProgress, {
|
|
141
|
+
did: this.input.appDid,
|
|
142
|
+
message: 'Importing data successfully...',
|
|
143
|
+
});
|
|
137
144
|
}
|
|
138
145
|
}
|
|
139
146
|
|
package/lib/event.js
CHANGED
|
@@ -81,7 +81,7 @@ module.exports = ({
|
|
|
81
81
|
}
|
|
82
82
|
};
|
|
83
83
|
|
|
84
|
-
const
|
|
84
|
+
const handleBlockletInstall = async (name, { blocklet, context }) => {
|
|
85
85
|
try {
|
|
86
86
|
const changed = await ensureBlockletRouting(blocklet, context);
|
|
87
87
|
if (changed) {
|
|
@@ -169,11 +169,17 @@ module.exports = ({
|
|
|
169
169
|
});
|
|
170
170
|
};
|
|
171
171
|
|
|
172
|
+
/**
|
|
173
|
+
*
|
|
174
|
+
* @description 事件必须注册在这里才能被发布出去
|
|
175
|
+
* @param {string} eventName
|
|
176
|
+
* @param {any} payload
|
|
177
|
+
*/
|
|
172
178
|
const handleBlockletEvent = async (eventName, payload) => {
|
|
173
179
|
const blocklet = payload.blocklet || payload;
|
|
174
180
|
|
|
175
181
|
if ([BlockletEvents.installed].includes(eventName)) {
|
|
176
|
-
await
|
|
182
|
+
await handleBlockletInstall(eventName, payload);
|
|
177
183
|
|
|
178
184
|
try {
|
|
179
185
|
await node.createAuditLog({
|
|
@@ -273,6 +279,13 @@ module.exports = ({
|
|
|
273
279
|
}
|
|
274
280
|
};
|
|
275
281
|
|
|
282
|
+
/**
|
|
283
|
+
*
|
|
284
|
+
*
|
|
285
|
+
* @param {*} subject
|
|
286
|
+
* @param {string} event
|
|
287
|
+
* @param {(event: string, data: any) => Promise<void> | void} handler
|
|
288
|
+
*/
|
|
276
289
|
const listen = (subject, event, handler) => subject.on(event, (data) => handler(event, data));
|
|
277
290
|
|
|
278
291
|
[
|
|
@@ -291,6 +304,9 @@ module.exports = ({
|
|
|
291
304
|
BlockletEvents.startFailed,
|
|
292
305
|
BlockletEvents.stopped,
|
|
293
306
|
BlockletEvents.appDidChanged,
|
|
307
|
+
|
|
308
|
+
BlockletEvents.backupProgress,
|
|
309
|
+
BlockletEvents.restoreProgress,
|
|
294
310
|
].forEach((eventName) => {
|
|
295
311
|
listen(blockletManager, eventName, handleBlockletEvent);
|
|
296
312
|
});
|
package/lib/index.js
CHANGED
|
@@ -26,7 +26,7 @@ const Maintain = require('./util/maintain');
|
|
|
26
26
|
const resetNode = require('./util/reset-node');
|
|
27
27
|
const DiskMonitor = require('./util/disk-monitor');
|
|
28
28
|
const StoreUtil = require('./util/store');
|
|
29
|
-
const createQueue = require('./queue');
|
|
29
|
+
const createQueue = require('./util/queue');
|
|
30
30
|
const createEvents = require('./event');
|
|
31
31
|
const pm2Events = require('./blocklet/manager/pm2-events');
|
|
32
32
|
const { createStateReadyQueue, createStateReadyHandler } = require('./util/ready');
|
|
@@ -99,7 +99,25 @@ function ABTNode(options) {
|
|
|
99
99
|
concurrency,
|
|
100
100
|
maxRetries: 3,
|
|
101
101
|
retryDelay: 5000, // retry after 5 seconds
|
|
102
|
-
maxTimeout: 60 * 1000 * 15, // throw timeout error after
|
|
102
|
+
maxTimeout: 60 * 1000 * 15, // throw timeout error after 15 minutes
|
|
103
|
+
id: (job) => (job ? md5(`${job.entity}-${job.action}-${job.id}`) : ''),
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const backupQueue = createQueue({
|
|
108
|
+
daemon: options.daemon,
|
|
109
|
+
name: 'backup_queue',
|
|
110
|
+
dataDir: dataDirs.core,
|
|
111
|
+
onJob: async (job) => {
|
|
112
|
+
if (typeof blockletManager.onJob === 'function') {
|
|
113
|
+
await blockletManager.onJob(job);
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
options: {
|
|
117
|
+
concurrency,
|
|
118
|
+
maxRetries: 3,
|
|
119
|
+
retryDelay: 10000, // retry after 10 seconds
|
|
120
|
+
maxTimeout: 60 * 1000 * 30, // throw timeout error after 30 minutes
|
|
103
121
|
id: (job) => (job ? md5(`${job.entity}-${job.action}-${job.id}`) : ''),
|
|
104
122
|
},
|
|
105
123
|
});
|
|
@@ -129,6 +147,7 @@ function ABTNode(options) {
|
|
|
129
147
|
dataDirs,
|
|
130
148
|
startQueue,
|
|
131
149
|
installQueue,
|
|
150
|
+
backupQueue,
|
|
132
151
|
daemon: options.daemon,
|
|
133
152
|
teamManager,
|
|
134
153
|
});
|
|
@@ -192,7 +211,6 @@ function ABTNode(options) {
|
|
|
192
211
|
|
|
193
212
|
// Blocklet manager
|
|
194
213
|
installBlocklet: blockletManager.install.bind(blockletManager),
|
|
195
|
-
installBlockletFromVc: blockletManager.installBlockletFromVc.bind(blockletManager),
|
|
196
214
|
installComponent: blockletManager.installComponent.bind(blockletManager),
|
|
197
215
|
startBlocklet: blockletManager.start.bind(blockletManager),
|
|
198
216
|
stopBlocklet: blockletManager.stop.bind(blockletManager),
|
package/lib/router/helper.js
CHANGED
|
@@ -26,7 +26,6 @@ const {
|
|
|
26
26
|
NAME_FOR_WELLKNOWN_SITE,
|
|
27
27
|
DEFAULT_HTTP_PORT,
|
|
28
28
|
DEFAULT_HTTPS_PORT,
|
|
29
|
-
NODE_MODES,
|
|
30
29
|
ROUTING_RULE_TYPES,
|
|
31
30
|
CERTIFICATE_EXPIRES_OFFSET,
|
|
32
31
|
DEFAULT_SERVICE_PATH,
|
|
@@ -56,6 +55,7 @@ const {
|
|
|
56
55
|
findInterfacePortByName,
|
|
57
56
|
getWellknownSitePort,
|
|
58
57
|
getServerDidDomain,
|
|
58
|
+
isGatewayCacheEnabled,
|
|
59
59
|
} = require('../util');
|
|
60
60
|
const { getIpDnsDomainForBlocklet, getDidDomainForBlocklet } = require('../util/get-domain-for-blocklet');
|
|
61
61
|
const { getFromCache: getAccessibleExternalNodeIp } = require('../util/get-accessible-external-node-ip');
|
|
@@ -382,7 +382,7 @@ const ensureBlockletCache = async (sites = [], blocklets) => {
|
|
|
382
382
|
const clone = cloneDeep(rule);
|
|
383
383
|
clone.from.pathPrefix = joinUrl(rule.from.pathPrefix, cachePrefix);
|
|
384
384
|
clone.to.cacheGroup = 'blockletProxy';
|
|
385
|
-
clone.to.
|
|
385
|
+
clone.to.targetPrefix = cachePrefix;
|
|
386
386
|
clone.dynamic = true; // mark as dynamic to avoid redundant generated rules
|
|
387
387
|
cacheRules.push(clone);
|
|
388
388
|
});
|
|
@@ -518,7 +518,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
518
518
|
|
|
519
519
|
const ensureDomainCert = async (domain, url) => {
|
|
520
520
|
const cert = await certManager.getByDomain(domain);
|
|
521
|
-
if (!cert) {
|
|
521
|
+
if (!cert || get(cert, 'meta.validTo') <= Date.now()) {
|
|
522
522
|
await downloadCert({
|
|
523
523
|
domain,
|
|
524
524
|
url,
|
|
@@ -1115,7 +1115,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
1115
1115
|
configDir: path.join(dataDirs.router, providerName),
|
|
1116
1116
|
httpPort: nodeInfo.routing.httpPort || DEFAULT_HTTP_PORT,
|
|
1117
1117
|
httpsPort: nodeInfo.routing.httpsPort || DEFAULT_HTTPS_PORT,
|
|
1118
|
-
|
|
1118
|
+
cacheEnabled: isGatewayCacheEnabled(nodeInfo),
|
|
1119
1119
|
}),
|
|
1120
1120
|
getRoutingParams: async () => {
|
|
1121
1121
|
try {
|
|
@@ -1220,7 +1220,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
1220
1220
|
configDir: path.join(dataDirs.router, info.routing.provider),
|
|
1221
1221
|
httpPort: info.routing.httpPort || DEFAULT_HTTP_PORT,
|
|
1222
1222
|
httpsPort: info.routing.httpsPort || DEFAULT_HTTPS_PORT,
|
|
1223
|
-
|
|
1223
|
+
cacheEnabled: isGatewayCacheEnabled(info),
|
|
1224
1224
|
});
|
|
1225
1225
|
await providerInstance.stop();
|
|
1226
1226
|
logger.info('original router stopped:', { provider: info.routing.provider });
|
|
@@ -1408,6 +1408,8 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
1408
1408
|
deleteRoutingRule,
|
|
1409
1409
|
addDomainAlias,
|
|
1410
1410
|
deleteDomainAlias,
|
|
1411
|
+
|
|
1412
|
+
isGatewayCacheEnabled,
|
|
1411
1413
|
};
|
|
1412
1414
|
};
|
|
1413
1415
|
|
package/lib/router/index.js
CHANGED
|
@@ -11,8 +11,11 @@ const {
|
|
|
11
11
|
GATEWAY_REQ_LIMIT,
|
|
12
12
|
} = require('@abtnode/constant');
|
|
13
13
|
const { BLOCKLET_UI_INTERFACES, BLOCKLET_MODES } = require('@blocklet/constant');
|
|
14
|
+
|
|
14
15
|
const logger = require('@abtnode/logger')('@abtnode/core:router');
|
|
15
16
|
|
|
17
|
+
const { isGatewayCacheEnabled } = require('../util');
|
|
18
|
+
|
|
16
19
|
const expandSites = (sites = []) => {
|
|
17
20
|
const result = [];
|
|
18
21
|
|
|
@@ -117,6 +120,7 @@ class Router {
|
|
|
117
120
|
services,
|
|
118
121
|
nodeInfo: pick(nodeInfo, ['name', 'version', 'port', 'mode', 'enableWelcomePage', 'routing']),
|
|
119
122
|
requestLimit,
|
|
123
|
+
cacheEnabled: isGatewayCacheEnabled(nodeInfo),
|
|
120
124
|
});
|
|
121
125
|
}
|
|
122
126
|
|
|
@@ -183,6 +187,10 @@ Router.formatSites = (sites = []) => {
|
|
|
183
187
|
result.forEach((site) => {
|
|
184
188
|
if (Array.isArray(site.rules) && site.rules.length > 0) {
|
|
185
189
|
const rules = cloneDeep(site.rules);
|
|
190
|
+
|
|
191
|
+
let hasRootPathBlockletRule = false;
|
|
192
|
+
let tmpBlockletRule;
|
|
193
|
+
|
|
186
194
|
rules.forEach((rule) => {
|
|
187
195
|
if ([ROUTING_RULE_TYPES.BLOCKLET].includes(rule.to.type) === false) {
|
|
188
196
|
return;
|
|
@@ -197,6 +205,11 @@ Router.formatSites = (sites = []) => {
|
|
|
197
205
|
}
|
|
198
206
|
|
|
199
207
|
if (daemonRule) {
|
|
208
|
+
if (rule.from.pathPrefix === '/') {
|
|
209
|
+
hasRootPathBlockletRule = true;
|
|
210
|
+
}
|
|
211
|
+
tmpBlockletRule = rule;
|
|
212
|
+
|
|
200
213
|
// Serve meta js: both prefix and suffix do not contain trailing slash
|
|
201
214
|
// NOTICE: 这里隐含了一个约定
|
|
202
215
|
// 如果安装的 blockletA 和 blockletB 都需要 __blocklet__.js
|
|
@@ -244,6 +257,24 @@ Router.formatSites = (sites = []) => {
|
|
|
244
257
|
});
|
|
245
258
|
}
|
|
246
259
|
});
|
|
260
|
+
|
|
261
|
+
// ensure /__blocklet__.js should be proxy to daemon
|
|
262
|
+
if (daemonRule && !hasRootPathBlockletRule && tmpBlockletRule) {
|
|
263
|
+
site.rules.push({
|
|
264
|
+
from: {
|
|
265
|
+
pathPrefix: '/',
|
|
266
|
+
groupPathPrefix: '/',
|
|
267
|
+
pathSuffix: '/__blocklet__.js',
|
|
268
|
+
},
|
|
269
|
+
to: {
|
|
270
|
+
type: ROUTING_RULE_TYPES.DAEMON,
|
|
271
|
+
port: daemonRule.to.port,
|
|
272
|
+
did: tmpBlockletRule.to.did,
|
|
273
|
+
componentId: tmpBlockletRule.to.did,
|
|
274
|
+
cacheGroup: site.mode === BLOCKLET_MODES.PRODUCTION ? 'blockletJs' : '',
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
}
|
|
247
278
|
}
|
|
248
279
|
});
|
|
249
280
|
|
package/lib/states/audit-log.js
CHANGED
|
@@ -239,9 +239,11 @@ const getLogContent = async (action, args, context, result, info, node) => {
|
|
|
239
239
|
case 'deleteRoutingRule':
|
|
240
240
|
return `deleted routing rule from ${site}`; // prettier-ignore
|
|
241
241
|
case 'updateGateway': {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
242
|
+
const changes = [
|
|
243
|
+
args.requestLimit.enabled ? `rate limit: enabled, rate: ${args.requestLimit.rate}` : 'rate limit: disabled',
|
|
244
|
+
args.cacheEnabled ? `global cache: enabled, rate: ${args.cacheEnabled}` : 'global cache: disabled',
|
|
245
|
+
];
|
|
246
|
+
const message = `update gateway: ${changes.join('; ')}`;
|
|
245
247
|
return message;
|
|
246
248
|
}
|
|
247
249
|
case 'createTransferInvitation':
|