@abtnode/core 1.8.68 → 1.8.69-beta-e0666d0d
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 +162 -65
- package/lib/blocklet/manager/helper/install-from-backup.js +7 -6
- package/lib/blocklet/storage/backup/audit-log.js +2 -2
- package/lib/blocklet/storage/backup/base.js +7 -8
- package/lib/blocklet/storage/backup/blocklet-extras.js +22 -39
- package/lib/blocklet/storage/backup/blocklet.js +34 -17
- package/lib/blocklet/storage/backup/blocklets.js +2 -2
- package/lib/blocklet/storage/backup/data.js +2 -2
- package/lib/blocklet/storage/backup/logs.js +2 -2
- package/lib/blocklet/storage/backup/routing-rule.js +2 -2
- package/lib/blocklet/storage/backup/spaces.js +105 -39
- package/lib/blocklet/storage/restore/base.js +26 -14
- package/lib/blocklet/storage/restore/blocklet-extras.js +34 -34
- package/lib/blocklet/storage/restore/blocklet.js +49 -0
- package/lib/blocklet/storage/restore/blocklets.js +6 -3
- package/lib/blocklet/storage/restore/logs.js +2 -2
- package/lib/blocklet/storage/restore/spaces.js +64 -50
- package/lib/event.js +28 -2
- package/lib/index.js +21 -3
- package/lib/router/helper.js +27 -6
- 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
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const { removeSync, outputJsonSync, readJSONSync } = require('fs-extra');
|
|
2
|
+
const { join } = require('path');
|
|
3
|
+
const security = require('@abtnode/util/lib/security');
|
|
4
|
+
const { BaseRestore } = require('./base');
|
|
5
|
+
|
|
6
|
+
class BlockletRestore extends BaseRestore {
|
|
7
|
+
filename = 'blocklet.json';
|
|
8
|
+
|
|
9
|
+
async import(params) {
|
|
10
|
+
const blocklet = this.decrypt(this.getBlocklet(), params);
|
|
11
|
+
removeSync(join(this.restoreDir, this.filename));
|
|
12
|
+
outputJsonSync(join(this.restoreDir, this.filename), blocklet);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
*
|
|
17
|
+
* @description
|
|
18
|
+
* @return {import('@abtnode/client').BlockletState}
|
|
19
|
+
* @memberof BlockletRestore
|
|
20
|
+
*/
|
|
21
|
+
getBlocklet() {
|
|
22
|
+
return readJSONSync(join(this.restoreDir, this.filename));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
*
|
|
27
|
+
* @description 解密加密的数据
|
|
28
|
+
* @param {import('@abtnode/client').BlockletState} blocklet
|
|
29
|
+
* @return {import('@abtnode/client').BlockletState}
|
|
30
|
+
* @memberof BlockletRestore
|
|
31
|
+
*/
|
|
32
|
+
decrypt(blocklet, params) {
|
|
33
|
+
const { password } = this.input;
|
|
34
|
+
if (Array.isArray(blocklet.migratedFrom)) {
|
|
35
|
+
blocklet.migratedFrom = blocklet.migratedFrom.map((x) => {
|
|
36
|
+
x.appSk = security.decrypt(x.appSk, params.salt, password);
|
|
37
|
+
return x;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return blocklet;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
getImportParams() {
|
|
45
|
+
return { salt: this.getBlocklet().appDid };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = { BlockletRestore };
|
|
@@ -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');
|
|
@@ -8,10 +8,13 @@ class BlockletsRestore extends BaseRestore {
|
|
|
8
8
|
filename = 'blocklets';
|
|
9
9
|
|
|
10
10
|
async import() {
|
|
11
|
-
const blockletsDir = join(this.
|
|
11
|
+
const blockletsDir = join(this.restoreDir, 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', {
|
|
@@ -7,13 +7,13 @@ class LogsRestore extends BaseRestore {
|
|
|
7
7
|
filename = 'logs.zip';
|
|
8
8
|
|
|
9
9
|
async import() {
|
|
10
|
-
const blockletZipPath = join(this.
|
|
10
|
+
const blockletZipPath = join(this.restoreDir, this.filename);
|
|
11
11
|
|
|
12
12
|
if (!existsSync(blockletZipPath)) {
|
|
13
13
|
throw new Error(`file not found: ${blockletZipPath}`);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
await zipToDir(blockletZipPath, join(this.
|
|
16
|
+
await zipToDir(blockletZipPath, join(this.restoreDir, 'logs'));
|
|
17
17
|
removeSync(blockletZipPath);
|
|
18
18
|
}
|
|
19
19
|
}
|
|
@@ -1,17 +1,27 @@
|
|
|
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 merge = require('lodash/merge');
|
|
16
|
+
const { BlockletEvents } = require('@blocklet/constant');
|
|
17
|
+
const { SpaceClient, RestoreBlockletCommand } = require('@did-space/client');
|
|
18
|
+
const { ensureDirSync, existsSync, rmdirSync } = require('fs-extra');
|
|
19
|
+
const { join, basename } = require('path');
|
|
20
|
+
|
|
21
|
+
const logger = require('@abtnode/logger')('@abtnode/core:storage:restore');
|
|
22
|
+
|
|
14
23
|
const { BlockletExtrasRestore } = require('./blocklet-extras');
|
|
24
|
+
const { BlockletRestore } = require('./blocklet');
|
|
15
25
|
const { BlockletsRestore } = require('./blocklets');
|
|
16
26
|
|
|
17
27
|
class SpacesRestore {
|
|
@@ -27,7 +37,7 @@ class SpacesRestore {
|
|
|
27
37
|
* @type {string}
|
|
28
38
|
* @memberof SpacesRestore
|
|
29
39
|
*/
|
|
30
|
-
|
|
40
|
+
restoreDir;
|
|
31
41
|
|
|
32
42
|
/**
|
|
33
43
|
*
|
|
@@ -35,15 +45,7 @@ class SpacesRestore {
|
|
|
35
45
|
* @type {string}
|
|
36
46
|
* @memberof SpacesRestore
|
|
37
47
|
*/
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
*
|
|
42
|
-
* @description spaces 的 endpoint
|
|
43
|
-
* @type {import('@ocap/wallet').WalletObject}
|
|
44
|
-
* @memberof SpacesRestore
|
|
45
|
-
*/
|
|
46
|
-
blockletWallet;
|
|
48
|
+
serverDir;
|
|
47
49
|
|
|
48
50
|
storages;
|
|
49
51
|
|
|
@@ -55,7 +57,11 @@ class SpacesRestore {
|
|
|
55
57
|
constructor(input) {
|
|
56
58
|
this.verify(input);
|
|
57
59
|
this.input = input;
|
|
58
|
-
this.storages = [
|
|
60
|
+
this.storages = [
|
|
61
|
+
new BlockletExtrasRestore(this.input),
|
|
62
|
+
new BlockletRestore(this.input),
|
|
63
|
+
new BlockletsRestore(this.input),
|
|
64
|
+
];
|
|
59
65
|
}
|
|
60
66
|
|
|
61
67
|
/**
|
|
@@ -68,72 +74,80 @@ class SpacesRestore {
|
|
|
68
74
|
if (!validUrl.isWebUri(input.endpoint)) {
|
|
69
75
|
throw new Error(`endpoint(${input.endpoint}) must be a WebUri`);
|
|
70
76
|
}
|
|
77
|
+
if (!input.endpoint.includes(input.appDid)) {
|
|
78
|
+
throw new Error(`endpoint and blocklet.appDid(${input.appDid}) do not match`);
|
|
79
|
+
}
|
|
71
80
|
}
|
|
72
81
|
|
|
73
82
|
async initialize() {
|
|
74
|
-
this.
|
|
75
|
-
this.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
throw new Error(`endpoint and blocklet.appDid(${this.blockletWallet.address}) do not match`);
|
|
83
|
+
this.serverDir = process.env.ABT_NODE_DATA_DIR;
|
|
84
|
+
this.restoreDir = join(this.serverDir, 'tmp/restore', this.input.appDid);
|
|
85
|
+
if (existsSync(this.restoreDir)) {
|
|
86
|
+
rmdirSync(this.restoreDir, { recursive: true });
|
|
79
87
|
}
|
|
88
|
+
ensureDirSync(this.restoreDir);
|
|
80
89
|
|
|
81
|
-
this.
|
|
82
|
-
if (existsSync(this.blockletRestoreDir)) {
|
|
83
|
-
rmdirSync(this.blockletRestoreDir, { recursive: true });
|
|
84
|
-
}
|
|
85
|
-
ensureDirSync(this.blockletRestoreDir);
|
|
90
|
+
this.storages.map((x) => x.ensureParams(this));
|
|
86
91
|
}
|
|
87
92
|
|
|
88
|
-
/**
|
|
89
|
-
*
|
|
90
|
-
* @returns {Promise<void>}
|
|
91
|
-
* @memberof SpacesRestore
|
|
92
|
-
*/
|
|
93
93
|
async restore() {
|
|
94
94
|
await this.initialize();
|
|
95
95
|
await this.syncFromSpaces();
|
|
96
|
-
await this.import();
|
|
97
|
-
}
|
|
98
96
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const wallet = fromSecretKey(this.input.blockletSecretKey, WalletType({ role: types.RoleType.ROLE_APPLICATION }));
|
|
97
|
+
const params = await Promise.all(this.storages.map((x) => x.getImportParams()));
|
|
98
|
+
await this.import(merge(...params));
|
|
102
99
|
|
|
103
|
-
return
|
|
100
|
+
return this.storages.map((x) => x.getInstallParams());
|
|
104
101
|
}
|
|
105
102
|
|
|
106
103
|
async syncFromSpaces() {
|
|
107
|
-
const { endpoint } = this.input;
|
|
108
|
-
const wallet = await this.getBlockletWallet();
|
|
104
|
+
const { endpoint, wallet, delegation } = this.input;
|
|
109
105
|
|
|
110
106
|
const spaceClient = new SpaceClient({
|
|
111
107
|
endpoint,
|
|
108
|
+
delegation,
|
|
112
109
|
wallet,
|
|
113
110
|
});
|
|
114
111
|
|
|
115
|
-
const { errorCount } = await spaceClient.send(
|
|
116
|
-
new
|
|
117
|
-
|
|
118
|
-
target: this.
|
|
112
|
+
const { errorCount, message } = await spaceClient.send(
|
|
113
|
+
new RestoreBlockletCommand({
|
|
114
|
+
appDid: this.input.appDid,
|
|
115
|
+
target: join(this.restoreDir, '/'),
|
|
119
116
|
debug: true,
|
|
120
|
-
concurrency:
|
|
121
|
-
retryCount:
|
|
117
|
+
concurrency: 32,
|
|
118
|
+
retryCount: 10,
|
|
119
|
+
onProgress: (data) => {
|
|
120
|
+
logger.info('restore progress', { appDid: this.input.appDid, data });
|
|
121
|
+
this.input.event.emit(BlockletEvents.restoreProgress, {
|
|
122
|
+
appDid: this.input.appDid,
|
|
123
|
+
message: `Downloaded file ${basename(data.key)} (${data.completed}/${data.total})`,
|
|
124
|
+
});
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
userDid: this.input.userDid,
|
|
128
|
+
referrer: this.input.referrer,
|
|
122
129
|
})
|
|
123
130
|
);
|
|
124
131
|
|
|
125
132
|
if (errorCount !== 0) {
|
|
126
|
-
throw new Error(`Sync from spaces encountered ${
|
|
133
|
+
throw new Error(`Sync from spaces encountered error: ${message}`);
|
|
127
134
|
}
|
|
128
135
|
}
|
|
129
136
|
|
|
130
|
-
async import() {
|
|
137
|
+
async import(params) {
|
|
138
|
+
this.input.event.emit(BlockletEvents.restoreProgress, {
|
|
139
|
+
appDid: this.input.appDid,
|
|
140
|
+
message: 'Preparing to import data...',
|
|
141
|
+
});
|
|
131
142
|
await Promise.all(
|
|
132
143
|
this.storages.map((storage) => {
|
|
133
|
-
storage.
|
|
134
|
-
return storage.import();
|
|
144
|
+
return storage.import(params);
|
|
135
145
|
})
|
|
136
146
|
);
|
|
147
|
+
this.input.event.emit(BlockletEvents.restoreProgress, {
|
|
148
|
+
appDid: this.input.appDid,
|
|
149
|
+
message: 'Importing data successfully...',
|
|
150
|
+
});
|
|
137
151
|
}
|
|
138
152
|
}
|
|
139
153
|
|
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({
|
|
@@ -236,6 +242,14 @@ module.exports = ({
|
|
|
236
242
|
);
|
|
237
243
|
|
|
238
244
|
logger.info('take snapshot after updated blocklet app', { event: eventName, did: blocklet.meta.did, hash });
|
|
245
|
+
} else if (BlockletEvents.spaceConnected === eventName) {
|
|
246
|
+
nodeState
|
|
247
|
+
.read()
|
|
248
|
+
.then((info) => handleRouting(info))
|
|
249
|
+
.catch((err) => {
|
|
250
|
+
logger.error('Reload gateway failed on blocklet.connectedSpace', { error: err });
|
|
251
|
+
});
|
|
252
|
+
logger.info('Reload gateway after blocklet connected to space', { event: eventName, did: blocklet.appDid });
|
|
239
253
|
}
|
|
240
254
|
|
|
241
255
|
if (
|
|
@@ -273,6 +287,13 @@ module.exports = ({
|
|
|
273
287
|
}
|
|
274
288
|
};
|
|
275
289
|
|
|
290
|
+
/**
|
|
291
|
+
*
|
|
292
|
+
*
|
|
293
|
+
* @param {*} subject
|
|
294
|
+
* @param {string} event
|
|
295
|
+
* @param {(event: string, data: any) => Promise<void> | void} handler
|
|
296
|
+
*/
|
|
276
297
|
const listen = (subject, event, handler) => subject.on(event, (data) => handler(event, data));
|
|
277
298
|
|
|
278
299
|
[
|
|
@@ -291,6 +312,11 @@ module.exports = ({
|
|
|
291
312
|
BlockletEvents.startFailed,
|
|
292
313
|
BlockletEvents.stopped,
|
|
293
314
|
BlockletEvents.appDidChanged,
|
|
315
|
+
|
|
316
|
+
BlockletEvents.backupProgress,
|
|
317
|
+
BlockletEvents.restoreProgress,
|
|
318
|
+
|
|
319
|
+
BlockletEvents.spaceConnected,
|
|
294
320
|
].forEach((eventName) => {
|
|
295
321
|
listen(blockletManager, eventName, handleBlockletEvent);
|
|
296
322
|
});
|
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
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const fs = require('fs-extra');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const tar = require('tar');
|
|
6
|
+
const isUrl = require('is-url');
|
|
6
7
|
const get = require('lodash/get');
|
|
7
8
|
const cloneDeep = require('lodash/cloneDeep');
|
|
8
9
|
const isEqual = require('lodash/isEqual');
|
|
@@ -26,7 +27,6 @@ const {
|
|
|
26
27
|
NAME_FOR_WELLKNOWN_SITE,
|
|
27
28
|
DEFAULT_HTTP_PORT,
|
|
28
29
|
DEFAULT_HTTPS_PORT,
|
|
29
|
-
NODE_MODES,
|
|
30
30
|
ROUTING_RULE_TYPES,
|
|
31
31
|
CERTIFICATE_EXPIRES_OFFSET,
|
|
32
32
|
DEFAULT_SERVICE_PATH,
|
|
@@ -44,6 +44,7 @@ const {
|
|
|
44
44
|
BLOCKLET_INTERFACE_PUBLIC,
|
|
45
45
|
BLOCKLET_INTERFACE_WELLKNOWN,
|
|
46
46
|
BLOCKLET_INTERFACE_TYPE_WELLKNOWN,
|
|
47
|
+
BLOCKLET_CONFIGURABLE_KEY,
|
|
47
48
|
BlockletEvents,
|
|
48
49
|
BLOCKLET_MODES,
|
|
49
50
|
} = require('@blocklet/constant');
|
|
@@ -56,6 +57,7 @@ const {
|
|
|
56
57
|
findInterfacePortByName,
|
|
57
58
|
getWellknownSitePort,
|
|
58
59
|
getServerDidDomain,
|
|
60
|
+
isGatewayCacheEnabled,
|
|
59
61
|
} = require('../util');
|
|
60
62
|
const { getIpDnsDomainForBlocklet, getDidDomainForBlocklet } = require('../util/get-domain-for-blocklet');
|
|
61
63
|
const { getFromCache: getAccessibleExternalNodeIp } = require('../util/get-accessible-external-node-ip');
|
|
@@ -122,7 +124,7 @@ const addCorsToSite = (site, rawUrl) => {
|
|
|
122
124
|
site.corsAllowedOrigins.push(url.hostname);
|
|
123
125
|
}
|
|
124
126
|
} catch (err) {
|
|
125
|
-
|
|
127
|
+
console.error('addCorsToSite', err);
|
|
126
128
|
}
|
|
127
129
|
};
|
|
128
130
|
|
|
@@ -336,6 +338,22 @@ const ensureCorsForWebWallet = async (sites) => {
|
|
|
336
338
|
return sites;
|
|
337
339
|
};
|
|
338
340
|
|
|
341
|
+
const ensureCorsForDidSpace = async (sites = [], blocklets) => {
|
|
342
|
+
return sites.map((site) => {
|
|
343
|
+
const blocklet = blocklets.find((x) => x.meta.did === site.blockletDid);
|
|
344
|
+
if (blocklet) {
|
|
345
|
+
const endpoint = blocklet.environments.find(
|
|
346
|
+
(x) => x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SPACE_ENDPOINT
|
|
347
|
+
);
|
|
348
|
+
if (endpoint && isUrl(endpoint.value)) {
|
|
349
|
+
addCorsToSite(site, endpoint.value);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return site;
|
|
354
|
+
});
|
|
355
|
+
};
|
|
356
|
+
|
|
339
357
|
const filterSitesForRemovedBlocklets = async (sites = [], blocklets) => {
|
|
340
358
|
return sites.filter((site) => {
|
|
341
359
|
if (!site.domain.endsWith(BLOCKLET_SITE_GROUP_SUFFIX)) {
|
|
@@ -382,7 +400,7 @@ const ensureBlockletCache = async (sites = [], blocklets) => {
|
|
|
382
400
|
const clone = cloneDeep(rule);
|
|
383
401
|
clone.from.pathPrefix = joinUrl(rule.from.pathPrefix, cachePrefix);
|
|
384
402
|
clone.to.cacheGroup = 'blockletProxy';
|
|
385
|
-
clone.to.
|
|
403
|
+
clone.to.targetPrefix = cachePrefix;
|
|
386
404
|
clone.dynamic = true; // mark as dynamic to avoid redundant generated rules
|
|
387
405
|
cacheRules.push(clone);
|
|
388
406
|
});
|
|
@@ -407,6 +425,7 @@ const ensureLatestInfo = async (sites = [], { withDefaultCors = true } = {}) =>
|
|
|
407
425
|
result = await ensureBlockletCache(result, blocklets);
|
|
408
426
|
result = await ensureWellknownRule(result);
|
|
409
427
|
result = await ensureCorsForWebWallet(result);
|
|
428
|
+
result = await ensureCorsForDidSpace(result, blocklets);
|
|
410
429
|
result = await ensureLatestInterfaceInfo(result);
|
|
411
430
|
|
|
412
431
|
return result;
|
|
@@ -518,7 +537,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
518
537
|
|
|
519
538
|
const ensureDomainCert = async (domain, url) => {
|
|
520
539
|
const cert = await certManager.getByDomain(domain);
|
|
521
|
-
if (!cert) {
|
|
540
|
+
if (!cert || get(cert, 'meta.validTo') <= Date.now()) {
|
|
522
541
|
await downloadCert({
|
|
523
542
|
domain,
|
|
524
543
|
url,
|
|
@@ -1115,7 +1134,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
1115
1134
|
configDir: path.join(dataDirs.router, providerName),
|
|
1116
1135
|
httpPort: nodeInfo.routing.httpPort || DEFAULT_HTTP_PORT,
|
|
1117
1136
|
httpsPort: nodeInfo.routing.httpsPort || DEFAULT_HTTPS_PORT,
|
|
1118
|
-
|
|
1137
|
+
cacheEnabled: isGatewayCacheEnabled(nodeInfo),
|
|
1119
1138
|
}),
|
|
1120
1139
|
getRoutingParams: async () => {
|
|
1121
1140
|
try {
|
|
@@ -1220,7 +1239,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
1220
1239
|
configDir: path.join(dataDirs.router, info.routing.provider),
|
|
1221
1240
|
httpPort: info.routing.httpPort || DEFAULT_HTTP_PORT,
|
|
1222
1241
|
httpsPort: info.routing.httpsPort || DEFAULT_HTTPS_PORT,
|
|
1223
|
-
|
|
1242
|
+
cacheEnabled: isGatewayCacheEnabled(info),
|
|
1224
1243
|
});
|
|
1225
1244
|
await providerInstance.stop();
|
|
1226
1245
|
logger.info('original router stopped:', { provider: info.routing.provider });
|
|
@@ -1408,6 +1427,8 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
1408
1427
|
deleteRoutingRule,
|
|
1409
1428
|
addDomainAlias,
|
|
1410
1429
|
deleteDomainAlias,
|
|
1430
|
+
|
|
1431
|
+
isGatewayCacheEnabled,
|
|
1411
1432
|
};
|
|
1412
1433
|
};
|
|
1413
1434
|
|
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':
|
package/lib/states/blocklet.js
CHANGED
|
@@ -28,6 +28,7 @@ const lock = new Lock('blocklet-port-assign-lock');
|
|
|
28
28
|
|
|
29
29
|
const isHex = (str) => /^0x[0-9a-f]+$/i.test(str);
|
|
30
30
|
const getMaxPort = (ports = {}) => Math.max(Object.values(ports).map(Number));
|
|
31
|
+
const getConditions = (did) => [{ 'meta.did': did }, { appDid: did }, { appPid: did }];
|
|
31
32
|
|
|
32
33
|
const getExternalPortsFromMeta = (meta) =>
|
|
33
34
|
(meta.interfaces || []).map((x) => x.port && x.port.external).filter(Boolean);
|
|
@@ -48,7 +49,16 @@ const formatBlocklet = (blocklet, phase, dek) => {
|
|
|
48
49
|
return;
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
[
|
|
52
|
+
(Array.isArray(b.migratedFrom) ? b.migratedFrom : []).forEach((x) => {
|
|
53
|
+
if (phase === 'onUpdate' && isHex(x.appSk) === true) {
|
|
54
|
+
x.appSk = security.encrypt(x.appSk, b.meta.did, dek);
|
|
55
|
+
}
|
|
56
|
+
if (phase === 'onRead' && isHex(x.appSk) === false) {
|
|
57
|
+
x.appSk = security.decrypt(x.appSk, b.meta.did, dek);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
['BLOCKLET_APP_SK', 'BLOCKLET_APP_PSK'].forEach((key) => {
|
|
52
62
|
const env = b.environments.find((x) => x.key === key);
|
|
53
63
|
if (!env) {
|
|
54
64
|
return;
|
|
@@ -98,7 +108,7 @@ class BlockletState extends BaseState {
|
|
|
98
108
|
resolve(null);
|
|
99
109
|
}
|
|
100
110
|
|
|
101
|
-
this.findOne({ $or:
|
|
111
|
+
this.findOne({ $or: getConditions(did) }, (err, doc) => {
|
|
102
112
|
if (err) {
|
|
103
113
|
return reject(err);
|
|
104
114
|
}
|
|
@@ -114,7 +124,7 @@ class BlockletState extends BaseState {
|
|
|
114
124
|
resolve(null);
|
|
115
125
|
}
|
|
116
126
|
|
|
117
|
-
this.findOne({ $or:
|
|
127
|
+
this.findOne({ $or: getConditions(did) }, (err, doc) => {
|
|
118
128
|
if (err) {
|
|
119
129
|
return reject(err);
|
|
120
130
|
}
|
|
@@ -130,7 +140,7 @@ class BlockletState extends BaseState {
|
|
|
130
140
|
resolve(null);
|
|
131
141
|
}
|
|
132
142
|
|
|
133
|
-
this.findOne({ $or:
|
|
143
|
+
this.findOne({ $or: getConditions(did) }, { status: 1 }, (err, doc) => {
|
|
134
144
|
if (err) {
|
|
135
145
|
return reject(err);
|
|
136
146
|
}
|
|
@@ -146,7 +156,7 @@ class BlockletState extends BaseState {
|
|
|
146
156
|
resolve(false);
|
|
147
157
|
}
|
|
148
158
|
|
|
149
|
-
this.count({ $or:
|
|
159
|
+
this.count({ $or: getConditions(did) }, (err, count) => {
|
|
150
160
|
if (err) {
|
|
151
161
|
return reject(err);
|
|
152
162
|
}
|
|
@@ -199,6 +209,9 @@ class BlockletState extends BaseState {
|
|
|
199
209
|
deployedFrom = '',
|
|
200
210
|
mode = BLOCKLET_MODES.PRODUCTION,
|
|
201
211
|
children: rawChildren = [],
|
|
212
|
+
appPid = null, // the permanent appDid, which will not change after initial set
|
|
213
|
+
migratedFrom = [], // the complete migrate history
|
|
214
|
+
externalSk = false, // whether sk is managed by some party beside server, such as did-wallet
|
|
202
215
|
} = {}) {
|
|
203
216
|
return this.getBlocklet(meta.did).then(
|
|
204
217
|
(exist) =>
|
|
@@ -229,6 +242,7 @@ class BlockletState extends BaseState {
|
|
|
229
242
|
|
|
230
243
|
const data = {
|
|
231
244
|
appDid: null, // will updated later when updating blocklet environments
|
|
245
|
+
appPid,
|
|
232
246
|
mode,
|
|
233
247
|
meta: sanitized,
|
|
234
248
|
status,
|
|
@@ -237,6 +251,8 @@ class BlockletState extends BaseState {
|
|
|
237
251
|
ports,
|
|
238
252
|
environments: [],
|
|
239
253
|
children,
|
|
254
|
+
migratedFrom,
|
|
255
|
+
externalSk,
|
|
240
256
|
};
|
|
241
257
|
|
|
242
258
|
// add to db
|