@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.
@@ -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 { BLOCKLET_PURCHASE_NFT_TYPE } = require('@abtnode/constant');
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(BLOCKLET_PURCHASE_NFT_TYPE)) {
248
- throw new Error(`Expect ${BLOCKLET_PURCHASE_NFT_TYPE} VC type`);
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 blocklet = await this.registry.getBlocklet(did, registryUrl);
1376
- if (!blocklet) {
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(blocklet.did);
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
- if (isFreeBlocklet(blocklet) === false && !context.blockletPurchaseVerified) {
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: blocklet,
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
  {
@@ -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 pick = require('lodash/pick');
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 = async (registry) => {
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
- const requiredFields = ['name', 'description', 'maintainer'];
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
- ensureDashboardCertificate,
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
- ensureDashboardCertificate,
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
+ };
@@ -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
- const executed = await node.isMigrationExecuted({ script, version });
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}`);
@@ -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 updateDashboardCertificate = async ({ checkExpire = true }) => {
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(certDownloadAddress, filename);
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: dashboardDomain,
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 ensureDashboardCertificate = async () => {
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 downloadUrl = get(info, 'routing.dashboardCertDownloadAddress', '');
505
- const dashboardDomain = get(info, 'routing.dashboardDomain', '');
506
- if (!dashboardDomain || !downloadUrl) {
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 cert = await certManager.getByDomain(dashboardDomain);
511
- if (cert) {
512
- return { status: 'existed' };
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
- logger.debug('downloading dashboard ip-domain certificate', { url: downloadUrl, dashboardDomain });
516
- await updateDashboardCertificate({ checkExpire: false });
517
- logger.debug('downloaded dashboard ip-domain certificate', { url: downloadUrl, dashboardDomain });
518
- return { status: 'downloaded' };
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 dashboardDomain = get(info, 'routing.dashboardDomain', '');
693
+ const ipWildcardDomain = get(info, 'routing.ipWildcardDomain', '');
662
694
  const didDomain = `${info.did.toLowerCase()}.${info.didDomain}`;
663
- let dashboardAliasDomains = [dashboardDomain, didDomain];
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
- ensureDashboardCertificate,
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: () => updateDashboardCertificate({ checkExpire: true }),
1432
+ fn: () => updateDashboardCertificates(),
1400
1433
  options: { runOnInit: true },
1401
1434
  },
1402
1435
  {
@@ -5,7 +5,7 @@ const {
5
5
  DOMAIN_FOR_DEFAULT_SITE,
6
6
  DOMAIN_FOR_IP_SITE_REGEXP,
7
7
  ROUTING_RULE_TYPES,
8
- DEFAULT_DASHBOARD_DOMAIN,
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 dashboardDomain to last, to let blockletDomain match first
63
+ // put ipWildcardDomain to last, to let blockletDomain match first
64
64
  // e.g.
65
- // dashboardDomain: 192-168-3-2.ip.abtnet.io
65
+ // ipWildcardDomain: 192-168-3-2.ip.abtnet.io
66
66
  // blockletDomain: static-demo-xxx-192-168-3-2.ip.abtnet.io
67
- const dashboardDomain = get(nodeInfo, 'routing.dashboardDomain', '');
68
- const index = routingTable.findIndex((x) => x.domain === dashboardDomain);
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 dashboardDomain = get(info, 'routing.dashboardDomain', DEFAULT_DASHBOARD_DOMAIN);
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 !== dashboardDomain)
226
+ .filter((x) => x !== ipWildcardDomain)
227
227
  .filter(Boolean);
228
228
 
229
229
  if (Array.isArray(site.rules) && site.rules.length > 0) {
@@ -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.indexOf('0x') === 0) {
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.indexOf('0x') === -1) {
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.updateById(doc._id, {
247
- $set: {
248
- meta: omit(sanitized, ['htmlAst']),
249
- source,
250
- deployedFrom,
251
- children,
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
- const formatted = formatBlocklet(newDoc, 'onRead', this.options.dek);
258
- this.emit('upgrade', formatted);
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
- await this.update(doc._id, { $set: updates });
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 ${meta.title || meta.name}`);
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;
@@ -92,7 +92,7 @@ class NodeState extends BaseState {
92
92
  mode,
93
93
  runtimeConfig,
94
94
  ownerNft,
95
- launcherInfo,
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
- launcherInfo: launcherInfo || undefined,
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
- const updateResult = await this.update(record._id, { $set: omit(entity, ['ownerNft']) });
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
  }
@@ -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.meta.title || x.meta.name)
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 { DEFAULT_DASHBOARD_DOMAIN, DEFAULT_ADMIN_PATH } = require('@abtnode/constant');
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 ? DEFAULT_DASHBOARD_DOMAIN.replace(/^\*/, ip.replace(/\./g, '-')) : '');
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
- return `${formatName(name)}-${daemonDid.toLowerCase()}.${didDomain}`;
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 { dashboardDomain } = info.routing;
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 (dashboardDomain) {
387
- const site = sites.find((c) => (c.domainAliases || []).find((item) => item.value === dashboardDomain));
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(dashboardDomain);
389
+ const httpInfo = await getHttpInfo(ipWildcardDomain);
390
390
  const httpsUrls = availableIps.map((ip) => ({
391
- url: `${httpInfo.protocol}://${transformIPToDomain(ip)}.${dashboardDomain.substring(2)}${
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
+ };
@@ -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
- servicesNames
4
+ ['auth']
8
5
  .map((x) => {
9
- const service = {};
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 schemaFile = require.resolve(`${x}/api/schema.json`);
25
- service.schema = fs.readJSONSync(schemaFile);
9
+ const metaFile = require.resolve(`@abtnode/blocklet-services/services/${x}/meta.json`);
10
+ service = fs.readJSONSync(metaFile);
26
11
  } catch (err) {
27
- service.schema = {};
12
+ service = {};
28
13
  }
29
14
 
30
15
  if (stringifySchema) {
@@ -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 httpUrls = urls.filter((x) => x.url.startsWith('http://'));
32
- const httpsUrls = urls.filter((x) => x.url.startsWith('https://'));
33
-
34
- const showUrls = httpsUrls.length ? httpsUrls : httpUrls;
35
- const elements = [];
36
- showUrls.forEach((x) => {
37
- const { protocol } = new URL(x.url);
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, [internal, external]);
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
- options.urlInfo = getSlackUrlInfo(message.action, baseUrls);
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.20",
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.20",
23
- "@abtnode/constant": "1.6.20",
24
- "@abtnode/cron": "1.6.20",
25
- "@abtnode/db": "1.6.20",
26
- "@abtnode/logger": "1.6.20",
27
- "@abtnode/queue": "1.6.20",
28
- "@abtnode/rbac": "1.6.20",
29
- "@abtnode/router-provider": "1.6.20",
30
- "@abtnode/static-server": "1.6.20",
31
- "@abtnode/timemachine": "1.6.20",
32
- "@abtnode/util": "1.6.20",
33
- "@arcblock/did": "^1.14.13",
34
- "@arcblock/event-hub": "1.14.13",
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.13",
37
- "@blocklet/meta": "1.6.20",
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.13",
42
- "@ocap/util": "^1.14.13",
43
- "@ocap/wallet": "^1.14.13",
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": "e695a9fa4e2cbf7fcef554be3309090c37919d94"
78
+ "gitHead": "6c478fb7e2a30b302981b5339f349c69134e022e"
79
79
  }