@abtnode/core 1.8.65-beta-5405baf2 → 1.8.65-beta-f7af64a4
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/team.js +1 -0
- package/lib/blocklet/manager/disk.js +130 -124
- package/lib/blocklet/manager/helper/install-from-backup.js +2 -0
- package/lib/router/helper.js +23 -2
- package/lib/router/index.js +4 -1
- package/lib/states/blocklet.js +3 -0
- package/lib/util/blocklet.js +9 -6
- package/lib/validators/router.js +7 -1
- package/package.json +17 -17
package/lib/api/team.js
CHANGED
|
@@ -444,6 +444,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
444
444
|
|
|
445
445
|
async start({ did, throwOnError, checkHealthImmediately = false, e2eMode = false }, context) {
|
|
446
446
|
logger.info('start blocklet', { did });
|
|
447
|
+
// should check blocklet integrity
|
|
447
448
|
const blocklet = await this.ensureBlocklet(did, { e2eMode });
|
|
448
449
|
|
|
449
450
|
try {
|
|
@@ -557,7 +558,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
557
558
|
async stop({ did, updateStatus = true, silent = false }, context) {
|
|
558
559
|
logger.info('stop blocklet', { did });
|
|
559
560
|
|
|
560
|
-
const blocklet = await this.
|
|
561
|
+
const blocklet = await this.getBlocklet(did);
|
|
561
562
|
const { processId } = blocklet.env;
|
|
562
563
|
|
|
563
564
|
if (updateStatus) {
|
|
@@ -655,7 +656,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
655
656
|
|
|
656
657
|
// eslint-disable-next-line no-unused-vars
|
|
657
658
|
async reload({ did }, context) {
|
|
658
|
-
const blocklet = await this.
|
|
659
|
+
const blocklet = await this.getBlocklet(did);
|
|
659
660
|
|
|
660
661
|
await states.blocklet.setBlockletStatus(did, BlockletStatus.stopping);
|
|
661
662
|
await reloadBlockletProcess(blocklet);
|
|
@@ -671,13 +672,12 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
671
672
|
logger.info('delete blocklet', { did, keepData });
|
|
672
673
|
|
|
673
674
|
try {
|
|
674
|
-
const blocklet = await this.
|
|
675
|
+
const blocklet = await this.getBlocklet(did);
|
|
675
676
|
if (isDeletableBlocklet(blocklet) === false) {
|
|
676
677
|
throw new Error('Blocklet is protected from accidental deletion');
|
|
677
678
|
}
|
|
678
679
|
|
|
679
680
|
const nodeEnvironments = await states.node.getEnvironments();
|
|
680
|
-
|
|
681
681
|
await deleteBlockletProcess(blocklet, {
|
|
682
682
|
preDelete: (b, { ancestors }) =>
|
|
683
683
|
hooks.preUninstall(b.env.processId, {
|
|
@@ -700,31 +700,27 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
700
700
|
severity: 'success',
|
|
701
701
|
});
|
|
702
702
|
return doc;
|
|
703
|
-
} catch (
|
|
703
|
+
} catch (error) {
|
|
704
704
|
// If we installed a corrupted blocklet accidentally, just cleanup the disk and state db
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
const doc = await this._deleteBlocklet({ did, keepData, keepLogsDir, keepConfigs }, context);
|
|
708
|
-
|
|
709
|
-
this._createNotification(doc.meta.did, {
|
|
710
|
-
title: 'Blocklet Deleted',
|
|
711
|
-
description: `Blocklet ${doc.meta.name}@${doc.meta.version} is deleted.`,
|
|
712
|
-
entityType: 'blocklet',
|
|
713
|
-
entityId: doc.meta.did,
|
|
714
|
-
severity: 'success',
|
|
715
|
-
});
|
|
705
|
+
logger.error('blocklet delete failed, will delete again', { did, error });
|
|
706
|
+
const doc = await this._deleteBlocklet({ did, keepData, keepLogsDir, keepConfigs }, context);
|
|
716
707
|
|
|
717
|
-
|
|
718
|
-
|
|
708
|
+
this._createNotification(doc.meta.did, {
|
|
709
|
+
title: 'Blocklet Deleted',
|
|
710
|
+
description: `Blocklet ${doc.meta.name}@${doc.meta.version} is deleted.`,
|
|
711
|
+
entityType: 'blocklet',
|
|
712
|
+
entityId: doc.meta.did,
|
|
713
|
+
severity: 'success',
|
|
714
|
+
});
|
|
719
715
|
|
|
720
|
-
|
|
716
|
+
return doc;
|
|
721
717
|
}
|
|
722
718
|
}
|
|
723
719
|
|
|
724
720
|
async reset({ did, childDid }, context = {}) {
|
|
725
721
|
logger.info('reset blocklet', { did, childDid });
|
|
726
722
|
|
|
727
|
-
const blocklet = await this.
|
|
723
|
+
const blocklet = await this.getBlocklet(did);
|
|
728
724
|
|
|
729
725
|
if (isInProgress(blocklet.status || blocklet.status === BlockletStatus.running)) {
|
|
730
726
|
throw new Error('Cannot reset when blocklet is in progress');
|
|
@@ -774,7 +770,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
774
770
|
async deleteComponent({ did, rootDid, keepData, keepState }, context) {
|
|
775
771
|
logger.info('delete blocklet component', { did, rootDid, keepData });
|
|
776
772
|
|
|
777
|
-
const blocklet = await this.
|
|
773
|
+
const blocklet = await this.getBlocklet(rootDid);
|
|
778
774
|
const child = blocklet.children.find((x) => x.meta.did === did);
|
|
779
775
|
if (!child) {
|
|
780
776
|
throw new Error('Component does not exist');
|
|
@@ -828,7 +824,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
828
824
|
await states.blockletExtras.delConfigs([blocklet.meta.did, child.meta.did]);
|
|
829
825
|
}
|
|
830
826
|
|
|
831
|
-
const newBlocklet = await this.
|
|
827
|
+
const newBlocklet = await this.getBlocklet(rootDid);
|
|
832
828
|
this.emit(BlockletEvents.upgraded, { blocklet: newBlocklet, context: { ...context, createAuditLog: false } }); // trigger router refresh
|
|
833
829
|
|
|
834
830
|
this._createNotification(newBlocklet.meta.did, {
|
|
@@ -881,7 +877,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
881
877
|
|
|
882
878
|
// eslint-disable-next-line no-unused-vars
|
|
883
879
|
async deleteProcess({ did }, context) {
|
|
884
|
-
const blocklet = await this.
|
|
880
|
+
const blocklet = await this.getBlocklet(did);
|
|
885
881
|
|
|
886
882
|
logger.info('delete blocklet process', { did });
|
|
887
883
|
|
|
@@ -904,17 +900,17 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
904
900
|
|
|
905
901
|
if (!attachRuntimeInfo) {
|
|
906
902
|
try {
|
|
907
|
-
const blocklet = await this.
|
|
903
|
+
const blocklet = await this.getBlocklet(did, { throwOnNotExist: false });
|
|
908
904
|
return blocklet;
|
|
909
905
|
} catch (e) {
|
|
910
906
|
logger.error('get blocklet detail error', { error: e });
|
|
911
|
-
return
|
|
907
|
+
return states.blocklet.getBlocklet(did);
|
|
912
908
|
}
|
|
913
909
|
}
|
|
914
910
|
|
|
915
911
|
const nodeInfo = await states.node.read();
|
|
916
912
|
|
|
917
|
-
return this.
|
|
913
|
+
return this._attachRuntimeInfo({ did, nodeInfo, diskInfo: true, context });
|
|
918
914
|
}
|
|
919
915
|
|
|
920
916
|
async attachBlockletListRuntimeInfo({ blocklets, useCache }, context) {
|
|
@@ -929,7 +925,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
929
925
|
const cachedBlocklet =
|
|
930
926
|
useCache && this.cachedBlocklets ? this.cachedBlocklets.find((y) => y.meta.did === x.meta.did) : null;
|
|
931
927
|
|
|
932
|
-
return this.
|
|
928
|
+
return this._attachRuntimeInfo({
|
|
933
929
|
did: x.meta.did,
|
|
934
930
|
nodeInfo,
|
|
935
931
|
diskInfo: false,
|
|
@@ -977,7 +973,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
977
973
|
const [rootDid, ...childDids] = dids;
|
|
978
974
|
logger.info('config blocklet', { dids });
|
|
979
975
|
|
|
980
|
-
let blocklet = await this.
|
|
976
|
+
let blocklet = await this.getBlocklet(rootDid);
|
|
981
977
|
for (const childDid of childDids) {
|
|
982
978
|
blocklet = blocklet.children.find((x) => x.meta.did === childDid);
|
|
983
979
|
if (!blocklet) {
|
|
@@ -1035,20 +1031,20 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1035
1031
|
await this.updateBlockletEnvironment(rootDid);
|
|
1036
1032
|
|
|
1037
1033
|
// response
|
|
1038
|
-
const newState = await this.
|
|
1034
|
+
const newState = await this.getBlocklet(rootDid);
|
|
1039
1035
|
this.emit(BlockletEvents.updated, newState);
|
|
1040
1036
|
return newState;
|
|
1041
1037
|
}
|
|
1042
1038
|
|
|
1043
1039
|
async configPublicToStore({ did, publicToStore = false }) {
|
|
1044
|
-
const blocklet = await this.
|
|
1040
|
+
const blocklet = await this.getBlocklet(did);
|
|
1045
1041
|
// publicToStore 由用户传入
|
|
1046
1042
|
// handleInstanceInStore 方法写在前面,保证向 store 操作成功后才会更改 blocklet 中的 publicToStore值
|
|
1047
1043
|
// handleInstanceInStore 中会校验修改 publicToStore字段 的条件,不符合则会抛错,就不会执行下面更新 publicToStore 的逻辑
|
|
1048
1044
|
await handleInstanceInStore(blocklet, { publicToStore });
|
|
1049
1045
|
await states.blockletExtras.setSettings(did, { publicToStore });
|
|
1050
1046
|
|
|
1051
|
-
const newState = await this.
|
|
1047
|
+
const newState = await this.getBlocklet(did);
|
|
1052
1048
|
return newState;
|
|
1053
1049
|
}
|
|
1054
1050
|
|
|
@@ -1058,7 +1054,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1058
1054
|
}
|
|
1059
1055
|
await states.blockletExtras.setSettings(did, { navigations });
|
|
1060
1056
|
|
|
1061
|
-
const newState = await this.
|
|
1057
|
+
const newState = await this.getBlocklet(did);
|
|
1062
1058
|
this.emit(BlockletEvents.updated, newState);
|
|
1063
1059
|
return newState;
|
|
1064
1060
|
}
|
|
@@ -1110,7 +1106,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1110
1106
|
await states.blockletExtras.setConfigs(dids, configs);
|
|
1111
1107
|
}
|
|
1112
1108
|
|
|
1113
|
-
const blocklet = await this.
|
|
1109
|
+
const blocklet = await this.getBlocklet(rootDid);
|
|
1114
1110
|
|
|
1115
1111
|
this.emit(BlockletEvents.updated, { meta: { did: blocklet.meta.did } });
|
|
1116
1112
|
|
|
@@ -1158,7 +1154,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1158
1154
|
// trigger dashboard frontend refresh
|
|
1159
1155
|
this.emit(BlockletEvents.updated, blocklet);
|
|
1160
1156
|
|
|
1161
|
-
return this.
|
|
1157
|
+
return this.getBlocklet(rootDid);
|
|
1162
1158
|
}
|
|
1163
1159
|
|
|
1164
1160
|
async updateComponentMountPoint({ did, rootDid: inputRootDid, mountPoint }, context) {
|
|
@@ -1194,7 +1190,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1194
1190
|
|
|
1195
1191
|
this.emit(BlockletEvents.upgraded, { blocklet, context: { ...context, createAuditLog: false } }); // trigger router refresh
|
|
1196
1192
|
|
|
1197
|
-
return this.
|
|
1193
|
+
return this.getBlocklet(rootDid);
|
|
1198
1194
|
}
|
|
1199
1195
|
|
|
1200
1196
|
/**
|
|
@@ -1540,6 +1536,8 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1540
1536
|
|
|
1541
1537
|
// Add Config
|
|
1542
1538
|
await this._setConfigsFromMeta(did);
|
|
1539
|
+
|
|
1540
|
+
// should ensure blocklet integrity
|
|
1543
1541
|
let blocklet = await this.ensureBlocklet(did);
|
|
1544
1542
|
|
|
1545
1543
|
// pre install
|
|
@@ -1547,14 +1545,14 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1547
1545
|
|
|
1548
1546
|
// Add environments
|
|
1549
1547
|
await this.updateBlockletEnvironment(did);
|
|
1550
|
-
blocklet = await this.
|
|
1548
|
+
blocklet = await this.getBlocklet(did);
|
|
1551
1549
|
|
|
1552
1550
|
// post install
|
|
1553
1551
|
await this._runPostInstallHook(blocklet);
|
|
1554
1552
|
|
|
1555
1553
|
await states.blocklet.setBlockletStatus(did, BlockletStatus.installed);
|
|
1556
1554
|
|
|
1557
|
-
blocklet = await this.
|
|
1555
|
+
blocklet = await this.getBlocklet(did);
|
|
1558
1556
|
|
|
1559
1557
|
await fs.writeFile(path.join(blocklet.env.dataDir, 'logo.svg'), createDidLogo(blocklet.meta.did));
|
|
1560
1558
|
|
|
@@ -1607,6 +1605,8 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1607
1605
|
|
|
1608
1606
|
// Add Config
|
|
1609
1607
|
await this._setConfigsFromMeta(rootDid);
|
|
1608
|
+
|
|
1609
|
+
// should ensure blocklet integrity
|
|
1610
1610
|
let blocklet = await this.ensureBlocklet(rootDid);
|
|
1611
1611
|
|
|
1612
1612
|
// pre install
|
|
@@ -1614,14 +1614,14 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1614
1614
|
|
|
1615
1615
|
// Add environments
|
|
1616
1616
|
await this.updateBlockletEnvironment(rootDid);
|
|
1617
|
-
blocklet = await this.
|
|
1617
|
+
blocklet = await this.getBlocklet(rootDid);
|
|
1618
1618
|
|
|
1619
1619
|
// post install
|
|
1620
1620
|
await this._runPostInstallHook(blocklet);
|
|
1621
1621
|
|
|
1622
1622
|
logger.info('add blocklet component for dev', { did, version, meta });
|
|
1623
1623
|
|
|
1624
|
-
blocklet = await this.
|
|
1624
|
+
blocklet = await this.getBlocklet(rootDid);
|
|
1625
1625
|
|
|
1626
1626
|
return blocklet;
|
|
1627
1627
|
}
|
|
@@ -1645,7 +1645,11 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1645
1645
|
}
|
|
1646
1646
|
|
|
1647
1647
|
async ensureBlocklet(did, opts = {}) {
|
|
1648
|
-
return getBlocklet({ ...opts, states, dataDirs: this.dataDirs, did });
|
|
1648
|
+
return getBlocklet({ ...opts, states, dataDirs: this.dataDirs, did, ensureIntegrity: true });
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
async getBlocklet(did, opts = {}) {
|
|
1652
|
+
return getBlocklet({ ...opts, states, dataDirs: this.dataDirs, did, ensureIntegrity: false });
|
|
1649
1653
|
}
|
|
1650
1654
|
|
|
1651
1655
|
async hasBlocklet({ did }) {
|
|
@@ -1663,7 +1667,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1663
1667
|
|
|
1664
1668
|
this.emit(BlockletEvents.updated, { meta: { did: blocklet.meta.did } });
|
|
1665
1669
|
|
|
1666
|
-
return this.
|
|
1670
|
+
return this.getBlocklet(did);
|
|
1667
1671
|
}
|
|
1668
1672
|
|
|
1669
1673
|
async status(did, { forceSync = false } = {}) {
|
|
@@ -1680,7 +1684,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1680
1684
|
return res;
|
|
1681
1685
|
};
|
|
1682
1686
|
|
|
1683
|
-
const blocklet = await this.
|
|
1687
|
+
const blocklet = await this.getBlocklet(did);
|
|
1684
1688
|
|
|
1685
1689
|
let shouldUpdateStatus = forceSync || shouldUpdateBlockletStatus(blocklet.status);
|
|
1686
1690
|
if (isInProgress(blocklet.status)) {
|
|
@@ -1709,80 +1713,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1709
1713
|
}
|
|
1710
1714
|
}
|
|
1711
1715
|
|
|
1712
|
-
async attachRuntimeInfo({ did, nodeInfo, diskInfo = true, context, cachedBlocklet }) {
|
|
1713
|
-
if (!did) {
|
|
1714
|
-
throw new Error('did should not be empty');
|
|
1715
|
-
}
|
|
1716
|
-
|
|
1717
|
-
try {
|
|
1718
|
-
const blocklet = await this.ensureBlocklet(did, { throwOnNotExist: false });
|
|
1719
|
-
|
|
1720
|
-
if (!blocklet) {
|
|
1721
|
-
return null;
|
|
1722
|
-
}
|
|
1723
|
-
|
|
1724
|
-
const fromCache = !!cachedBlocklet;
|
|
1725
|
-
|
|
1726
|
-
// if from cached data, only use cache data of runtime info (engine, diskInfo, runtimeInfo...)
|
|
1727
|
-
if (fromCache) {
|
|
1728
|
-
const cached = {};
|
|
1729
|
-
forEachBlockletSync(cachedBlocklet, (component, { id }) => {
|
|
1730
|
-
cached[id] = component;
|
|
1731
|
-
});
|
|
1732
|
-
|
|
1733
|
-
Object.assign(blocklet, pick(cachedBlocklet, ['appRuntimeInfo', 'diskInfo']));
|
|
1734
|
-
|
|
1735
|
-
forEachBlockletSync(blocklet, (component, { id }) => {
|
|
1736
|
-
if (cached[id]) {
|
|
1737
|
-
Object.assign(component, pick(cached[id], ['runtimeInfo']));
|
|
1738
|
-
}
|
|
1739
|
-
});
|
|
1740
|
-
}
|
|
1741
|
-
|
|
1742
|
-
// 处理 domainAliases#value SLOT_FOR_IP_DNS_SITE
|
|
1743
|
-
if (blocklet?.site?.domainAliases?.length) {
|
|
1744
|
-
const nodeIp = await getAccessibleExternalNodeIp(nodeInfo);
|
|
1745
|
-
blocklet.site.domainAliases = blocklet.site.domainAliases.map((x) => ({
|
|
1746
|
-
...x,
|
|
1747
|
-
value: util.replaceDomainSlot({ domain: x.value, context, nodeIp }),
|
|
1748
|
-
}));
|
|
1749
|
-
}
|
|
1750
|
-
|
|
1751
|
-
// app runtime info, app status
|
|
1752
|
-
blocklet.appRuntimeInfo = this.runtimeMonitor.getRuntimeInfo(blocklet.meta.did);
|
|
1753
|
-
|
|
1754
|
-
if (!fromCache) {
|
|
1755
|
-
// app disk info, component runtime info, component status, component engine
|
|
1756
|
-
await forEachBlocklet(blocklet, async (component, { level }) => {
|
|
1757
|
-
component.engine = getEngine(getBlockletEngineNameByPlatform(component.meta)).describe();
|
|
1758
|
-
|
|
1759
|
-
if (level === 0) {
|
|
1760
|
-
component.diskInfo = await getDiskInfo(component, {
|
|
1761
|
-
useFakeDiskInfo: !diskInfo,
|
|
1762
|
-
});
|
|
1763
|
-
}
|
|
1764
|
-
|
|
1765
|
-
component.runtimeInfo = this.runtimeMonitor.getRuntimeInfo(blocklet.meta.did, component.env.id);
|
|
1766
|
-
|
|
1767
|
-
if (component.runtimeInfo?.status && shouldUpdateBlockletStatus(component.status)) {
|
|
1768
|
-
component.status = statusMap[component.runtimeInfo.status];
|
|
1769
|
-
}
|
|
1770
|
-
});
|
|
1771
|
-
}
|
|
1772
|
-
|
|
1773
|
-
return blocklet;
|
|
1774
|
-
} catch (err) {
|
|
1775
|
-
const simpleState = await states.blocklet.getBlocklet(did);
|
|
1776
|
-
logger.error('failed to get blocklet info', {
|
|
1777
|
-
did,
|
|
1778
|
-
name: get(simpleState, 'meta.name'),
|
|
1779
|
-
status: get(simpleState, 'status'),
|
|
1780
|
-
error: err,
|
|
1781
|
-
});
|
|
1782
|
-
return simpleState;
|
|
1783
|
-
}
|
|
1784
|
-
}
|
|
1785
|
-
|
|
1786
1716
|
async refreshListCache() {
|
|
1787
1717
|
this.list({ useCache: false }).catch((err) => {
|
|
1788
1718
|
logger.error('refresh blocklet list failed', { error: err });
|
|
@@ -1981,7 +1911,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1981
1911
|
|
|
1982
1912
|
async onCheckIfStarted(jobInfo, { throwOnError } = {}) {
|
|
1983
1913
|
const { did, context, minConsecutiveTime = 5000, timeout } = jobInfo;
|
|
1984
|
-
const blocklet = await this.
|
|
1914
|
+
const blocklet = await this.getBlocklet(did);
|
|
1985
1915
|
|
|
1986
1916
|
const { meta } = blocklet;
|
|
1987
1917
|
const { name } = meta;
|
|
@@ -2022,7 +1952,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2022
1952
|
}
|
|
2023
1953
|
|
|
2024
1954
|
async updateBlockletEnvironment(did) {
|
|
2025
|
-
const blockletWithEnv = await this.
|
|
1955
|
+
const blockletWithEnv = await this.getBlocklet(did);
|
|
2026
1956
|
const blocklet = await states.blocklet.getBlocklet(did);
|
|
2027
1957
|
const nodeInfo = await states.node.read();
|
|
2028
1958
|
|
|
@@ -2650,6 +2580,80 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2650
2580
|
];
|
|
2651
2581
|
}
|
|
2652
2582
|
|
|
2583
|
+
async _attachRuntimeInfo({ did, nodeInfo, diskInfo = true, context, cachedBlocklet }) {
|
|
2584
|
+
if (!did) {
|
|
2585
|
+
throw new Error('did should not be empty');
|
|
2586
|
+
}
|
|
2587
|
+
|
|
2588
|
+
try {
|
|
2589
|
+
const blocklet = await this.getBlocklet(did, { throwOnNotExist: false });
|
|
2590
|
+
|
|
2591
|
+
if (!blocklet) {
|
|
2592
|
+
return null;
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2595
|
+
const fromCache = !!cachedBlocklet;
|
|
2596
|
+
|
|
2597
|
+
// if from cached data, only use cache data of runtime info (engine, diskInfo, runtimeInfo...)
|
|
2598
|
+
if (fromCache) {
|
|
2599
|
+
const cached = {};
|
|
2600
|
+
forEachBlockletSync(cachedBlocklet, (component, { id }) => {
|
|
2601
|
+
cached[id] = component;
|
|
2602
|
+
});
|
|
2603
|
+
|
|
2604
|
+
Object.assign(blocklet, pick(cachedBlocklet, ['appRuntimeInfo', 'diskInfo']));
|
|
2605
|
+
|
|
2606
|
+
forEachBlockletSync(blocklet, (component, { id }) => {
|
|
2607
|
+
if (cached[id]) {
|
|
2608
|
+
Object.assign(component, pick(cached[id], ['runtimeInfo']));
|
|
2609
|
+
}
|
|
2610
|
+
});
|
|
2611
|
+
}
|
|
2612
|
+
|
|
2613
|
+
// 处理 domainAliases#value SLOT_FOR_IP_DNS_SITE
|
|
2614
|
+
if (blocklet?.site?.domainAliases?.length) {
|
|
2615
|
+
const nodeIp = await getAccessibleExternalNodeIp(nodeInfo);
|
|
2616
|
+
blocklet.site.domainAliases = blocklet.site.domainAliases.map((x) => ({
|
|
2617
|
+
...x,
|
|
2618
|
+
value: util.replaceDomainSlot({ domain: x.value, context, nodeIp }),
|
|
2619
|
+
}));
|
|
2620
|
+
}
|
|
2621
|
+
|
|
2622
|
+
// app runtime info, app status
|
|
2623
|
+
blocklet.appRuntimeInfo = this.runtimeMonitor.getRuntimeInfo(blocklet.meta.did);
|
|
2624
|
+
|
|
2625
|
+
if (!fromCache) {
|
|
2626
|
+
// app disk info, component runtime info, component status, component engine
|
|
2627
|
+
await forEachBlocklet(blocklet, async (component, { level }) => {
|
|
2628
|
+
component.engine = getEngine(getBlockletEngineNameByPlatform(component.meta)).describe();
|
|
2629
|
+
|
|
2630
|
+
if (level === 0) {
|
|
2631
|
+
component.diskInfo = await getDiskInfo(component, {
|
|
2632
|
+
useFakeDiskInfo: !diskInfo,
|
|
2633
|
+
});
|
|
2634
|
+
}
|
|
2635
|
+
|
|
2636
|
+
component.runtimeInfo = this.runtimeMonitor.getRuntimeInfo(blocklet.meta.did, component.env.id);
|
|
2637
|
+
|
|
2638
|
+
if (component.runtimeInfo?.status && shouldUpdateBlockletStatus(component.status)) {
|
|
2639
|
+
component.status = statusMap[component.runtimeInfo.status];
|
|
2640
|
+
}
|
|
2641
|
+
});
|
|
2642
|
+
}
|
|
2643
|
+
|
|
2644
|
+
return blocklet;
|
|
2645
|
+
} catch (err) {
|
|
2646
|
+
const simpleState = await states.blocklet.getBlocklet(did);
|
|
2647
|
+
logger.error('failed to get blocklet info', {
|
|
2648
|
+
did,
|
|
2649
|
+
name: get(simpleState, 'meta.name'),
|
|
2650
|
+
status: get(simpleState, 'status'),
|
|
2651
|
+
error: err,
|
|
2652
|
+
});
|
|
2653
|
+
return simpleState;
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
|
|
2653
2657
|
async _syncBlockletStatus() {
|
|
2654
2658
|
const run = async (blocklet) => {
|
|
2655
2659
|
try {
|
|
@@ -2898,6 +2902,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2898
2902
|
*/
|
|
2899
2903
|
async _installBlocklet({ did, oldBlocklet, context }) {
|
|
2900
2904
|
try {
|
|
2905
|
+
// should ensure blocklet integrity
|
|
2901
2906
|
let blocklet = await this.ensureBlocklet(did);
|
|
2902
2907
|
const { meta, source, deployedFrom } = blocklet;
|
|
2903
2908
|
|
|
@@ -2914,13 +2919,13 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2914
2919
|
|
|
2915
2920
|
// Add environments
|
|
2916
2921
|
await this.updateBlockletEnvironment(meta.did);
|
|
2917
|
-
blocklet = await this.
|
|
2922
|
+
blocklet = await this.getBlocklet(did);
|
|
2918
2923
|
|
|
2919
2924
|
// post install
|
|
2920
2925
|
await this._runPostInstallHook(blocklet, context);
|
|
2921
2926
|
|
|
2922
2927
|
await states.blocklet.setBlockletStatus(did, BlockletStatus.installed);
|
|
2923
|
-
blocklet = await this.
|
|
2928
|
+
blocklet = await this.getBlocklet(did);
|
|
2924
2929
|
logger.info('blocklet installed', { source, did: meta.did });
|
|
2925
2930
|
|
|
2926
2931
|
await fs.writeFile(path.join(blocklet.env.dataDir, 'logo.svg'), createDidLogo(blocklet.meta.did));
|
|
@@ -3005,6 +3010,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3005
3010
|
await states.blocklet.upgradeBlocklet({ meta, source, deployedFrom, children });
|
|
3006
3011
|
await this._setConfigsFromMeta(did);
|
|
3007
3012
|
|
|
3013
|
+
// should ensure blocklet integrity
|
|
3008
3014
|
let blocklet = await this.ensureBlocklet(did);
|
|
3009
3015
|
|
|
3010
3016
|
// pre install
|
|
@@ -3012,7 +3018,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3012
3018
|
|
|
3013
3019
|
// Add environments
|
|
3014
3020
|
await this.updateBlockletEnvironment(did);
|
|
3015
|
-
blocklet = await this.
|
|
3021
|
+
blocklet = await this.getBlocklet(did);
|
|
3016
3022
|
|
|
3017
3023
|
// post install
|
|
3018
3024
|
await this._runPostInstallHook(blocklet, context);
|
|
@@ -3052,7 +3058,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3052
3058
|
logger.info('started blocklet for upgrading', { did, version });
|
|
3053
3059
|
}
|
|
3054
3060
|
|
|
3055
|
-
blocklet = await this.
|
|
3061
|
+
blocklet = await this.getBlocklet(did, context);
|
|
3056
3062
|
|
|
3057
3063
|
await fs.writeFile(path.join(blocklet.env.dataDir, 'logo.svg'), createDidLogo(blocklet.meta.did));
|
|
3058
3064
|
|
|
@@ -3283,7 +3289,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3283
3289
|
}
|
|
3284
3290
|
|
|
3285
3291
|
async _setConfigsFromMeta(did, childDid) {
|
|
3286
|
-
const blocklet = await getBlocklet({ states, dataDirs: this.dataDirs, did
|
|
3292
|
+
const blocklet = await getBlocklet({ states, dataDirs: this.dataDirs, did });
|
|
3287
3293
|
|
|
3288
3294
|
if (!childDid) {
|
|
3289
3295
|
await forEachBlocklet(blocklet, async (b, { ancestors }) => {
|
|
@@ -3563,7 +3569,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3563
3569
|
|
|
3564
3570
|
this.emit(BlockletEvents.appDidChanged, blocklet);
|
|
3565
3571
|
|
|
3566
|
-
const blockletWithEnv = await this.
|
|
3572
|
+
const blockletWithEnv = await this.getBlocklet(blocklet.meta.did);
|
|
3567
3573
|
const appSystemEnvironments = getAppOverwrittenEnvironments(blockletWithEnv, nodeInfo);
|
|
3568
3574
|
|
|
3569
3575
|
await didDocument.updateBlockletDocument({
|
|
@@ -71,6 +71,8 @@ module.exports = async ({ url, blockletSecretKey, moveDir, context = {}, states,
|
|
|
71
71
|
const skConfig = extra.configs.find((x) => x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK);
|
|
72
72
|
if (skConfig) {
|
|
73
73
|
skConfig.value = blockletSecretKey;
|
|
74
|
+
skConfig.secure = true;
|
|
75
|
+
skConfig.shared = false;
|
|
74
76
|
} else {
|
|
75
77
|
extra.configs.push({
|
|
76
78
|
key: BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK,
|
package/lib/router/helper.js
CHANGED
|
@@ -21,6 +21,7 @@ const {
|
|
|
21
21
|
DOMAIN_FOR_INTERNAL_SITE,
|
|
22
22
|
WELLKNOWN_PATH_PREFIX,
|
|
23
23
|
WELLKNOWN_SERVICE_PATH_PREFIX,
|
|
24
|
+
USER_AVATAR_PATH_PREFIX,
|
|
24
25
|
DOMAIN_FOR_IP_SITE,
|
|
25
26
|
NAME_FOR_WELLKNOWN_SITE,
|
|
26
27
|
DEFAULT_HTTP_PORT,
|
|
@@ -210,6 +211,9 @@ const ensureLatestInterfaceInfo = async (sites = []) => {
|
|
|
210
211
|
if (rule.isProtected && rule.to.target === WELLKNOWN_SERVICE_PATH_PREFIX) {
|
|
211
212
|
return rule;
|
|
212
213
|
}
|
|
214
|
+
if (rule.isProtected && rule.to.target === joinUrl(WELLKNOWN_SERVICE_PATH_PREFIX, USER_AVATAR_PATH_PREFIX)) {
|
|
215
|
+
return rule;
|
|
216
|
+
}
|
|
213
217
|
|
|
214
218
|
const { did, interfaceName } = rule.to;
|
|
215
219
|
if (interfaces[did] && interfaces[did][interfaceName]) {
|
|
@@ -273,6 +277,18 @@ const ensureWellknownRule = async (sites) => {
|
|
|
273
277
|
if (!site.rules.some((x) => x.from.pathPrefix === servicePathPrefix)) {
|
|
274
278
|
const rule = cloneDeep(rootBlockletRule || blockletRules[0]);
|
|
275
279
|
rule.from.pathPrefix = servicePathPrefix;
|
|
280
|
+
rule.to.target = servicePathPrefix;
|
|
281
|
+
rule.isProtected = true;
|
|
282
|
+
site.rules.push(rule);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Cache user avatar in nginx
|
|
286
|
+
const avatarPathPrefix = joinUrl(servicePathPrefix, USER_AVATAR_PATH_PREFIX);
|
|
287
|
+
if (!site.rules.some((x) => x.from.pathPrefix === avatarPathPrefix)) {
|
|
288
|
+
const rule = cloneDeep(rootBlockletRule || blockletRules[0]);
|
|
289
|
+
rule.from.pathPrefix = avatarPathPrefix;
|
|
290
|
+
rule.to.cacheGroup = 'blockletProxy';
|
|
291
|
+
rule.to.target = avatarPathPrefix;
|
|
276
292
|
rule.isProtected = true;
|
|
277
293
|
site.rules.push(rule);
|
|
278
294
|
}
|
|
@@ -321,7 +337,12 @@ const filterSitesForRemovedBlocklets = async (sites = []) => {
|
|
|
321
337
|
}
|
|
322
338
|
|
|
323
339
|
const did = getDidFromDomainGroupName(site.domain);
|
|
324
|
-
|
|
340
|
+
const blocklet = blocklets.find((x) => x.meta.did === did);
|
|
341
|
+
if (blocklet) {
|
|
342
|
+
site.mode = blocklet.mode;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return !!blocklet;
|
|
325
346
|
});
|
|
326
347
|
};
|
|
327
348
|
|
|
@@ -1125,7 +1146,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
1125
1146
|
// Ensure we have system rules for blocklets
|
|
1126
1147
|
const blocklets = await blockletState.getBlocklets();
|
|
1127
1148
|
const ensureBlocklet = async (x) => {
|
|
1128
|
-
const blocklet = await blockletManager.
|
|
1149
|
+
const blocklet = await blockletManager.getBlocklet(x.meta.did);
|
|
1129
1150
|
return ensureBlockletRouting(blocklet, context);
|
|
1130
1151
|
};
|
|
1131
1152
|
const ensureBlockletResults = await Promise.all(blocklets.map((x) => ensureBlocklet(x)));
|
package/lib/router/index.js
CHANGED
|
@@ -10,7 +10,7 @@ const {
|
|
|
10
10
|
BLOCKLET_SITE_GROUP_SUFFIX,
|
|
11
11
|
GATEWAY_REQ_LIMIT,
|
|
12
12
|
} = require('@abtnode/constant');
|
|
13
|
-
const { BLOCKLET_UI_INTERFACES } = require('@blocklet/constant');
|
|
13
|
+
const { BLOCKLET_UI_INTERFACES, BLOCKLET_MODES } = require('@blocklet/constant');
|
|
14
14
|
const logger = require('@abtnode/logger')('@abtnode/core:router');
|
|
15
15
|
|
|
16
16
|
const expandSites = (sites = []) => {
|
|
@@ -208,6 +208,7 @@ Router.formatSites = (sites = []) => {
|
|
|
208
208
|
port: daemonRule.to.port,
|
|
209
209
|
did: rule.to.did,
|
|
210
210
|
componentId: rule.to.componentId,
|
|
211
|
+
cacheGroup: site.mode === BLOCKLET_MODES.PRODUCTION ? 'blockletJs' : '',
|
|
211
212
|
},
|
|
212
213
|
});
|
|
213
214
|
site.rules.push({
|
|
@@ -221,6 +222,7 @@ Router.formatSites = (sites = []) => {
|
|
|
221
222
|
port: daemonRule.to.port,
|
|
222
223
|
did: rule.to.did,
|
|
223
224
|
componentId: rule.to.componentId,
|
|
225
|
+
cacheGroup: site.mode === BLOCKLET_MODES.PRODUCTION ? 'blockletJs' : '',
|
|
224
226
|
},
|
|
225
227
|
});
|
|
226
228
|
|
|
@@ -233,6 +235,7 @@ Router.formatSites = (sites = []) => {
|
|
|
233
235
|
did: rule.to.did,
|
|
234
236
|
type: ROUTING_RULE_TYPES.DAEMON,
|
|
235
237
|
target: BLOCKLET_PROXY_PATH_PREFIX,
|
|
238
|
+
cacheGroup: site.mode === BLOCKLET_MODES.PRODUCTION ? 'blockletProxy' : '',
|
|
236
239
|
},
|
|
237
240
|
});
|
|
238
241
|
}
|
package/lib/states/blocklet.js
CHANGED
|
@@ -53,6 +53,9 @@ const formatBlocklet = (blocklet, phase, dek) => {
|
|
|
53
53
|
if (!env) {
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
|
+
// salt in blocklet state is different from the salt in blocklet-extra state
|
|
57
|
+
// in blocklet-extra state, salt is app meta did in each component
|
|
58
|
+
// in blocklet state, salt is component meta did in each component
|
|
56
59
|
if (phase === 'onUpdate' && isHex(env.value) === true) {
|
|
57
60
|
env.value = security.encrypt(env.value, b.meta.did, dek);
|
|
58
61
|
}
|
package/lib/util/blocklet.js
CHANGED
|
@@ -142,8 +142,9 @@ const PRIVATE_NODE_ENVS = [
|
|
|
142
142
|
*/
|
|
143
143
|
const getComponentDirs = (
|
|
144
144
|
component,
|
|
145
|
-
{ dataDirs, ensure = false, e2eMode = false, validate =
|
|
145
|
+
{ dataDirs, ensure = false, e2eMode = false, validate = false, ancestors = [] } = {}
|
|
146
146
|
) => {
|
|
147
|
+
// FIXME 这个函数做了太多的事
|
|
147
148
|
// get data dirs
|
|
148
149
|
|
|
149
150
|
const { name: appName } = ancestors.concat(component)[0].meta;
|
|
@@ -1360,9 +1361,8 @@ const getBlocklet = async ({
|
|
|
1360
1361
|
dataDirs,
|
|
1361
1362
|
states,
|
|
1362
1363
|
e2eMode = false,
|
|
1363
|
-
validateEnv = true,
|
|
1364
1364
|
throwOnNotExist = true,
|
|
1365
|
-
|
|
1365
|
+
ensureIntegrity = false,
|
|
1366
1366
|
} = {}) => {
|
|
1367
1367
|
if (!did) {
|
|
1368
1368
|
throw new Error('Blocklet did does not exist');
|
|
@@ -1381,7 +1381,7 @@ const getBlocklet = async ({
|
|
|
1381
1381
|
|
|
1382
1382
|
const blocklet = await states.blocklet.getBlocklet(did);
|
|
1383
1383
|
if (!blocklet) {
|
|
1384
|
-
if (throwOnNotExist) {
|
|
1384
|
+
if (throwOnNotExist || ensureIntegrity) {
|
|
1385
1385
|
throw new Error(`can not find blocklet in database by did ${did}`);
|
|
1386
1386
|
}
|
|
1387
1387
|
return null;
|
|
@@ -1418,8 +1418,8 @@ const getBlocklet = async ({
|
|
|
1418
1418
|
processId: getComponentProcessId(component, ancestors),
|
|
1419
1419
|
...getComponentDirs(component, {
|
|
1420
1420
|
dataDirs,
|
|
1421
|
-
ensure:
|
|
1422
|
-
validate:
|
|
1421
|
+
ensure: ensureIntegrity,
|
|
1422
|
+
validate: ensureIntegrity,
|
|
1423
1423
|
ancestors,
|
|
1424
1424
|
e2eMode: level === 0 ? e2eMode : false,
|
|
1425
1425
|
}),
|
|
@@ -1554,6 +1554,9 @@ const createDataArchive = (dataDir, fileName) => {
|
|
|
1554
1554
|
const validateAppConfig = async (config, blockletDid, states) => {
|
|
1555
1555
|
const x = config;
|
|
1556
1556
|
|
|
1557
|
+
// sk should be force secured while other app prop should not be secured
|
|
1558
|
+
config.secure = x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK;
|
|
1559
|
+
|
|
1557
1560
|
if (x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK) {
|
|
1558
1561
|
if (x.value) {
|
|
1559
1562
|
try {
|
package/lib/validators/router.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable newline-per-chained-call */
|
|
2
2
|
const Joi = require('joi');
|
|
3
|
-
const { DOMAIN_FOR_DEFAULT_SITE, ROUTING_RULE_TYPES } = require('@abtnode/constant');
|
|
3
|
+
const { DOMAIN_FOR_DEFAULT_SITE, ROUTING_RULE_TYPES, ROUTER_CACHE_GROUPS } = require('@abtnode/constant');
|
|
4
4
|
const { getMultipleLangParams } = require('./util');
|
|
5
5
|
|
|
6
6
|
const WILDCARD_DOMAIN_REGEX = /^\*.(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]/;
|
|
@@ -62,6 +62,12 @@ const ruleSchema = {
|
|
|
62
62
|
then: Joi.required(),
|
|
63
63
|
}),
|
|
64
64
|
componentId: Joi.string().label('component id'), // component global id
|
|
65
|
+
// FUTURE: blocklets can register routing rules for provider cache
|
|
66
|
+
cacheGroup: Joi.string()
|
|
67
|
+
.label('cache group')
|
|
68
|
+
.valid(...Object.keys(ROUTER_CACHE_GROUPS))
|
|
69
|
+
.allow('')
|
|
70
|
+
.default(''),
|
|
65
71
|
},
|
|
66
72
|
|
|
67
73
|
// List of services that manipulate the request before the upstream blocklet
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.8.65-beta-
|
|
6
|
+
"version": "1.8.65-beta-f7af64a4",
|
|
7
7
|
"description": "",
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"files": [
|
|
@@ -19,18 +19,18 @@
|
|
|
19
19
|
"author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@abtnode/auth": "1.8.65-beta-
|
|
23
|
-
"@abtnode/certificate-manager": "1.8.65-beta-
|
|
24
|
-
"@abtnode/constant": "1.8.65-beta-
|
|
25
|
-
"@abtnode/cron": "1.8.65-beta-
|
|
26
|
-
"@abtnode/db": "1.8.65-beta-
|
|
27
|
-
"@abtnode/logger": "1.8.65-beta-
|
|
28
|
-
"@abtnode/queue": "1.8.65-beta-
|
|
29
|
-
"@abtnode/rbac": "1.8.65-beta-
|
|
30
|
-
"@abtnode/router-provider": "1.8.65-beta-
|
|
31
|
-
"@abtnode/static-server": "1.8.65-beta-
|
|
32
|
-
"@abtnode/timemachine": "1.8.65-beta-
|
|
33
|
-
"@abtnode/util": "1.8.65-beta-
|
|
22
|
+
"@abtnode/auth": "1.8.65-beta-f7af64a4",
|
|
23
|
+
"@abtnode/certificate-manager": "1.8.65-beta-f7af64a4",
|
|
24
|
+
"@abtnode/constant": "1.8.65-beta-f7af64a4",
|
|
25
|
+
"@abtnode/cron": "1.8.65-beta-f7af64a4",
|
|
26
|
+
"@abtnode/db": "1.8.65-beta-f7af64a4",
|
|
27
|
+
"@abtnode/logger": "1.8.65-beta-f7af64a4",
|
|
28
|
+
"@abtnode/queue": "1.8.65-beta-f7af64a4",
|
|
29
|
+
"@abtnode/rbac": "1.8.65-beta-f7af64a4",
|
|
30
|
+
"@abtnode/router-provider": "1.8.65-beta-f7af64a4",
|
|
31
|
+
"@abtnode/static-server": "1.8.65-beta-f7af64a4",
|
|
32
|
+
"@abtnode/timemachine": "1.8.65-beta-f7af64a4",
|
|
33
|
+
"@abtnode/util": "1.8.65-beta-f7af64a4",
|
|
34
34
|
"@arcblock/did": "1.18.37",
|
|
35
35
|
"@arcblock/did-motif": "^1.1.10",
|
|
36
36
|
"@arcblock/did-util": "1.18.37",
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
"@arcblock/jwt": "^1.18.37",
|
|
39
39
|
"@arcblock/pm2-events": "^0.0.5",
|
|
40
40
|
"@arcblock/vc": "1.18.37",
|
|
41
|
-
"@blocklet/constant": "1.8.65-beta-
|
|
42
|
-
"@blocklet/meta": "1.8.65-beta-
|
|
43
|
-
"@blocklet/sdk": "1.8.65-beta-
|
|
41
|
+
"@blocklet/constant": "1.8.65-beta-f7af64a4",
|
|
42
|
+
"@blocklet/meta": "1.8.65-beta-f7af64a4",
|
|
43
|
+
"@blocklet/sdk": "1.8.65-beta-f7af64a4",
|
|
44
44
|
"@did-space/client": "^0.1.66",
|
|
45
45
|
"@fidm/x509": "^1.2.1",
|
|
46
46
|
"@ocap/mcrypto": "1.18.37",
|
|
@@ -88,5 +88,5 @@
|
|
|
88
88
|
"express": "^4.18.2",
|
|
89
89
|
"jest": "^27.5.1"
|
|
90
90
|
},
|
|
91
|
-
"gitHead": "
|
|
91
|
+
"gitHead": "97607d6e12bf8508ac29ab6546110c4ae3b31a82"
|
|
92
92
|
}
|