@abtnode/core 1.6.20 → 1.6.23
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/manager/disk.js +40 -13
- package/lib/blocklet/registry.js +4 -51
- package/lib/index.js +2 -2
- package/lib/migrations/1.6.21-rename-ip-echo-domain.js +35 -0
- package/lib/migrations/index.js +6 -2
- package/lib/router/helper.js +85 -52
- package/lib/router/index.js +7 -7
- package/lib/states/blocklet.js +15 -16
- package/lib/states/node.js +4 -3
- package/lib/util/blocklet.js +33 -3
- package/lib/util/get-accessible-external-node-ip.js +2 -2
- package/lib/util/get-domain-for-blocklet.js +4 -1
- package/lib/util/index.js +5 -5
- package/lib/util/registry.js +90 -0
- package/lib/util/service.js +5 -20
- package/lib/webhook/index.js +24 -25
- package/package.json +20 -20
|
@@ -20,7 +20,7 @@ const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager');
|
|
|
20
20
|
const downloadFile = require('@abtnode/util/lib/download-file');
|
|
21
21
|
const Lock = require('@abtnode/util/lib/lock');
|
|
22
22
|
const { getVcFromPresentation } = require('@abtnode/util/lib/vc');
|
|
23
|
-
const {
|
|
23
|
+
const { VC_TYPE_BLOCKLET_PURCHASE } = require('@abtnode/constant');
|
|
24
24
|
|
|
25
25
|
const getBlockletEngine = require('@blocklet/meta/lib/engine');
|
|
26
26
|
const {
|
|
@@ -90,7 +90,9 @@ const {
|
|
|
90
90
|
getDiffFiles,
|
|
91
91
|
getBundleDir,
|
|
92
92
|
needBlockletDownload,
|
|
93
|
+
verifyPurchase,
|
|
93
94
|
} = require('../../util/blocklet');
|
|
95
|
+
const { parseSourceUrl } = require('../../util/registry');
|
|
94
96
|
const states = require('../../states');
|
|
95
97
|
const BlockletRegistry = require('../registry');
|
|
96
98
|
const BaseBlockletManager = require('./base');
|
|
@@ -197,6 +199,20 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
197
199
|
throw new Error('Unknown source');
|
|
198
200
|
}
|
|
199
201
|
|
|
202
|
+
/**
|
|
203
|
+
* @param {String} rootDid
|
|
204
|
+
* @param {String} mountPoint
|
|
205
|
+
* @param {Boolean} context.blockletPurchaseVerified
|
|
206
|
+
*
|
|
207
|
+
* installFromUrl
|
|
208
|
+
* @param {String} url
|
|
209
|
+
*
|
|
210
|
+
* InstallFromUpload
|
|
211
|
+
* @param {Object} file
|
|
212
|
+
* @param {String} did for diff upload
|
|
213
|
+
* @param {String} diffVersion for diff upload
|
|
214
|
+
* @param {Array} deleteSet for diff upload
|
|
215
|
+
*/
|
|
200
216
|
async installComponent({ rootDid, mountPoint, url, file, did, diffVersion, deleteSet }, context = {}) {
|
|
201
217
|
logger.debug('start install component', { rootDid, mountPoint, url });
|
|
202
218
|
|
|
@@ -244,8 +260,8 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
244
260
|
// FIXME: 这里的 trustedIssuers 相当于相信任何 VC,需要想更安全的方法
|
|
245
261
|
verifyPresentation({ presentation: vcPresentation, trustedIssuers: [get(vc, 'issuer.id')], challenge });
|
|
246
262
|
|
|
247
|
-
if (!vc.type.includes(
|
|
248
|
-
throw new Error(`Expect ${
|
|
263
|
+
if (!vc.type.includes(VC_TYPE_BLOCKLET_PURCHASE)) {
|
|
264
|
+
throw new Error(`Expect ${VC_TYPE_BLOCKLET_PURCHASE} VC type`);
|
|
249
265
|
}
|
|
250
266
|
|
|
251
267
|
const blockletUrl = get(vc, 'credentialSubject.purchased.blocklet.url');
|
|
@@ -658,7 +674,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
658
674
|
/**
|
|
659
675
|
* upgrade blocklet from registry
|
|
660
676
|
*/
|
|
661
|
-
async upgrade({ did, registryUrl }, context) {
|
|
677
|
+
async upgrade({ did, registryUrl, sync }, context) {
|
|
662
678
|
const blocklet = await states.blocklet.getBlocklet(did);
|
|
663
679
|
|
|
664
680
|
// TODO: 查看了下目前页面中的升级按钮,都是会传 registryUrl 过来的,这个函数里的逻辑感觉需要在以后做一个简化
|
|
@@ -725,6 +741,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
725
741
|
source: BlockletSource.registry,
|
|
726
742
|
deployedFrom: upgradeFromRegistry,
|
|
727
743
|
context,
|
|
744
|
+
sync,
|
|
728
745
|
});
|
|
729
746
|
}
|
|
730
747
|
|
|
@@ -905,7 +922,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
905
922
|
}
|
|
906
923
|
|
|
907
924
|
const { did, version } = meta;
|
|
908
|
-
meta.title = `[DEV] ${meta.title || meta.name}`;
|
|
909
925
|
|
|
910
926
|
const exist = await states.blocklet.getBlocklet(did);
|
|
911
927
|
if (exist) {
|
|
@@ -1364,7 +1380,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1364
1380
|
}
|
|
1365
1381
|
}
|
|
1366
1382
|
|
|
1367
|
-
async _installFromStore({ did, registry }, context) {
|
|
1383
|
+
async _installFromStore({ did, registry, sync }, context) {
|
|
1368
1384
|
logger.debug('start install blocklet', { did });
|
|
1369
1385
|
if (!isValidDid(did)) {
|
|
1370
1386
|
throw new Error('Blocklet did is invalid');
|
|
@@ -1372,25 +1388,24 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1372
1388
|
|
|
1373
1389
|
const registryUrl = registry || (await states.node.getBlockletRegistry());
|
|
1374
1390
|
const info = await BlockletRegistry.getRegistryMeta(registryUrl);
|
|
1375
|
-
const
|
|
1376
|
-
if (!
|
|
1391
|
+
const meta = await this.registry.getBlocklet(did, registryUrl);
|
|
1392
|
+
if (!meta) {
|
|
1377
1393
|
throw new Error('Can not install blocklet that not found in registry');
|
|
1378
1394
|
}
|
|
1379
1395
|
|
|
1380
|
-
const state = await states.blocklet.getBlocklet(
|
|
1396
|
+
const state = await states.blocklet.getBlocklet(meta.did);
|
|
1381
1397
|
if (state) {
|
|
1382
1398
|
throw new Error('Can not install an already installed blocklet');
|
|
1383
1399
|
}
|
|
1384
1400
|
|
|
1385
|
-
|
|
1386
|
-
throw new Error('Can not install a non-free blocklet directly');
|
|
1387
|
-
}
|
|
1401
|
+
verifyPurchase(meta, context);
|
|
1388
1402
|
|
|
1389
1403
|
// install
|
|
1390
1404
|
return this._install({
|
|
1391
|
-
meta
|
|
1405
|
+
meta,
|
|
1392
1406
|
source: BlockletSource.registry,
|
|
1393
1407
|
deployedFrom: info.cdnUrl || registryUrl,
|
|
1408
|
+
sync,
|
|
1394
1409
|
context,
|
|
1395
1410
|
});
|
|
1396
1411
|
}
|
|
@@ -1398,6 +1413,16 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1398
1413
|
async _installFromUrl({ url, sync }, context) {
|
|
1399
1414
|
logger.debug('start install blocklet', { url });
|
|
1400
1415
|
|
|
1416
|
+
const { inStore, registryUrl, blockletDid } = await parseSourceUrl(url);
|
|
1417
|
+
if (inStore) {
|
|
1418
|
+
const exist = await states.blocklet.getBlocklet(blockletDid);
|
|
1419
|
+
if (exist) {
|
|
1420
|
+
return this.upgrade({ did: blockletDid, registryUrl }, context);
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
return this._installFromStore({ did: blockletDid, registry: registryUrl }, context);
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1401
1426
|
const meta = await getBlockletMetaFromUrl(url);
|
|
1402
1427
|
|
|
1403
1428
|
if (!meta) {
|
|
@@ -1442,6 +1467,8 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1442
1467
|
throw new Error('The blocklet cannot be a component');
|
|
1443
1468
|
}
|
|
1444
1469
|
|
|
1470
|
+
verifyPurchase(meta, context);
|
|
1471
|
+
|
|
1445
1472
|
const newChildren = await parseChildren(blocklet.meta, {
|
|
1446
1473
|
children: [
|
|
1447
1474
|
{
|
package/lib/blocklet/registry.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
const isBase64 = require('is-base64');
|
|
2
1
|
const { BlockletGroup } = require('@blocklet/meta/lib/constants');
|
|
3
2
|
const joinURL = require('url-join');
|
|
4
3
|
const get = require('lodash/get');
|
|
5
|
-
const
|
|
6
|
-
const { BLOCKLET_STORE_API_PREFIX, BLOCKLET_STORE_META_PATH } = require('@abtnode/constant');
|
|
4
|
+
const { BLOCKLET_STORE_API_PREFIX } = require('@abtnode/constant');
|
|
7
5
|
|
|
8
6
|
const { name } = require('../../package.json');
|
|
9
7
|
|
|
@@ -15,6 +13,7 @@ const states = require('../states');
|
|
|
15
13
|
const isRequirementsSatisfied = require('../util/requirement');
|
|
16
14
|
const { fixAndVerifyBlockletMeta } = require('../util/blocklet');
|
|
17
15
|
const { translate } = require('../locales');
|
|
16
|
+
const { validateRegistryURL, getRegistryMeta } = require('../util/registry');
|
|
18
17
|
|
|
19
18
|
const DEFAULT_REFRESH_INTERVAL = 1 * 60 * 1000;
|
|
20
19
|
const MAX_REFRESH_INTERVAL = 1 * 60 * 1000;
|
|
@@ -152,54 +151,8 @@ class BlockletRegistry {
|
|
|
152
151
|
}
|
|
153
152
|
}
|
|
154
153
|
|
|
155
|
-
BlockletRegistry.validateRegistryURL =
|
|
156
|
-
const url = joinURL(registry, BLOCKLET_STORE_API_PREFIX, `/blocklets.json?__t__=${Date.now()}`);
|
|
157
|
-
try {
|
|
158
|
-
const res = await request.get(url);
|
|
159
|
-
if (Array.isArray(res.data)) {
|
|
160
|
-
return res.data;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
logger.error('Blocklet list fetch failed ', { url, data: res.data });
|
|
164
|
-
throw new Error('blocklet list fetch failed');
|
|
165
|
-
} catch (error) {
|
|
166
|
-
logger.error('Blocklet registry refresh failed', { url, error });
|
|
167
|
-
throw new Error(`Invalid Blocklet Registry URL ${registry}: ${error.message}`);
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
BlockletRegistry.getRegistryMeta = async (registry) => {
|
|
172
|
-
try {
|
|
173
|
-
const url = joinURL(registry, BLOCKLET_STORE_META_PATH, `?__t__=${Date.now()}`);
|
|
174
|
-
const { data } = await request.get(url);
|
|
175
|
-
|
|
176
|
-
if (!data) {
|
|
177
|
-
return {};
|
|
178
|
-
}
|
|
154
|
+
BlockletRegistry.validateRegistryURL = validateRegistryURL;
|
|
179
155
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
const missingFields = requiredFields.filter((x) => !data[x]);
|
|
183
|
-
if (missingFields.length > 0) {
|
|
184
|
-
throw new Error(`the registry missing required information: ${missingFields.join(', ')}`);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const result = pick(data, ['id', 'name', 'description', 'maintainer', 'cdnUrl', 'chainHost']);
|
|
188
|
-
const { logoUrl } = data;
|
|
189
|
-
if (logoUrl) {
|
|
190
|
-
if (logoUrl.startsWith('http') === true) {
|
|
191
|
-
result.logoUrl = logoUrl;
|
|
192
|
-
} else if (isBase64(logoUrl, { allowMime: true })) {
|
|
193
|
-
result.logoUrl = logoUrl;
|
|
194
|
-
} else {
|
|
195
|
-
result.logoUrl = joinURL(registry, logoUrl);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return result;
|
|
200
|
-
} catch (err) {
|
|
201
|
-
throw new Error(`Can not get meta info for registry [${registry}]: ${err.message}`);
|
|
202
|
-
}
|
|
203
|
-
};
|
|
156
|
+
BlockletRegistry.getRegistryMeta = getRegistryMeta;
|
|
204
157
|
|
|
205
158
|
module.exports = BlockletRegistry;
|
package/lib/index.js
CHANGED
|
@@ -147,7 +147,7 @@ function ABTNode(options) {
|
|
|
147
147
|
getCertificates,
|
|
148
148
|
getSitesFromSnapshot,
|
|
149
149
|
getRoutingCrons,
|
|
150
|
-
|
|
150
|
+
ensureWildcardCerts,
|
|
151
151
|
} = getRouterHelpers({ dataDirs, routingSnapshot, routerManager, blockletManager, certManager });
|
|
152
152
|
|
|
153
153
|
const teamManager = new TeamManager({ nodeDid: options.nodeDid, dataDirs, states });
|
|
@@ -318,7 +318,7 @@ function ABTNode(options) {
|
|
|
318
318
|
takeRoutingSnapshot,
|
|
319
319
|
getSitesFromSnapshot,
|
|
320
320
|
ensureDashboardRouting,
|
|
321
|
-
|
|
321
|
+
ensureWildcardCerts,
|
|
322
322
|
|
|
323
323
|
addDomainAlias: routerManager.addDomainAlias.bind(routerManager),
|
|
324
324
|
deleteDomainAlias: routerManager.deleteDomainAlias.bind(routerManager),
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const yaml = require('js-yaml');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const set = require('lodash/set');
|
|
4
|
+
const omit = require('lodash/omit');
|
|
5
|
+
const {
|
|
6
|
+
DEFAULT_WILDCARD_CERT_HOST,
|
|
7
|
+
DEFAULT_DID_DOMAIN,
|
|
8
|
+
DEFAULT_DID_REGISTRY,
|
|
9
|
+
DEFAULT_IP_DOMAIN,
|
|
10
|
+
} = require('@abtnode/constant');
|
|
11
|
+
|
|
12
|
+
module.exports = async ({ states, printInfo, configFile }) => {
|
|
13
|
+
printInfo('Try to rename dashboardDomain to ipWildcardDomain in db...');
|
|
14
|
+
|
|
15
|
+
let info = await states.node.read();
|
|
16
|
+
set(info, 'routing.ipWildcardDomain', info.routing.dashboardDomain || DEFAULT_IP_DOMAIN);
|
|
17
|
+
set(info, 'routing.wildcardCertHost', DEFAULT_WILDCARD_CERT_HOST);
|
|
18
|
+
set(info, 'didDomain', DEFAULT_DID_DOMAIN);
|
|
19
|
+
set(info, 'didRegistry', DEFAULT_DID_REGISTRY);
|
|
20
|
+
info = omit(info, 'routing.dashboardDomain');
|
|
21
|
+
|
|
22
|
+
await states.node.updateNodeInfo(info);
|
|
23
|
+
|
|
24
|
+
if (process.env.NODE_ENV !== 'development') {
|
|
25
|
+
let rawConfig = yaml.safeLoad(fs.readFileSync(configFile).toString());
|
|
26
|
+
set(rawConfig, 'node.routing.ipWildcardDomain', info.routing.ipWildcardDomain);
|
|
27
|
+
set(rawConfig, 'node.routing.wildcardCertHost', DEFAULT_WILDCARD_CERT_HOST);
|
|
28
|
+
set(rawConfig, 'node.didDomain', DEFAULT_DID_DOMAIN);
|
|
29
|
+
set(rawConfig, 'node.didRegistry', DEFAULT_DID_REGISTRY);
|
|
30
|
+
rawConfig = omit(rawConfig, 'node.routing.dashboardDomain');
|
|
31
|
+
fs.writeFileSync(configFile, yaml.dump(rawConfig));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
printInfo(`> Persist new config to file: ${configFile}`);
|
|
35
|
+
};
|
package/lib/migrations/index.js
CHANGED
|
@@ -132,9 +132,13 @@ const runMigrationScripts = async ({
|
|
|
132
132
|
for (let i = 0; i < scripts.length; i++) {
|
|
133
133
|
const { script, version } = scripts[i];
|
|
134
134
|
try {
|
|
135
|
-
|
|
136
|
-
if (executed === false) {
|
|
135
|
+
if (process.env.NODE_ENV === 'development') {
|
|
137
136
|
pending.push(scripts[i]);
|
|
137
|
+
} else {
|
|
138
|
+
const executed = await node.isMigrationExecuted({ script, version });
|
|
139
|
+
if (executed === false) {
|
|
140
|
+
pending.push(scripts[i]);
|
|
141
|
+
}
|
|
138
142
|
}
|
|
139
143
|
} catch (err) {
|
|
140
144
|
printError(`Failed to detect migration script execution status: ${script}, error: ${err.message}`);
|
package/lib/router/helper.js
CHANGED
|
@@ -223,6 +223,19 @@ const ensureServiceRule = async (sites) => {
|
|
|
223
223
|
return site;
|
|
224
224
|
});
|
|
225
225
|
};
|
|
226
|
+
const ensureRootRule = async (sites) => {
|
|
227
|
+
return sites.map((site) => {
|
|
228
|
+
if (!isBasicSite(site.domain) && !site.rules.some((x) => x.from.pathPrefix === '/')) {
|
|
229
|
+
site.rules.push({
|
|
230
|
+
from: { pathPrefix: '/' },
|
|
231
|
+
to: {
|
|
232
|
+
type: ROUTING_RULE_TYPES.NONE,
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
return site;
|
|
237
|
+
});
|
|
238
|
+
};
|
|
226
239
|
|
|
227
240
|
const ensureLatestNodeInfo = async (sites = [], { withDefaultCors = true } = {}) => {
|
|
228
241
|
const info = await states.node.read();
|
|
@@ -419,6 +432,13 @@ const decompressCertificates = async (source, dest) => {
|
|
|
419
432
|
return dest;
|
|
420
433
|
};
|
|
421
434
|
|
|
435
|
+
const joinCertDownUrl = (baseUrl, name) => joinUrl(baseUrl, '/certs', name);
|
|
436
|
+
|
|
437
|
+
const getIpEchoCertDownloadUrl = (baseUrl) => joinCertDownUrl(baseUrl, 'ip-abtnet-io.tar.gz');
|
|
438
|
+
const getDidDomainCertDownloadUrl = (baseUrl) => joinCertDownUrl(baseUrl, 'did-abtnet-io.tar.gz');
|
|
439
|
+
const getDownloadCertBaseUrl = (info) =>
|
|
440
|
+
process.env.ABT_NODE_WILDCARD_CERT_HOST || get(info, 'routing.wildcardCertHost', '');
|
|
441
|
+
|
|
422
442
|
module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerManager, blockletManager, certManager }) {
|
|
423
443
|
const nodeState = states.node;
|
|
424
444
|
const blockletState = states.blocklet;
|
|
@@ -428,46 +448,13 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
428
448
|
// site level duplication detection
|
|
429
449
|
const hasRuleByPrefix = (site, value) => site.rules.find((x) => x.isProtected && get(x, 'from.pathPrefix') === value);
|
|
430
450
|
|
|
431
|
-
const
|
|
432
|
-
const info = await nodeState.read();
|
|
433
|
-
const provider = getProviderFromNodeInfo(info);
|
|
434
|
-
if (provider === ROUTER_PROVIDER_NONE) {
|
|
435
|
-
return;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
const https = get(info, 'routing.https', true);
|
|
439
|
-
const dashboardDomain = get(info, 'routing.dashboardDomain', '');
|
|
440
|
-
const certDownloadAddress =
|
|
441
|
-
process.env.ABT_NODE_DASHBOARD_CERT_DOWN_URL || get(info, 'routing.dashboardCertDownloadAddress', '');
|
|
442
|
-
if (!https || !dashboardDomain || !certDownloadAddress) {
|
|
443
|
-
return;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
if (checkExpire) {
|
|
447
|
-
try {
|
|
448
|
-
const cert = await routerManager.findCertificateByDomain(dashboardDomain);
|
|
449
|
-
if (!cert) {
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
const now = Date.now();
|
|
454
|
-
const certInfo = getHttpsCertInfo(cert.certificate);
|
|
455
|
-
if (certInfo.validTo - now >= CERTIFICATE_EXPIRES_OFFSET) {
|
|
456
|
-
logger.info('skip dashboard certificate update before not expired');
|
|
457
|
-
return;
|
|
458
|
-
}
|
|
459
|
-
} catch (err) {
|
|
460
|
-
logger.error('failed to check dashboard certificate expiration', { error: err });
|
|
461
|
-
return;
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
|
|
451
|
+
const downloadCert = async ({ domain, url }) => {
|
|
465
452
|
const destFolder = getTmpDirectory(path.join(`certificate-${Date.now()}`));
|
|
466
453
|
try {
|
|
467
454
|
const filename = path.join(destFolder, 'certificate.tar.gz');
|
|
468
455
|
fs.ensureDirSync(destFolder);
|
|
469
456
|
|
|
470
|
-
await downloadFile(
|
|
457
|
+
await downloadFile(url, filename);
|
|
471
458
|
await decompressCertificates(filename, destFolder);
|
|
472
459
|
|
|
473
460
|
const certificateFilePath = path.join(destFolder, 'cert.pem');
|
|
@@ -485,7 +472,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
485
472
|
const privateKey = fs.readFileSync(privateKeyFilePath).toString();
|
|
486
473
|
|
|
487
474
|
await certManager.upsertByDomain({
|
|
488
|
-
domain
|
|
475
|
+
domain,
|
|
489
476
|
privateKey,
|
|
490
477
|
certificate,
|
|
491
478
|
isProtected: true,
|
|
@@ -499,23 +486,68 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
499
486
|
}
|
|
500
487
|
};
|
|
501
488
|
|
|
502
|
-
const
|
|
489
|
+
const updateCert = async (domain, url) => {
|
|
490
|
+
try {
|
|
491
|
+
const cert = await routerManager.findCertificateByDomain(domain);
|
|
492
|
+
if (!cert) {
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const now = Date.now();
|
|
497
|
+
const certInfo = getHttpsCertInfo(cert.certificate);
|
|
498
|
+
if (certInfo.validTo - now >= CERTIFICATE_EXPIRES_OFFSET) {
|
|
499
|
+
logger.info('skip dashboard certificate update before not expired', { domain, url });
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
await downloadCert({
|
|
504
|
+
domain,
|
|
505
|
+
url,
|
|
506
|
+
});
|
|
507
|
+
} catch (err) {
|
|
508
|
+
logger.error('failed to check dashboard certificate expiration', { error: err, domain, url });
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
const updateDashboardCertificates = async () => {
|
|
503
513
|
const info = await nodeState.read();
|
|
504
|
-
const
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
throw new Error('dashboardCertDownloadAddress and dashboardDomain are not found in the routing configs');
|
|
514
|
+
const provider = getProviderFromNodeInfo(info);
|
|
515
|
+
if (provider === ROUTER_PROVIDER_NONE) {
|
|
516
|
+
return;
|
|
508
517
|
}
|
|
509
518
|
|
|
510
|
-
const
|
|
511
|
-
|
|
512
|
-
|
|
519
|
+
const https = get(info, 'routing.https', true);
|
|
520
|
+
const ipWildcardDomain = get(info, 'routing.ipWildcardDomain', '');
|
|
521
|
+
const didDomain = info.didDomain;
|
|
522
|
+
const certDownloadAddress = getDownloadCertBaseUrl(info);
|
|
523
|
+
if (!https || !certDownloadAddress) {
|
|
524
|
+
return;
|
|
513
525
|
}
|
|
514
526
|
|
|
515
|
-
|
|
516
|
-
await
|
|
517
|
-
|
|
518
|
-
|
|
527
|
+
await updateCert(ipWildcardDomain, getIpEchoCertDownloadUrl(certDownloadAddress));
|
|
528
|
+
await updateCert(`*.${didDomain}`, getDidDomainCertDownloadUrl(certDownloadAddress));
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
const ensureWildcardCerts = async () => {
|
|
532
|
+
const info = await nodeState.read();
|
|
533
|
+
const didDomain = info.didDomain;
|
|
534
|
+
const ipWildcardDomain = get(info, 'routing.ipWildcardDomain', '');
|
|
535
|
+
|
|
536
|
+
const ensureDomainCert = async (domain, url) => {
|
|
537
|
+
const cert = await certManager.getByDomain(domain);
|
|
538
|
+
if (!cert) {
|
|
539
|
+
await downloadCert({
|
|
540
|
+
domain,
|
|
541
|
+
url,
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
const certBaseUrl = getDownloadCertBaseUrl(info);
|
|
547
|
+
await Promise.all([
|
|
548
|
+
ensureDomainCert(ipWildcardDomain, getIpEchoCertDownloadUrl(certBaseUrl)),
|
|
549
|
+
ensureDomainCert(`*.${didDomain}`, getDidDomainCertDownloadUrl(certBaseUrl)),
|
|
550
|
+
]);
|
|
519
551
|
};
|
|
520
552
|
|
|
521
553
|
const upsertSiteRule = async ({ site, rule }, context) => {
|
|
@@ -658,9 +690,9 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
658
690
|
return domainAlias === item;
|
|
659
691
|
});
|
|
660
692
|
|
|
661
|
-
const
|
|
693
|
+
const ipWildcardDomain = get(info, 'routing.ipWildcardDomain', '');
|
|
662
694
|
const didDomain = `${info.did.toLowerCase()}.${info.didDomain}`;
|
|
663
|
-
let dashboardAliasDomains = [
|
|
695
|
+
let dashboardAliasDomains = [ipWildcardDomain, didDomain];
|
|
664
696
|
|
|
665
697
|
dashboardAliasDomains = dashboardAliasDomains
|
|
666
698
|
.filter((item) => item && !isExistsInAlias(item))
|
|
@@ -1159,6 +1191,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
1159
1191
|
sites = await ensureLatestInfo(sites);
|
|
1160
1192
|
sites = await ensureAuthService(sites, blockletManager);
|
|
1161
1193
|
sites = await ensureServiceRule(sites);
|
|
1194
|
+
sites = await ensureRootRule(sites);
|
|
1162
1195
|
|
|
1163
1196
|
const certificates = httpsEnabled ? await certManager.getAllNormal() : [];
|
|
1164
1197
|
|
|
@@ -1388,7 +1421,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
1388
1421
|
getSitesFromSnapshot,
|
|
1389
1422
|
getCertificates,
|
|
1390
1423
|
checkDomain,
|
|
1391
|
-
|
|
1424
|
+
ensureWildcardCerts,
|
|
1392
1425
|
addWellknownSite,
|
|
1393
1426
|
upsertSiteRule,
|
|
1394
1427
|
|
|
@@ -1396,7 +1429,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
1396
1429
|
{
|
|
1397
1430
|
name: 'update-dashboard-certificate',
|
|
1398
1431
|
time: '0 1 */6 * * *', // refetch on 0:00, 6:00, etc.
|
|
1399
|
-
fn: () =>
|
|
1432
|
+
fn: () => updateDashboardCertificates(),
|
|
1400
1433
|
options: { runOnInit: true },
|
|
1401
1434
|
},
|
|
1402
1435
|
{
|
package/lib/router/index.js
CHANGED
|
@@ -5,7 +5,7 @@ const {
|
|
|
5
5
|
DOMAIN_FOR_DEFAULT_SITE,
|
|
6
6
|
DOMAIN_FOR_IP_SITE_REGEXP,
|
|
7
7
|
ROUTING_RULE_TYPES,
|
|
8
|
-
|
|
8
|
+
DEFAULT_IP_DOMAIN,
|
|
9
9
|
BLOCKLET_PROXY_PATH_PREFIX,
|
|
10
10
|
BLOCKLET_SITE_GROUP_SUFFIX,
|
|
11
11
|
} = require('@abtnode/constant');
|
|
@@ -60,12 +60,12 @@ const getRoutingTable = ({ sites, nodeInfo }) => {
|
|
|
60
60
|
let routingTable = Router.formatSites(sites);
|
|
61
61
|
routingTable = expandSites(routingTable);
|
|
62
62
|
|
|
63
|
-
// put
|
|
63
|
+
// put ipWildcardDomain to last, to let blockletDomain match first
|
|
64
64
|
// e.g.
|
|
65
|
-
//
|
|
65
|
+
// ipWildcardDomain: 192-168-3-2.ip.abtnet.io
|
|
66
66
|
// blockletDomain: static-demo-xxx-192-168-3-2.ip.abtnet.io
|
|
67
|
-
const
|
|
68
|
-
const index = routingTable.findIndex((x) => x.domain ===
|
|
67
|
+
const ipWildcardDomain = get(nodeInfo, 'routing.ipWildcardDomain', '');
|
|
68
|
+
const index = routingTable.findIndex((x) => x.domain === ipWildcardDomain);
|
|
69
69
|
if (index > -1) {
|
|
70
70
|
routingTable.push(...routingTable.splice(index, 1));
|
|
71
71
|
}
|
|
@@ -219,11 +219,11 @@ Router.formatSites = (sites = []) => {
|
|
|
219
219
|
|
|
220
220
|
Router.flattenSitesToRules = (sites = [], info = {}) => {
|
|
221
221
|
const result = [];
|
|
222
|
-
const
|
|
222
|
+
const ipWildcardDomain = get(info, 'routing.ipWildcardDomain', DEFAULT_IP_DOMAIN);
|
|
223
223
|
sites.forEach((site) => {
|
|
224
224
|
const aliases = (site.domainAliases || [])
|
|
225
225
|
.map((x) => (typeof x === 'string' ? x : x.value))
|
|
226
|
-
.filter((x) => x !==
|
|
226
|
+
.filter((x) => x !== ipWildcardDomain)
|
|
227
227
|
.filter(Boolean);
|
|
228
228
|
|
|
229
229
|
if (Array.isArray(site.rules) && site.rules.length > 0) {
|
package/lib/states/blocklet.js
CHANGED
|
@@ -9,6 +9,7 @@ const detectPort = require('detect-port');
|
|
|
9
9
|
const Lock = require('@abtnode/util/lib/lock');
|
|
10
10
|
const security = require('@abtnode/util/lib/security');
|
|
11
11
|
const { fixPerson, fixInterfaces } = require('@blocklet/meta/lib/fix');
|
|
12
|
+
const { getDisplayName } = require('@blocklet/meta/lib/util');
|
|
12
13
|
const {
|
|
13
14
|
BlockletStatus,
|
|
14
15
|
BlockletSource,
|
|
@@ -25,6 +26,7 @@ const { validateBlockletMeta } = require('../util');
|
|
|
25
26
|
|
|
26
27
|
const lock = new Lock('blocklet-port-assign-lock');
|
|
27
28
|
|
|
29
|
+
const isHex = (str) => /^0x[0-9a-f]+$/i.test(str);
|
|
28
30
|
const getMaxPort = (ports = {}) => Math.max(Object.values(ports).map(Number));
|
|
29
31
|
|
|
30
32
|
const getExternalPortsFromMeta = (meta) =>
|
|
@@ -50,10 +52,10 @@ const formatBlocklet = (blocklet, phase, dek) => {
|
|
|
50
52
|
if (!env) {
|
|
51
53
|
return;
|
|
52
54
|
}
|
|
53
|
-
if (phase === 'onUpdate' && env.value
|
|
55
|
+
if (phase === 'onUpdate' && isHex(env.value) === true) {
|
|
54
56
|
env.value = security.encrypt(env.value, b.meta.did, dek);
|
|
55
57
|
}
|
|
56
|
-
if (phase === 'onRead' && env.value
|
|
58
|
+
if (phase === 'onRead' && isHex(env.value) === false) {
|
|
57
59
|
env.value = security.decrypt(env.value, b.meta.did, dek);
|
|
58
60
|
}
|
|
59
61
|
});
|
|
@@ -243,20 +245,17 @@ class BlockletState extends BaseState {
|
|
|
243
245
|
await this.fillChildrenPorts(children, { oldChildren: doc.children, defaultPort: getMaxPort(ports) });
|
|
244
246
|
|
|
245
247
|
// add to db
|
|
246
|
-
const newDoc = await this.
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
ports,
|
|
253
|
-
},
|
|
248
|
+
const newDoc = await this.updateBlocklet(meta.did, {
|
|
249
|
+
meta: omit(sanitized, ['htmlAst']),
|
|
250
|
+
source,
|
|
251
|
+
deployedFrom,
|
|
252
|
+
children,
|
|
253
|
+
ports,
|
|
254
254
|
});
|
|
255
255
|
lock.release();
|
|
256
256
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
resolve(formatted);
|
|
257
|
+
this.emit('upgrade', newDoc);
|
|
258
|
+
resolve(newDoc);
|
|
260
259
|
} catch (err) {
|
|
261
260
|
lock.release();
|
|
262
261
|
reject(err);
|
|
@@ -446,8 +445,7 @@ class BlockletState extends BaseState {
|
|
|
446
445
|
return child;
|
|
447
446
|
});
|
|
448
447
|
|
|
449
|
-
|
|
450
|
-
return formatBlocklet({ ...doc, ...updates }, 'onRead', this.options.dek);
|
|
448
|
+
return this.updateBlocklet(did, updates);
|
|
451
449
|
}
|
|
452
450
|
|
|
453
451
|
async fillChildrenPorts(children, { defaultPort = 0, oldChildren } = {}) {
|
|
@@ -511,7 +509,7 @@ class BlockletState extends BaseState {
|
|
|
511
509
|
const { meta, mountPoint, sourceUrl = '', source = '', deployedFrom = '' } = child;
|
|
512
510
|
|
|
513
511
|
if (!mountPoint) {
|
|
514
|
-
throw new Error(`mountPoint is required when adding component ${
|
|
512
|
+
throw new Error(`mountPoint is required when adding component ${getDisplayName(child, true)}`);
|
|
515
513
|
}
|
|
516
514
|
|
|
517
515
|
if (meta.did === parent.meta.did) {
|
|
@@ -542,5 +540,6 @@ class BlockletState extends BaseState {
|
|
|
542
540
|
}
|
|
543
541
|
|
|
544
542
|
BlockletState.BlockletStatus = BlockletStatus;
|
|
543
|
+
BlockletState.formatBlocklet = formatBlocklet;
|
|
545
544
|
|
|
546
545
|
module.exports = BlockletState;
|
package/lib/states/node.js
CHANGED
|
@@ -92,7 +92,7 @@ class NodeState extends BaseState {
|
|
|
92
92
|
mode,
|
|
93
93
|
runtimeConfig,
|
|
94
94
|
ownerNft,
|
|
95
|
-
|
|
95
|
+
launcher,
|
|
96
96
|
didRegistry,
|
|
97
97
|
didDomain,
|
|
98
98
|
enablePassportIssuance = true,
|
|
@@ -128,7 +128,7 @@ class NodeState extends BaseState {
|
|
|
128
128
|
runtimeConfig,
|
|
129
129
|
ownerNft,
|
|
130
130
|
diskAlertThreshold: DISK_ALERT_THRESHOLD_PERCENT,
|
|
131
|
-
|
|
131
|
+
launcher: launcher || undefined,
|
|
132
132
|
didRegistry,
|
|
133
133
|
didDomain,
|
|
134
134
|
enablePassportIssuance,
|
|
@@ -162,7 +162,8 @@ class NodeState extends BaseState {
|
|
|
162
162
|
// FIXME: 这个接口比较危险,可能会修改一些本不应该修改的字段,后续需要考虑改进
|
|
163
163
|
async updateNodeInfo(entity = {}) {
|
|
164
164
|
const record = await this.read();
|
|
165
|
-
|
|
165
|
+
|
|
166
|
+
const updateResult = await this.update(record._id, { $set: omit(entity, ['ownerNft', 'sk']) });
|
|
166
167
|
this.emit('node.updated', updateResult, record);
|
|
167
168
|
return updateResult;
|
|
168
169
|
}
|
package/lib/util/blocklet.js
CHANGED
|
@@ -42,7 +42,7 @@ const validateBlockletEntry = require('@blocklet/meta/lib/entry');
|
|
|
42
42
|
const getBlockletEngine = require('@blocklet/meta/lib/engine');
|
|
43
43
|
const getBlockletInfo = require('@blocklet/meta/lib/info');
|
|
44
44
|
const { validateMeta, fixAndValidateService } = require('@blocklet/meta/lib/validate');
|
|
45
|
-
const { forEachBlocklet } = require('@blocklet/meta/lib/util');
|
|
45
|
+
const { forEachBlocklet, isFreeBlocklet, getDisplayName } = require('@blocklet/meta/lib/util');
|
|
46
46
|
|
|
47
47
|
const { validate: validateEngine, get: getEngine } = require('../blocklet/manager/engine');
|
|
48
48
|
|
|
@@ -951,7 +951,7 @@ const getRuntimeInfo = async (appId) => {
|
|
|
951
951
|
|
|
952
952
|
/**
|
|
953
953
|
* merge services
|
|
954
|
-
* from meta.children[].mountPoints[].services
|
|
954
|
+
* from meta.children[].mountPoints[].services, meta.children[].services
|
|
955
955
|
* to childrenMeta[].interfaces[].services
|
|
956
956
|
*
|
|
957
957
|
* @param {array<child>|object{children:array}} source e.g. [<config>] or { children: [<config>] }
|
|
@@ -993,6 +993,23 @@ const mergeMeta = (source, childrenMeta = []) => {
|
|
|
993
993
|
childInterface.services = services;
|
|
994
994
|
}
|
|
995
995
|
});
|
|
996
|
+
|
|
997
|
+
if (config.services) {
|
|
998
|
+
const childInterface = findWebInterface(childMeta);
|
|
999
|
+
if (childInterface) {
|
|
1000
|
+
// merge
|
|
1001
|
+
const services = childInterface.services || [];
|
|
1002
|
+
config.services.forEach((x) => {
|
|
1003
|
+
const index = services.findIndex((y) => y.name === x.name);
|
|
1004
|
+
if (index >= 0) {
|
|
1005
|
+
services.splice(index, 1, x);
|
|
1006
|
+
} else {
|
|
1007
|
+
services.push(x);
|
|
1008
|
+
}
|
|
1009
|
+
});
|
|
1010
|
+
childInterface.services = services;
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
996
1013
|
});
|
|
997
1014
|
};
|
|
998
1015
|
|
|
@@ -1052,7 +1069,7 @@ const checkDuplicateComponents = (dynamicComponents, staticComponents) => {
|
|
|
1052
1069
|
if (duplicates.length) {
|
|
1053
1070
|
throw new Error(
|
|
1054
1071
|
`Cannot add duplicate component${duplicates.length > 1 ? 's' : ''}: ${duplicates
|
|
1055
|
-
.map((x) => x
|
|
1072
|
+
.map((x) => getDisplayName(x, true))
|
|
1056
1073
|
.join(', ')}`
|
|
1057
1074
|
);
|
|
1058
1075
|
}
|
|
@@ -1117,6 +1134,18 @@ const needBlockletDownload = (blocklet, oldBlocklet) => {
|
|
|
1117
1134
|
return true;
|
|
1118
1135
|
};
|
|
1119
1136
|
|
|
1137
|
+
const verifyPurchase = (meta, opts = {}) => {
|
|
1138
|
+
if (opts.blockletPurchaseVerified) {
|
|
1139
|
+
return true;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
if (isFreeBlocklet(meta)) {
|
|
1143
|
+
return true;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
throw new Error('Can not install a non-free blocklet directly');
|
|
1147
|
+
};
|
|
1148
|
+
|
|
1120
1149
|
module.exports = {
|
|
1121
1150
|
forEachBlocklet,
|
|
1122
1151
|
getBlockletMetaFromUrl,
|
|
@@ -1152,4 +1181,5 @@ module.exports = {
|
|
|
1152
1181
|
getDiffFiles,
|
|
1153
1182
|
getBundleDir,
|
|
1154
1183
|
needBlockletDownload,
|
|
1184
|
+
verifyPurchase,
|
|
1155
1185
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const joinUrl = require('url-join');
|
|
2
2
|
const axios = require('@abtnode/util/lib/axios');
|
|
3
|
-
const {
|
|
3
|
+
const { DEFAULT_IP_DOMAIN, DEFAULT_ADMIN_PATH } = require('@abtnode/constant');
|
|
4
4
|
const { get: getIp } = require('./ip');
|
|
5
5
|
|
|
6
|
-
const getNodeDomain = (ip) => (ip ?
|
|
6
|
+
const getNodeDomain = (ip) => (ip ? DEFAULT_IP_DOMAIN.replace(/^\*/, ip.replace(/\./g, '-')) : '');
|
|
7
7
|
|
|
8
8
|
let cache = null;
|
|
9
9
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const slugify = require('slugify');
|
|
2
2
|
const { DEFAULT_IP_DNS_DOMAIN_SUFFIX } = require('@abtnode/constant');
|
|
3
|
+
const md5 = require('@abtnode/util/lib/md5');
|
|
3
4
|
|
|
4
5
|
const SLOT_FOR_IP_DNS_SITE = '888-888-888-888';
|
|
5
6
|
|
|
@@ -17,7 +18,9 @@ const getIpDnsDomainForBlocklet = (blocklet, blockletInterface) => {
|
|
|
17
18
|
};
|
|
18
19
|
|
|
19
20
|
const getDidDomainForBlocklet = ({ name, daemonDid, didDomain }) => {
|
|
20
|
-
|
|
21
|
+
const prefix = md5(name).substring(0, 8);
|
|
22
|
+
|
|
23
|
+
return `${prefix}-${daemonDid.toLowerCase()}.${didDomain}`;
|
|
21
24
|
};
|
|
22
25
|
|
|
23
26
|
module.exports = { getIpDnsDomainForBlocklet, getDidDomainForBlocklet };
|
package/lib/util/index.js
CHANGED
|
@@ -373,7 +373,7 @@ const getBaseUrls = async (node, ips) => {
|
|
|
373
373
|
if (info.routing.provider !== ROUTER_PROVIDER_NONE && info.routing.snapshotHash && info.routing.adminPath) {
|
|
374
374
|
const sites = await node.getSitesFromSnapshot();
|
|
375
375
|
|
|
376
|
-
const {
|
|
376
|
+
const { ipWildcardDomain } = info.routing;
|
|
377
377
|
const adminPath = normalizePathPrefix(info.routing.adminPath);
|
|
378
378
|
const tmpHttpPort = getPort(httpPort, DEFAULT_HTTP_PORT);
|
|
379
379
|
|
|
@@ -383,12 +383,12 @@ const getBaseUrls = async (node, ips) => {
|
|
|
383
383
|
};
|
|
384
384
|
});
|
|
385
385
|
|
|
386
|
-
if (
|
|
387
|
-
const site = sites.find((c) => (c.domainAliases || []).find((item) => item.value ===
|
|
386
|
+
if (ipWildcardDomain) {
|
|
387
|
+
const site = sites.find((c) => (c.domainAliases || []).find((item) => item.value === ipWildcardDomain));
|
|
388
388
|
if (site) {
|
|
389
|
-
const httpInfo = await getHttpInfo(
|
|
389
|
+
const httpInfo = await getHttpInfo(ipWildcardDomain);
|
|
390
390
|
const httpsUrls = availableIps.map((ip) => ({
|
|
391
|
-
url: `${httpInfo.protocol}://${transformIPToDomain(ip)}.${
|
|
391
|
+
url: `${httpInfo.protocol}://${transformIPToDomain(ip)}.${ipWildcardDomain.substring(2)}${
|
|
392
392
|
httpInfo.port
|
|
393
393
|
}${adminPath}`,
|
|
394
394
|
}));
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const joinURL = require('url-join');
|
|
2
|
+
const pick = require('lodash/pick');
|
|
3
|
+
const isBase64 = require('is-base64');
|
|
4
|
+
|
|
5
|
+
const { BLOCKLET_STORE_API_PREFIX, BLOCKLET_STORE_META_PATH } = require('@abtnode/constant');
|
|
6
|
+
|
|
7
|
+
const { name } = require('../../package.json');
|
|
8
|
+
const logger = require('@abtnode/logger')(`${name}:util:registry`); // eslint-disable-line
|
|
9
|
+
|
|
10
|
+
const request = require('./request');
|
|
11
|
+
|
|
12
|
+
const validateRegistryURL = async (registry) => {
|
|
13
|
+
const url = joinURL(registry, BLOCKLET_STORE_API_PREFIX, `/blocklets.json?__t__=${Date.now()}`);
|
|
14
|
+
try {
|
|
15
|
+
const res = await request.get(url);
|
|
16
|
+
if (Array.isArray(res.data)) {
|
|
17
|
+
return res.data;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
logger.error('Blocklet list fetch failed ', { url, data: res.data });
|
|
21
|
+
throw new Error('blocklet list fetch failed');
|
|
22
|
+
} catch (error) {
|
|
23
|
+
logger.error('Blocklet registry refresh failed', { url, error });
|
|
24
|
+
throw new Error(`Invalid Blocklet Registry URL ${registry}: ${error.message}`);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const getRegistryMeta = async (registry) => {
|
|
29
|
+
try {
|
|
30
|
+
const url = joinURL(registry, BLOCKLET_STORE_META_PATH, `?__t__=${Date.now()}`);
|
|
31
|
+
const { data } = await request.get(url);
|
|
32
|
+
|
|
33
|
+
if (!data) {
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const requiredFields = ['name', 'description', 'maintainer'];
|
|
38
|
+
|
|
39
|
+
const missingFields = requiredFields.filter((x) => !data[x]);
|
|
40
|
+
if (missingFields.length > 0) {
|
|
41
|
+
throw new Error(`the registry missing required information: ${missingFields.join(', ')}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const result = pick(data, ['id', 'name', 'description', 'maintainer', 'cdnUrl', 'chainHost']);
|
|
45
|
+
const { logoUrl } = data;
|
|
46
|
+
if (logoUrl) {
|
|
47
|
+
if (logoUrl.startsWith('http') === true) {
|
|
48
|
+
result.logoUrl = logoUrl;
|
|
49
|
+
} else if (isBase64(logoUrl, { allowMime: true })) {
|
|
50
|
+
result.logoUrl = logoUrl;
|
|
51
|
+
} else {
|
|
52
|
+
result.logoUrl = joinURL(registry, logoUrl);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return result;
|
|
57
|
+
} catch (err) {
|
|
58
|
+
throw new Error(`Can not get meta info for registry [${registry}]: ${err.message}`);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const parseSourceUrl = async (url) => {
|
|
63
|
+
const { origin, pathname } = new URL(url);
|
|
64
|
+
|
|
65
|
+
const match = pathname.match(/^\/api\/blocklets\/(\w*)\/blocklet\.json/);
|
|
66
|
+
if (match) {
|
|
67
|
+
try {
|
|
68
|
+
const m = await getRegistryMeta(origin);
|
|
69
|
+
if (m && m.id) {
|
|
70
|
+
return {
|
|
71
|
+
inStore: true,
|
|
72
|
+
registryUrl: origin,
|
|
73
|
+
blockletDid: match[1],
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
} catch {
|
|
77
|
+
// meat is not in store, do nothing
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
inStore: false,
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
module.exports = {
|
|
87
|
+
validateRegistryURL,
|
|
88
|
+
getRegistryMeta,
|
|
89
|
+
parseSourceUrl,
|
|
90
|
+
};
|
package/lib/util/service.js
CHANGED
|
@@ -1,30 +1,15 @@
|
|
|
1
1
|
const fs = require('fs-extra');
|
|
2
|
-
const { NODE_SERVICES } = require('@abtnode/constant');
|
|
3
|
-
|
|
4
|
-
const servicesNames = Object.values(NODE_SERVICES);
|
|
5
2
|
|
|
6
3
|
const getServices = ({ stringifySchema = false } = {}) =>
|
|
7
|
-
|
|
4
|
+
['auth']
|
|
8
5
|
.map((x) => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
// Read service meta
|
|
12
|
-
try {
|
|
13
|
-
const packageFile = require.resolve(`${x}/package.json`);
|
|
14
|
-
const { name, description, version } = fs.readJSONSync(packageFile);
|
|
15
|
-
service.name = name;
|
|
16
|
-
service.description = description || name;
|
|
17
|
-
service.version = version;
|
|
18
|
-
} catch (err) {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
6
|
+
let service;
|
|
21
7
|
|
|
22
|
-
// Read service config: optional
|
|
23
8
|
try {
|
|
24
|
-
const
|
|
25
|
-
service
|
|
9
|
+
const metaFile = require.resolve(`@abtnode/blocklet-services/services/${x}/meta.json`);
|
|
10
|
+
service = fs.readJSONSync(metaFile);
|
|
26
11
|
} catch (err) {
|
|
27
|
-
service
|
|
12
|
+
service = {};
|
|
28
13
|
}
|
|
29
14
|
|
|
30
15
|
if (stringifySchema) {
|
package/lib/webhook/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
const logger = require('@abtnode/logger')('@abtnode/core:webhook:index');
|
|
2
|
-
const upperFirst = require('lodash/upperFirst');
|
|
3
2
|
|
|
3
|
+
const sortPriorityUrl = require('@abtnode/util/lib/sort-priority-url');
|
|
4
4
|
const WebHookSender = require('./sender');
|
|
5
5
|
const createQueue = require('../queue');
|
|
6
6
|
const IP = require('../util/ip');
|
|
7
7
|
const states = require('../states');
|
|
8
8
|
const { getBaseUrls } = require('../util');
|
|
9
9
|
|
|
10
|
-
const getSlackUrlInfo = (actionPath = '/notifications', urls) => {
|
|
10
|
+
const getSlackUrlInfo = async (actionPath = '/notifications', urls) => {
|
|
11
11
|
const info = [];
|
|
12
12
|
|
|
13
13
|
if (actionPath && actionPath.startsWith('/blocklet/')) {
|
|
@@ -28,29 +28,27 @@ const getSlackUrlInfo = (actionPath = '/notifications', urls) => {
|
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const normalized = `${x.url}${actionPath}`.replace(`${protocol}//`, '').replace(/\/+/g, '/');
|
|
39
|
-
const url = `${protocol}//${normalized}`;
|
|
40
|
-
elements.push({
|
|
41
|
-
type: 'button',
|
|
42
|
-
text: {
|
|
43
|
-
type: 'plain_text',
|
|
44
|
-
text: `${upperFirst(x.type)} Address`,
|
|
45
|
-
},
|
|
46
|
-
style: 'primary',
|
|
47
|
-
value: `${upperFirst(x.type)} Address`,
|
|
48
|
-
url,
|
|
49
|
-
});
|
|
50
|
-
});
|
|
31
|
+
const prioritys = await sortPriorityUrl(urls);
|
|
32
|
+
const priorityUrl = prioritys[0].url;
|
|
33
|
+
|
|
34
|
+
const { protocol } = new URL(priorityUrl);
|
|
35
|
+
const normalized = `${priorityUrl}${actionPath}`.replace(`${protocol}//`, '').replace(/\/+/g, '/');
|
|
36
|
+
const url = `${protocol}//${normalized}`;
|
|
37
|
+
|
|
51
38
|
info.push({
|
|
52
39
|
type: 'actions',
|
|
53
|
-
elements
|
|
40
|
+
elements: [
|
|
41
|
+
{
|
|
42
|
+
type: 'button',
|
|
43
|
+
text: {
|
|
44
|
+
type: 'plain_text',
|
|
45
|
+
text: 'Click Me',
|
|
46
|
+
},
|
|
47
|
+
style: 'primary',
|
|
48
|
+
value: 'Click Me',
|
|
49
|
+
url,
|
|
50
|
+
},
|
|
51
|
+
],
|
|
54
52
|
});
|
|
55
53
|
|
|
56
54
|
return info;
|
|
@@ -66,7 +64,7 @@ module.exports = ({ events, dataDirs, instance }) => {
|
|
|
66
64
|
const webhookList = await webhookState.list();
|
|
67
65
|
const nodeInfo = await nodeState.read();
|
|
68
66
|
const { internal, external } = await IP.get();
|
|
69
|
-
const baseUrls = await getBaseUrls(instance, [
|
|
67
|
+
const baseUrls = await getBaseUrls(instance, [external, internal]);
|
|
70
68
|
const senderFns = {};
|
|
71
69
|
|
|
72
70
|
if (webhookList.length) {
|
|
@@ -79,7 +77,8 @@ module.exports = ({ events, dataDirs, instance }) => {
|
|
|
79
77
|
const { name } = nodeInfo;
|
|
80
78
|
const options = { ...message, nodeInfo: `*${name}*` };
|
|
81
79
|
if (item.type === 'slack') {
|
|
82
|
-
|
|
80
|
+
// eslint-disable-next-line
|
|
81
|
+
options.urlInfo = await getSlackUrlInfo(message.action, baseUrls);
|
|
83
82
|
}
|
|
84
83
|
try {
|
|
85
84
|
// eslint-disable-next-line
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.6.
|
|
6
|
+
"version": "1.6.23",
|
|
7
7
|
"description": "",
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"files": [
|
|
@@ -19,28 +19,28 @@
|
|
|
19
19
|
"author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@abtnode/certificate-manager": "1.6.
|
|
23
|
-
"@abtnode/constant": "1.6.
|
|
24
|
-
"@abtnode/cron": "1.6.
|
|
25
|
-
"@abtnode/db": "1.6.
|
|
26
|
-
"@abtnode/logger": "1.6.
|
|
27
|
-
"@abtnode/queue": "1.6.
|
|
28
|
-
"@abtnode/rbac": "1.6.
|
|
29
|
-
"@abtnode/router-provider": "1.6.
|
|
30
|
-
"@abtnode/static-server": "1.6.
|
|
31
|
-
"@abtnode/timemachine": "1.6.
|
|
32
|
-
"@abtnode/util": "1.6.
|
|
33
|
-
"@arcblock/did": "^1.14.
|
|
34
|
-
"@arcblock/event-hub": "1.14.
|
|
22
|
+
"@abtnode/certificate-manager": "1.6.23",
|
|
23
|
+
"@abtnode/constant": "1.6.23",
|
|
24
|
+
"@abtnode/cron": "1.6.23",
|
|
25
|
+
"@abtnode/db": "1.6.23",
|
|
26
|
+
"@abtnode/logger": "1.6.23",
|
|
27
|
+
"@abtnode/queue": "1.6.23",
|
|
28
|
+
"@abtnode/rbac": "1.6.23",
|
|
29
|
+
"@abtnode/router-provider": "1.6.23",
|
|
30
|
+
"@abtnode/static-server": "1.6.23",
|
|
31
|
+
"@abtnode/timemachine": "1.6.23",
|
|
32
|
+
"@abtnode/util": "1.6.23",
|
|
33
|
+
"@arcblock/did": "^1.14.19",
|
|
34
|
+
"@arcblock/event-hub": "1.14.19",
|
|
35
35
|
"@arcblock/pm2-events": "^0.0.5",
|
|
36
|
-
"@arcblock/vc": "^1.14.
|
|
37
|
-
"@blocklet/meta": "1.6.
|
|
36
|
+
"@arcblock/vc": "^1.14.19",
|
|
37
|
+
"@blocklet/meta": "1.6.23",
|
|
38
38
|
"@fidm/x509": "^1.2.1",
|
|
39
39
|
"@nedb/core": "^1.2.2",
|
|
40
40
|
"@nedb/multi": "^1.2.2",
|
|
41
|
-
"@ocap/mcrypto": "^1.14.
|
|
42
|
-
"@ocap/util": "^1.14.
|
|
43
|
-
"@ocap/wallet": "^1.14.
|
|
41
|
+
"@ocap/mcrypto": "^1.14.19",
|
|
42
|
+
"@ocap/util": "^1.14.19",
|
|
43
|
+
"@ocap/wallet": "^1.14.19",
|
|
44
44
|
"@slack/webhook": "^5.0.3",
|
|
45
45
|
"axios": "^0.25.0",
|
|
46
46
|
"axon": "^2.0.3",
|
|
@@ -75,5 +75,5 @@
|
|
|
75
75
|
"express": "^4.17.1",
|
|
76
76
|
"jest": "^27.4.5"
|
|
77
77
|
},
|
|
78
|
-
"gitHead": "
|
|
78
|
+
"gitHead": "6c478fb7e2a30b302981b5339f349c69134e022e"
|
|
79
79
|
}
|