@abtnode/core 1.15.17 → 1.16.0-beta-8ee536d7
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 +67 -69
- package/lib/api/team.js +386 -55
- package/lib/blocklet/downloader/blocklet-downloader.js +226 -0
- package/lib/blocklet/downloader/bundle-downloader.js +272 -0
- package/lib/blocklet/downloader/constants.js +3 -0
- package/lib/blocklet/downloader/resolve-download.js +199 -0
- package/lib/blocklet/extras.js +83 -26
- package/lib/blocklet/hooks.js +18 -65
- package/lib/blocklet/manager/base.js +10 -16
- package/lib/blocklet/manager/disk.js +1680 -1566
- package/lib/blocklet/manager/helper/install-application-from-backup.js +177 -0
- package/lib/blocklet/manager/helper/install-application-from-dev.js +94 -0
- package/lib/blocklet/manager/helper/install-application-from-general.js +188 -0
- package/lib/blocklet/manager/helper/install-component-from-dev.js +84 -0
- package/lib/blocklet/manager/helper/install-component-from-upload.js +181 -0
- package/lib/blocklet/manager/helper/install-component-from-url.js +173 -0
- package/lib/blocklet/manager/helper/migrate-application-to-struct-v2.js +450 -0
- package/lib/blocklet/manager/helper/rollback-cache.js +41 -0
- package/lib/blocklet/manager/helper/upgrade-components.js +152 -0
- package/lib/blocklet/migration.js +30 -52
- package/lib/blocklet/storage/backup/audit-log.js +27 -0
- package/lib/blocklet/storage/backup/base.js +62 -0
- package/lib/blocklet/storage/backup/blocklet-extras.js +92 -0
- package/lib/blocklet/storage/backup/blocklet.js +70 -0
- package/lib/blocklet/storage/backup/blocklets.js +74 -0
- package/lib/blocklet/storage/backup/data.js +19 -0
- package/lib/blocklet/storage/backup/logs.js +24 -0
- package/lib/blocklet/storage/backup/routing-rule.js +19 -0
- package/lib/blocklet/storage/backup/spaces.js +240 -0
- package/lib/blocklet/storage/restore/base.js +67 -0
- package/lib/blocklet/storage/restore/blocklet-extras.js +86 -0
- package/lib/blocklet/storage/restore/blocklet.js +56 -0
- package/lib/blocklet/storage/restore/blocklets.js +43 -0
- package/lib/blocklet/storage/restore/logs.js +21 -0
- package/lib/blocklet/storage/restore/spaces.js +156 -0
- package/lib/blocklet/storage/utils/hash.js +51 -0
- package/lib/blocklet/storage/utils/zip.js +43 -0
- package/lib/cert.js +206 -0
- package/lib/event.js +237 -64
- package/lib/index.js +191 -83
- package/lib/migrations/1.0.21-update-config.js +1 -1
- package/lib/migrations/1.0.22-max-memory.js +1 -1
- package/lib/migrations/1.0.25.js +1 -1
- package/lib/migrations/1.0.32-update-config.js +1 -1
- package/lib/migrations/1.0.33-blocklets.js +1 -1
- package/lib/migrations/1.5.20-registry.js +15 -0
- package/lib/migrations/1.6.17-blocklet-children.js +48 -0
- package/lib/migrations/1.6.21-rename-ip-echo-domain.js +35 -0
- package/lib/migrations/1.6.4-security.js +59 -0
- package/lib/migrations/1.6.5-security.js +60 -0
- package/lib/migrations/1.6.9-update-node-info-and-certificate.js +38 -0
- package/lib/migrations/1.7.1-blocklet-setup.js +18 -0
- package/lib/migrations/1.7.12-blocklet-meta.js +51 -0
- package/lib/migrations/1.7.15-blocklet-bundle-source.js +42 -0
- package/lib/migrations/1.7.20-blocklet-component.js +41 -0
- package/lib/migrations/1.8.33-blocklet-mem-limit.js +20 -0
- package/lib/migrations/README.md +1 -1
- package/lib/migrations/index.js +6 -2
- package/lib/monitor/blocklet-runtime-monitor.js +200 -0
- package/lib/monitor/get-history-list.js +37 -0
- package/lib/monitor/node-runtime-monitor.js +228 -0
- package/lib/router/helper.js +576 -500
- package/lib/router/index.js +85 -21
- package/lib/router/manager.js +146 -187
- package/lib/states/README.md +36 -1
- package/lib/states/access-key.js +39 -17
- package/lib/states/audit-log.js +462 -0
- package/lib/states/base.js +4 -213
- package/lib/states/blocklet-extras.js +195 -138
- package/lib/states/blocklet.js +371 -110
- package/lib/states/cache.js +8 -6
- package/lib/states/challenge.js +5 -5
- package/lib/states/index.js +19 -36
- package/lib/states/migration.js +4 -4
- package/lib/states/node.js +135 -46
- package/lib/states/notification.js +22 -35
- package/lib/states/session.js +17 -9
- package/lib/states/site.js +50 -25
- package/lib/states/user.js +74 -20
- package/lib/states/webhook.js +10 -6
- package/lib/team/manager.js +124 -7
- package/lib/util/blocklet.js +1223 -246
- package/lib/util/chain.js +1 -1
- package/lib/util/default-node-config.js +5 -23
- package/lib/util/disk-monitor.js +13 -10
- package/lib/util/domain-status.js +84 -15
- package/lib/util/get-accessible-external-node-ip.js +2 -2
- package/lib/util/get-domain-for-blocklet.js +13 -0
- package/lib/util/get-meta-from-url.js +33 -0
- package/lib/util/index.js +207 -272
- package/lib/util/ip.js +6 -0
- package/lib/util/maintain.js +233 -0
- package/lib/util/public-to-store.js +85 -0
- package/lib/util/ready.js +1 -1
- package/lib/util/requirement.js +28 -9
- package/lib/util/reset-node.js +22 -7
- package/lib/util/router.js +13 -0
- package/lib/util/rpc.js +16 -0
- package/lib/util/store.js +179 -0
- package/lib/util/sysinfo.js +44 -0
- package/lib/util/ua.js +54 -0
- package/lib/validators/blocklet-extra.js +24 -0
- package/lib/validators/node.js +25 -12
- package/lib/validators/permission.js +16 -1
- package/lib/validators/role.js +17 -3
- package/lib/validators/router.js +40 -20
- package/lib/validators/trusted-passport.js +1 -0
- package/lib/validators/util.js +22 -5
- package/lib/webhook/index.js +45 -35
- package/lib/webhook/sender/index.js +5 -0
- package/lib/webhook/sender/slack/index.js +1 -1
- package/lib/webhook/sender/wallet/index.js +48 -0
- package/package.json +54 -36
- package/lib/blocklet/registry.js +0 -205
- package/lib/states/https-cert.js +0 -67
- package/lib/util/get-ip-dns-domain-for-blocklet.js +0 -19
- package/lib/util/service.js +0 -66
- package/lib/util/upgrade.js +0 -178
- /package/lib/{queue.js → util/queue.js} +0 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const { removeSync, existsSync, move, createReadStream } = require('fs-extra');
|
|
2
|
+
const hasha = require('hasha');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
*
|
|
7
|
+
* @param {NodeJS.ReadableStream} stream1
|
|
8
|
+
* @param {NodeJS.ReadableStream} stream2
|
|
9
|
+
* @return {Promise<boolean>}
|
|
10
|
+
*/
|
|
11
|
+
async function compareHash(stream1, stream2) {
|
|
12
|
+
const hash1 = await hasha.fromStream(stream1, {
|
|
13
|
+
algorithm: 'md5',
|
|
14
|
+
});
|
|
15
|
+
const hash2 = await hasha.fromStream(stream2, {
|
|
16
|
+
algorithm: 'md5',
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
return hash1 === hash2;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
*
|
|
24
|
+
* @description 比对新文件和旧文件的hash,旧文件不存在或者hash不同时,使用新文件替换,hash相同,删除新文件
|
|
25
|
+
* @param {string} oldFilePath
|
|
26
|
+
* @param {string} newFilePath
|
|
27
|
+
* @returns {Promise<boolean>}
|
|
28
|
+
*/
|
|
29
|
+
async function compareAndMove(oldFilePath, newFilePath) {
|
|
30
|
+
if (!existsSync(oldFilePath)) {
|
|
31
|
+
await move(newFilePath, oldFilePath, { overwrite: true });
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!existsSync(newFilePath)) {
|
|
36
|
+
throw new Error(`newFilePath(${newFilePath}) not found`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const isSame = await compareHash(createReadStream(oldFilePath), createReadStream(newFilePath));
|
|
40
|
+
if (isSame) {
|
|
41
|
+
removeSync(newFilePath);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
await move(newFilePath, oldFilePath, { overwrite: true });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = {
|
|
49
|
+
compareHash,
|
|
50
|
+
compareAndMove,
|
|
51
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const archiver = require('archiver');
|
|
2
|
+
const { ensureDirSync, existsSync, removeSync, createWriteStream } = require('fs-extra');
|
|
3
|
+
const { dirname } = require('path');
|
|
4
|
+
const StreamZip = require('node-stream-zip');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
*
|
|
9
|
+
* @param {string} source ~/abc/ 通常是一个文件夹
|
|
10
|
+
* @param {string} target abc.zip 压缩包的名称
|
|
11
|
+
* @return {*}
|
|
12
|
+
*/
|
|
13
|
+
function dirToZip(source, target) {
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
ensureDirSync(dirname(target));
|
|
16
|
+
|
|
17
|
+
if (existsSync(target)) {
|
|
18
|
+
removeSync(target);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const output = createWriteStream(target);
|
|
22
|
+
const archive = archiver('zip', { level: 9 });
|
|
23
|
+
archive.on('error', (err) => reject(err));
|
|
24
|
+
output.on('close', () => resolve());
|
|
25
|
+
|
|
26
|
+
archive.directory(source, false);
|
|
27
|
+
|
|
28
|
+
archive.pipe(output);
|
|
29
|
+
archive.finalize();
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function zipToDir(source, target) {
|
|
34
|
+
// eslint-disable-next-line new-cap
|
|
35
|
+
const zip = new StreamZip.async({ file: source });
|
|
36
|
+
await zip.extract(null, target);
|
|
37
|
+
await zip.close();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = {
|
|
41
|
+
dirToZip,
|
|
42
|
+
zipToDir,
|
|
43
|
+
};
|
package/lib/cert.js
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
const { EventEmitter } = require('events');
|
|
2
|
+
const CertificateManager = require('@abtnode/certificate-manager/sdk/manager');
|
|
3
|
+
const logger = require('@abtnode/logger')('@abtnode/core:cert');
|
|
4
|
+
const { EVENTS } = require('@abtnode/constant');
|
|
5
|
+
const { BlockletEvents } = require('@blocklet/constant');
|
|
6
|
+
|
|
7
|
+
const onCertExpired = async (cert, states) => {
|
|
8
|
+
logger.info('send certificate expire notification', { domain: cert.domain });
|
|
9
|
+
states.notification.create({
|
|
10
|
+
title: 'SSL Certificate Expired',
|
|
11
|
+
description: `Your SSL certificate for domain ${cert.domain} has expired, please update it in Blocklet Server`,
|
|
12
|
+
severity: 'error',
|
|
13
|
+
entityType: 'certificate',
|
|
14
|
+
entityId: cert.id,
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const onCertAboutExpire = (cert, states) => {
|
|
19
|
+
logger.info('send certificate about-expire notification', { domain: cert.domain });
|
|
20
|
+
states.notification.create({
|
|
21
|
+
title: 'SSL Certificate Expire Warning',
|
|
22
|
+
description: `Your SSL certificate for domain ${cert.domain} will expire in ${
|
|
23
|
+
cert.expireInDays
|
|
24
|
+
} days (on ${new Date(cert.validTo).toLocaleString()}), please remember to update it in Blocklet Server`,
|
|
25
|
+
severity: 'warning',
|
|
26
|
+
entityType: 'certificate',
|
|
27
|
+
entityId: cert.id, // eslint-disable-line no-underscore-dangle
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const onCertIssued = (cert, states) => {
|
|
32
|
+
states.notification.create({
|
|
33
|
+
title: 'Certificate Issued',
|
|
34
|
+
description: `The ${cert.domain} certificate is issued successfully`,
|
|
35
|
+
severity: 'success',
|
|
36
|
+
entityType: 'certificate',
|
|
37
|
+
entityId: cert.id,
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const onCertIssueFailed = (cert, states) => {
|
|
42
|
+
states.notification.create({
|
|
43
|
+
title: 'Certificate Issue Failed',
|
|
44
|
+
description: `Failed to issue certificate for ${cert.domain}`,
|
|
45
|
+
severity: 'error',
|
|
46
|
+
entityType: 'certificate',
|
|
47
|
+
entityId: cert.id,
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const getDomainFromInput = (input) => {
|
|
52
|
+
if (Object.prototype.toString.call(input) === '[object Object]') {
|
|
53
|
+
return input.domain;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return input;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
class Cert extends EventEmitter {
|
|
60
|
+
constructor({ maintainerEmail, dataDir, states }) {
|
|
61
|
+
super();
|
|
62
|
+
|
|
63
|
+
this.manager = new CertificateManager({ maintainerEmail, dataDir });
|
|
64
|
+
|
|
65
|
+
this.manager.on('cert.issued', this._onCertIssued.bind(this));
|
|
66
|
+
this.manager.on('cert.expired', this._onCertExpired.bind(this));
|
|
67
|
+
this.manager.on('cert.about_to_expire', this._onCertAboutToExpire.bind(this));
|
|
68
|
+
this.manager.on('cert.error', this._onCertError.bind(this));
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Array<{domain: string, did: string}>
|
|
72
|
+
*/
|
|
73
|
+
this._blockletDomains = [];
|
|
74
|
+
this.states = states;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
start() {
|
|
78
|
+
return this.manager.start();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
static fixCertificate(entity) {
|
|
82
|
+
// Hack: this logic exists because gql does not allow line breaks in arg values
|
|
83
|
+
entity.privateKey = entity.privateKey.split('|').join('\n');
|
|
84
|
+
entity.certificate = entity.certificate.split('|').join('\n');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
getAll() {
|
|
88
|
+
return this.manager.getAll();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
getAllNormal() {
|
|
92
|
+
return this.manager.getAllNormal();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
getNormalByDomain(domain) {
|
|
96
|
+
return this.manager.getNormalByDomain(domain);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
getByDomain(inputDomain) {
|
|
100
|
+
const domain = getDomainFromInput(inputDomain);
|
|
101
|
+
return this.manager.getByDomain(domain);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async add(data) {
|
|
105
|
+
if (!data.certificate || !data.privateKey) {
|
|
106
|
+
throw new Error('certificate and privateKey are required');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
Cert.fixCertificate(data);
|
|
110
|
+
|
|
111
|
+
const result = await this.manager.add(data);
|
|
112
|
+
logger.info('add certificate result', { name: result.name });
|
|
113
|
+
this.emit(EVENTS.CERT_ADDED, result);
|
|
114
|
+
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @param {{
|
|
120
|
+
* did?:string // blocklet.meta.did
|
|
121
|
+
* }}
|
|
122
|
+
*/
|
|
123
|
+
async issue({ domain, did }) {
|
|
124
|
+
logger.info(`generate certificate for ${domain}`);
|
|
125
|
+
|
|
126
|
+
if (did) {
|
|
127
|
+
this._bindBlocklet({ domain, did });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return this.manager.issue(domain);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async upsertByDomain(data) {
|
|
134
|
+
const result = await this.manager.upsertByDomain(data);
|
|
135
|
+
this.emit(EVENTS.CERT_UPDATED, result);
|
|
136
|
+
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async update(data) {
|
|
141
|
+
return this.manager.update(data.id, { name: data.name, public: data.public });
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async remove({ id }) {
|
|
145
|
+
await this.manager.remove(id);
|
|
146
|
+
logger.info('delete certificate', { id });
|
|
147
|
+
this.emit(EVENTS.CERT_REMOVED);
|
|
148
|
+
|
|
149
|
+
return {};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async addWithoutValidations(data) {
|
|
153
|
+
return this.manager.addWithoutValidations(data);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async updateWithoutValidations(id, data) {
|
|
157
|
+
return this.manager.updateWithoutValidations(id, data);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
_bindBlocklet({ domain, did }) {
|
|
161
|
+
// only save 100 domains in memory
|
|
162
|
+
const list = this._blockletDomains.slice(-100).filter((x) => x.domain !== domain);
|
|
163
|
+
list.push({ domain, did });
|
|
164
|
+
this._blockletDomains = list;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* @param {{
|
|
169
|
+
* blocklet: string;
|
|
170
|
+
* server: string;
|
|
171
|
+
* }} event
|
|
172
|
+
* @param {{
|
|
173
|
+
* domain: string
|
|
174
|
+
* }} cert
|
|
175
|
+
*/
|
|
176
|
+
_emitEvent(event, cert) {
|
|
177
|
+
const blockletDomain = this._blockletDomains.find((x) => x.domain === cert.domain);
|
|
178
|
+
if (blockletDomain) {
|
|
179
|
+
this.emit(event.blocklet, { ...cert, meta: { did: blockletDomain.did } });
|
|
180
|
+
} else {
|
|
181
|
+
this.emit(event.server, cert);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
_onCertIssued(cert) {
|
|
186
|
+
this._emitEvent({ blocklet: BlockletEvents.certIssued, server: EVENTS.CERT_ISSUED }, cert);
|
|
187
|
+
|
|
188
|
+
onCertIssued(cert, this.states);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
_onCertError(cert) {
|
|
192
|
+
this._emitEvent({ blocklet: BlockletEvents.certError, server: EVENTS.CERT_ERROR }, cert);
|
|
193
|
+
|
|
194
|
+
onCertIssueFailed(cert, this.states);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
_onCertExpired(cert) {
|
|
198
|
+
onCertExpired(cert, this.states);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
_onCertAboutToExpire(cert) {
|
|
202
|
+
onCertAboutExpire(cert, this.states);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
module.exports = Cert;
|
package/lib/event.js
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
|
+
const get = require('lodash/get');
|
|
2
|
+
const cloneDeep = require('lodash/cloneDeep');
|
|
1
3
|
const { EventEmitter } = require('events');
|
|
2
|
-
const {
|
|
4
|
+
const { wipeSensitiveData } = require('@blocklet/meta/lib/util');
|
|
3
5
|
const logger = require('@abtnode/logger')('@abtnode/core:event');
|
|
4
|
-
const { BlockletStatus, BlockletSource, BlockletEvents } = require('@blocklet/
|
|
6
|
+
const { BLOCKLET_MODES, BlockletStatus, BlockletSource, BlockletEvents } = require('@blocklet/constant');
|
|
7
|
+
const { EVENTS } = require('@abtnode/constant');
|
|
8
|
+
const handleInstanceInStore = require('./util/public-to-store');
|
|
5
9
|
|
|
6
10
|
const eventHub =
|
|
7
11
|
process.env.NODE_ENV === 'test' ? require('@arcblock/event-hub/single') : require('@arcblock/event-hub');
|
|
8
12
|
|
|
9
|
-
const { isCLI } = require('./util');
|
|
10
13
|
const states = require('./states');
|
|
14
|
+
const { isBeforeInstalled } = require('./util');
|
|
11
15
|
|
|
12
16
|
const routingSnapshotPrefix = (blocklet) => (blocklet.mode === BLOCKLET_MODES.DEVELOPMENT ? '[DEV] ' : '');
|
|
13
17
|
|
|
14
18
|
// Initialize the event queue: this will make events across process
|
|
15
19
|
module.exports = ({
|
|
16
20
|
blockletManager,
|
|
17
|
-
blockletRegistry,
|
|
18
21
|
ensureBlockletRouting,
|
|
19
22
|
ensureBlockletRoutingForUpgrade,
|
|
20
23
|
removeBlockletRouting,
|
|
@@ -22,34 +25,63 @@ module.exports = ({
|
|
|
22
25
|
handleRouting,
|
|
23
26
|
domainStatus,
|
|
24
27
|
teamAPI,
|
|
28
|
+
teamManager,
|
|
29
|
+
certManager,
|
|
30
|
+
routerManager,
|
|
31
|
+
node,
|
|
32
|
+
nodeRuntimeMonitor,
|
|
25
33
|
}) => {
|
|
26
34
|
const notificationState = states.notification;
|
|
27
35
|
const nodeState = states.node;
|
|
28
36
|
|
|
29
37
|
const events = new EventEmitter();
|
|
30
|
-
|
|
31
|
-
if (isCLI() && process.env.NODE_ENV !== 'test') {
|
|
32
|
-
events.emit = (name) => logger.debug('stopped core state event in CLI', name);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Push events to event hub for other process to consume
|
|
36
|
-
const originEmit = events.emit.bind(events);
|
|
37
|
-
events.emit = (name, ...args) => {
|
|
38
|
-
logger.debug('proxy event to event hub', { name });
|
|
39
|
-
eventHub.broadcast(name, ...args);
|
|
40
|
-
originEmit(name, ...args);
|
|
41
|
-
};
|
|
38
|
+
events.setMaxListeners(0);
|
|
42
39
|
|
|
43
40
|
let eventHandler = null;
|
|
41
|
+
events.setEventHandler = (handler) => {
|
|
42
|
+
if (typeof handler === 'function') {
|
|
43
|
+
eventHandler = handler;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Listen events from eventHub and call eventHandler
|
|
48
|
+
[...Object.values(BlockletEvents), ...Object.values(EVENTS)].forEach((name) => {
|
|
49
|
+
eventHub.on(name, (data) => {
|
|
50
|
+
if (name === BlockletEvents.removed) {
|
|
51
|
+
// Cleanup cache in teamManager for every node instance
|
|
52
|
+
teamManager.deleteTeam(data?.meta?.did, { closeDatabase: false });
|
|
53
|
+
}
|
|
44
54
|
|
|
55
|
+
if (typeof eventHandler === 'function') {
|
|
56
|
+
eventHandler({ name, data });
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Wipe sensitive data
|
|
62
|
+
// Emit events to event hub
|
|
63
|
+
// Emit events to node listener
|
|
45
64
|
const onEvent = (name, data) => {
|
|
65
|
+
let safeData = data;
|
|
66
|
+
if (get(data, 'meta.did', '')) {
|
|
67
|
+
safeData = wipeSensitiveData(cloneDeep(data));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
logger.debug('proxy event to event hub', { name });
|
|
71
|
+
eventHub.broadcast(name, safeData);
|
|
72
|
+
events.emit(name, safeData);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Emit events to node listener
|
|
76
|
+
// Call eventHandler
|
|
77
|
+
const onInternalEvent = (name, data) => {
|
|
46
78
|
events.emit(name, data);
|
|
47
79
|
if (typeof eventHandler === 'function') {
|
|
48
80
|
eventHandler({ name, data });
|
|
49
81
|
}
|
|
50
82
|
};
|
|
51
83
|
|
|
52
|
-
const
|
|
84
|
+
const handleBlockletInstall = async (name, { blocklet, context }) => {
|
|
53
85
|
try {
|
|
54
86
|
const changed = await ensureBlockletRouting(blocklet, context);
|
|
55
87
|
if (changed) {
|
|
@@ -72,11 +104,13 @@ module.exports = ({
|
|
|
72
104
|
severity: 'error',
|
|
73
105
|
});
|
|
74
106
|
}
|
|
75
|
-
|
|
76
|
-
onEvent(name, blocklet);
|
|
77
107
|
};
|
|
78
108
|
|
|
79
109
|
const handleBlockletRemove = async (name, { blocklet, context }) => {
|
|
110
|
+
if (context?.skipAll) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
80
114
|
try {
|
|
81
115
|
const changed = await removeBlockletRouting(blocklet, context);
|
|
82
116
|
|
|
@@ -96,8 +130,6 @@ module.exports = ({
|
|
|
96
130
|
} catch (error) {
|
|
97
131
|
logger.error('prune blocklet app folder error', { event: name, error });
|
|
98
132
|
}
|
|
99
|
-
|
|
100
|
-
onEvent(name, blocklet);
|
|
101
133
|
};
|
|
102
134
|
|
|
103
135
|
const handleBlockletUpgrade = async (name, { blocklet, context }) => {
|
|
@@ -114,6 +146,14 @@ module.exports = ({
|
|
|
114
146
|
await teamAPI.refreshBlockletInterfacePermissions(blocklet.meta);
|
|
115
147
|
} catch (error) {
|
|
116
148
|
logger.error('upgrade blocklet routing rules error', { event: name, error });
|
|
149
|
+
notificationState.create({
|
|
150
|
+
title: 'Blocklet URL Mapping Error',
|
|
151
|
+
// eslint-disable-next-line max-len
|
|
152
|
+
description: `Failed to upgrade URL mapping for blocklet ${blocklet.meta.name}@${blocklet.meta.version}, due to: ${error.message}`,
|
|
153
|
+
entityType: 'blocklet',
|
|
154
|
+
entityId: blocklet.meta.did,
|
|
155
|
+
severity: 'error',
|
|
156
|
+
});
|
|
117
157
|
}
|
|
118
158
|
|
|
119
159
|
try {
|
|
@@ -121,11 +161,9 @@ module.exports = ({
|
|
|
121
161
|
} catch (error) {
|
|
122
162
|
logger.error('prune blocklet app folder error', { event: name, error });
|
|
123
163
|
}
|
|
124
|
-
|
|
125
|
-
onEvent(name, blocklet);
|
|
126
164
|
};
|
|
127
165
|
|
|
128
|
-
const
|
|
166
|
+
const handleServerEvent = (eventName) => {
|
|
129
167
|
const [, status] = eventName.split('.');
|
|
130
168
|
onEvent(eventName, {
|
|
131
169
|
title: `Blocklet Server ${status}`,
|
|
@@ -135,6 +173,108 @@ module.exports = ({
|
|
|
135
173
|
});
|
|
136
174
|
};
|
|
137
175
|
|
|
176
|
+
/**
|
|
177
|
+
*
|
|
178
|
+
* @description 事件必须注册在这里才能被发布出去
|
|
179
|
+
* @param {string} eventName
|
|
180
|
+
* @param {any} payload
|
|
181
|
+
*/
|
|
182
|
+
const handleBlockletEvent = async (eventName, payload) => {
|
|
183
|
+
const blocklet = payload.blocklet || payload;
|
|
184
|
+
|
|
185
|
+
if ([BlockletEvents.installed].includes(eventName)) {
|
|
186
|
+
await handleBlockletInstall(eventName, payload);
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
await node.createAuditLog({
|
|
190
|
+
action: 'installBlocklet',
|
|
191
|
+
args: {
|
|
192
|
+
did: blocklet.meta.did,
|
|
193
|
+
},
|
|
194
|
+
context: payload.context || {},
|
|
195
|
+
result: blocklet,
|
|
196
|
+
});
|
|
197
|
+
} catch (error) {
|
|
198
|
+
logger.error('Failed to createAuditLog for installBlocklet', { error });
|
|
199
|
+
}
|
|
200
|
+
} else if ([BlockletEvents.upgraded, BlockletEvents.downgraded].includes(eventName)) {
|
|
201
|
+
await handleBlockletUpgrade(eventName, payload);
|
|
202
|
+
|
|
203
|
+
if (payload?.context?.createAuditLog !== false) {
|
|
204
|
+
try {
|
|
205
|
+
await node.createAuditLog({
|
|
206
|
+
action: 'upgradeBlocklet',
|
|
207
|
+
args: {
|
|
208
|
+
did: blocklet.meta.did,
|
|
209
|
+
},
|
|
210
|
+
context: payload.context || {},
|
|
211
|
+
result: blocklet,
|
|
212
|
+
});
|
|
213
|
+
} catch (error) {
|
|
214
|
+
logger.error('Failed to createAuditLog for upgradeBlocklet', { error });
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
} else if ([BlockletEvents.removed, BlockletEvents.dataCleaned].includes(eventName)) {
|
|
218
|
+
await handleBlockletRemove(eventName, payload);
|
|
219
|
+
} else if ([BlockletEvents.started].includes(eventName)) {
|
|
220
|
+
const { publicToStore } = blocklet.settings || {};
|
|
221
|
+
if (publicToStore) {
|
|
222
|
+
handleInstanceInStore(blocklet, { publicToStore }).catch((error) => {
|
|
223
|
+
logger.error('handleInstanceInStore failed', { message: error.message });
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
} else if ([BlockletEvents.upgradeFailed, BlockletEvents.downgradeFailed].includes(eventName)) {
|
|
227
|
+
try {
|
|
228
|
+
await node.createAuditLog({
|
|
229
|
+
action: 'upgradeBlocklet',
|
|
230
|
+
args: {
|
|
231
|
+
did: blocklet.meta.did,
|
|
232
|
+
},
|
|
233
|
+
context: payload.context || {},
|
|
234
|
+
result: {
|
|
235
|
+
...blocklet,
|
|
236
|
+
resultStatus: 'failed',
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
} catch (error) {
|
|
240
|
+
logger.error('Failed to createAuditLog for upgradeBlocklet failed', { error });
|
|
241
|
+
}
|
|
242
|
+
} else if (BlockletEvents.appDidChanged === eventName) {
|
|
243
|
+
const hash = await takeRoutingSnapshot(
|
|
244
|
+
{ message: `${routingSnapshotPrefix(blocklet)}Update blocklet ${blocklet.meta.name} app did`, dryRun: false },
|
|
245
|
+
get(payload, 'context') || {}
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
logger.info('take snapshot after updated blocklet app', { event: eventName, did: blocklet.meta.did, hash });
|
|
249
|
+
} else if (BlockletEvents.spaceConnected === eventName) {
|
|
250
|
+
nodeState
|
|
251
|
+
.read()
|
|
252
|
+
.then((info) => handleRouting(info))
|
|
253
|
+
.catch((err) => {
|
|
254
|
+
logger.error('Reload gateway failed on blocklet.connectedSpace', { error: err });
|
|
255
|
+
});
|
|
256
|
+
logger.info('Reload gateway after blocklet connected to space', { event: eventName, did: blocklet.appDid });
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (
|
|
260
|
+
![BlockletEvents.removed, BlockletEvents.dataCleaned].includes(eventName) &&
|
|
261
|
+
blocklet.status &&
|
|
262
|
+
!isBeforeInstalled(blocklet.status)
|
|
263
|
+
) {
|
|
264
|
+
try {
|
|
265
|
+
await blockletManager.runtimeMonitor.monit(blocklet.meta.did);
|
|
266
|
+
} catch (error) {
|
|
267
|
+
logger.error('monit runtime info failed', { eventName, error });
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (payload.blocklet && !payload.meta) {
|
|
272
|
+
onEvent(eventName, payload.blocklet);
|
|
273
|
+
} else {
|
|
274
|
+
onEvent(eventName, payload);
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
|
|
138
278
|
const downloadAddedBlocklet = async () => {
|
|
139
279
|
try {
|
|
140
280
|
const blocklets = await states.blocklet.find({
|
|
@@ -151,31 +291,50 @@ module.exports = ({
|
|
|
151
291
|
}
|
|
152
292
|
};
|
|
153
293
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
294
|
+
/**
|
|
295
|
+
*
|
|
296
|
+
*
|
|
297
|
+
* @param {*} subject
|
|
298
|
+
* @param {string} event
|
|
299
|
+
* @param {(event: string, data: any) => Promise<void> | void} handler
|
|
300
|
+
*/
|
|
301
|
+
const listen = (subject, event, handler) => subject.on(event, (data) => handler(event, data));
|
|
302
|
+
|
|
303
|
+
[
|
|
304
|
+
BlockletEvents.added,
|
|
305
|
+
BlockletEvents.downloadFailed,
|
|
306
|
+
BlockletEvents.installed,
|
|
307
|
+
BlockletEvents.installFailed,
|
|
308
|
+
BlockletEvents.upgraded,
|
|
309
|
+
BlockletEvents.upgradeFailed,
|
|
310
|
+
BlockletEvents.downgraded,
|
|
311
|
+
BlockletEvents.downgradeFailed,
|
|
312
|
+
BlockletEvents.updated,
|
|
313
|
+
BlockletEvents.statusChange,
|
|
314
|
+
BlockletEvents.removed,
|
|
315
|
+
BlockletEvents.started,
|
|
316
|
+
BlockletEvents.startFailed,
|
|
317
|
+
BlockletEvents.stopped,
|
|
318
|
+
BlockletEvents.appDidChanged,
|
|
319
|
+
|
|
320
|
+
BlockletEvents.backupProgress,
|
|
321
|
+
BlockletEvents.restoreProgress,
|
|
322
|
+
|
|
323
|
+
BlockletEvents.spaceConnected,
|
|
324
|
+
].forEach((eventName) => {
|
|
325
|
+
listen(blockletManager, eventName, handleBlockletEvent);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
listen(notificationState, EVENTS.NOTIFICATION_CREATE, onEvent);
|
|
329
|
+
|
|
330
|
+
listen(nodeState, BlockletEvents.purchaseChange, onEvent);
|
|
331
|
+
nodeState.on(EVENTS.ROUTING_UPDATED, (nodeInfo) => onEvent(EVENTS.ROUTING_UPDATED, { routing: nodeInfo.routing }));
|
|
332
|
+
nodeState.once(EVENTS.NODE_ADDED_OWNER, () => downloadAddedBlocklet());
|
|
333
|
+
nodeState.on(EVENTS.NODE_UPDATED, (nodeInfo, oldInfo) => {
|
|
334
|
+
onEvent(EVENTS.NODE_UPDATED, { did: nodeInfo.did });
|
|
176
335
|
|
|
177
336
|
// We need update router on some fields change
|
|
178
|
-
const fields = ['enableWelcomePage', 'webWalletUrl'];
|
|
337
|
+
const fields = ['enableWelcomePage', 'webWalletUrl', 'registerUrl'];
|
|
179
338
|
const shouldUpdateRouter = fields.some((x) => nodeInfo[x] !== oldInfo[x]);
|
|
180
339
|
if (shouldUpdateRouter) {
|
|
181
340
|
handleRouting(nodeInfo).catch((err) => {
|
|
@@ -183,26 +342,40 @@ module.exports = ({
|
|
|
183
342
|
});
|
|
184
343
|
}
|
|
185
344
|
});
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
345
|
+
|
|
346
|
+
listen(nodeState, EVENTS.NODE_MAINTAIN_PROGRESS, onEvent);
|
|
347
|
+
nodeState.on(EVENTS.RELOAD_GATEWAY, (nodeInfo) => {
|
|
348
|
+
handleRouting(nodeInfo).catch((err) => {
|
|
349
|
+
logger.error('Handle routing failed on node.updated', { error: err });
|
|
350
|
+
});
|
|
191
351
|
});
|
|
192
|
-
teamAPI.on('user.added', (data) => onEvent('user.added', data));
|
|
193
|
-
teamAPI.on('user.removed', (data) => onEvent('user.removed', data));
|
|
194
|
-
teamAPI.on('user.updated', (data) => onEvent('user.updated', data));
|
|
195
|
-
teamAPI.on(BlockletEvents.updated, (data) => onEvent(BlockletEvents.updated, data));
|
|
196
352
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
353
|
+
[EVENTS.DOMAIN_STATUS, BlockletEvents.domainStatus].forEach((eventName) => {
|
|
354
|
+
domainStatus.on(eventName, (data) => {
|
|
355
|
+
if (data) {
|
|
356
|
+
onEvent(eventName, data);
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
listen(teamAPI, EVENTS.USER_ADDED, onEvent);
|
|
362
|
+
listen(teamAPI, EVENTS.USER_REMOVED, onEvent);
|
|
363
|
+
listen(teamAPI, EVENTS.USER_UPDATED, onEvent);
|
|
364
|
+
listen(teamAPI, BlockletEvents.updated, onEvent);
|
|
365
|
+
listen(teamManager, BlockletEvents.storeChange, onEvent);
|
|
366
|
+
|
|
367
|
+
listen(certManager, EVENTS.CERT_ISSUED, onEvent);
|
|
368
|
+
listen(certManager, EVENTS.CERT_ERROR, onEvent);
|
|
369
|
+
listen(certManager, BlockletEvents.certIssued, onEvent);
|
|
370
|
+
listen(certManager, BlockletEvents.certError, onEvent);
|
|
371
|
+
|
|
372
|
+
listen(routerManager, BlockletEvents.updated, onEvent);
|
|
373
|
+
|
|
374
|
+
listen(nodeRuntimeMonitor, EVENTS.NODE_RUNTIME_INFO, onInternalEvent);
|
|
375
|
+
listen(blockletManager.runtimeMonitor, EVENTS.BLOCKLETS_RUNTIME_INFO, onInternalEvent);
|
|
202
376
|
|
|
203
|
-
events.
|
|
204
|
-
events.
|
|
205
|
-
events.handleCLIEvent = handleCLIEvent;
|
|
377
|
+
events.handleServerEvent = handleServerEvent;
|
|
378
|
+
events.handleBlockletEvent = handleBlockletEvent;
|
|
206
379
|
|
|
207
380
|
return events;
|
|
208
381
|
};
|