@abtnode/core 1.16.13 → 1.16.14-beta-0c29907f
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 +5 -3
- package/lib/blocklet/manager/disk.js +301 -126
- package/lib/blocklet/manager/helper/install-application-from-backup.js +7 -6
- package/lib/blocklet/manager/helper/install-application-from-general.js +5 -5
- package/lib/blocklet/manager/helper/install-component-from-upload.js +3 -1
- package/lib/blocklet/manager/helper/install-component-from-url.js +22 -5
- package/lib/blocklet/manager/helper/upgrade-components.js +21 -6
- package/lib/event.js +32 -32
- package/lib/index.js +4 -0
- package/lib/states/audit-log.js +64 -70
- package/lib/states/blocklet.js +1 -0
- package/lib/states/notification.js +2 -2
- package/lib/util/blocklet.js +11 -0
- package/lib/util/get-accessible-external-node-ip.js +2 -2
- package/lib/util/get-domain-for-blocklet.js +2 -2
- package/lib/util/launcher.js +11 -5
- package/lib/util/store.js +7 -7
- package/lib/webhook/index.js +25 -14
- package/package.json +32 -32
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable max-classes-per-file */
|
|
1
2
|
/* eslint-disable no-underscore-dangle */
|
|
2
3
|
/* eslint-disable no-await-in-loop */
|
|
3
4
|
const fs = require('fs-extra');
|
|
@@ -25,6 +26,7 @@ const {
|
|
|
25
26
|
APP_STRUCT_VERSION,
|
|
26
27
|
BLOCKLET_CACHE_TTL,
|
|
27
28
|
MONITOR_RECORD_INTERVAL_SEC,
|
|
29
|
+
INSTALL_ACTIONS,
|
|
28
30
|
} = require('@abtnode/constant');
|
|
29
31
|
|
|
30
32
|
const getBlockletEngine = require('@blocklet/meta/lib/engine');
|
|
@@ -45,7 +47,6 @@ const {
|
|
|
45
47
|
forEachComponentV2Sync,
|
|
46
48
|
findComponentByIdV2,
|
|
47
49
|
} = require('@blocklet/meta/lib/util');
|
|
48
|
-
const getComponentProcessId = require('@blocklet/meta/lib/get-component-process-id');
|
|
49
50
|
const { update: updateMetaFile } = require('@blocklet/meta/lib/file');
|
|
50
51
|
const { titleSchema, updateMountPointSchema, environmentNameSchema } = require('@blocklet/meta/lib/schema');
|
|
51
52
|
const { emailConfigSchema } = require('@blocklet/sdk/lib/validators/email');
|
|
@@ -76,6 +77,12 @@ const {
|
|
|
76
77
|
SUSPENDED_REASON,
|
|
77
78
|
} = require('@blocklet/constant');
|
|
78
79
|
const isUndefined = require('lodash/isUndefined');
|
|
80
|
+
const { WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
|
|
81
|
+
const { signV2 } = require('@arcblock/jwt');
|
|
82
|
+
const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
|
|
83
|
+
const pLimit = require('p-limit');
|
|
84
|
+
const pRetry = require('p-retry');
|
|
85
|
+
|
|
79
86
|
const { consumeServerlessNFT, consumeLauncherSession } = require('../../util/launcher');
|
|
80
87
|
const util = require('../../util');
|
|
81
88
|
const {
|
|
@@ -118,7 +125,9 @@ const {
|
|
|
118
125
|
shouldSkipComponent,
|
|
119
126
|
exceedRedemptionPeriod,
|
|
120
127
|
ensureAppPortsNotOccupied,
|
|
128
|
+
getComponentNamesWithVersion,
|
|
121
129
|
} = require('../../util/blocklet');
|
|
130
|
+
const { getDidDomainForBlocklet } = require('../../util/get-domain-for-blocklet');
|
|
122
131
|
const states = require('../../states');
|
|
123
132
|
const BaseBlockletManager = require('./base');
|
|
124
133
|
const { get: getEngine } = require('./engine');
|
|
@@ -147,9 +156,12 @@ const { getBackupFilesUrlFromEndpoint, getBackupEndpoint, getSpaceNameByEndpoint
|
|
|
147
156
|
const { validateAddSpaceGateway, validateUpdateSpaceGateway } = require('../../validators/space-gateway');
|
|
148
157
|
const { sessionConfigSchema } = require('../../validators/util');
|
|
149
158
|
|
|
159
|
+
const request = require('../../util/request');
|
|
160
|
+
|
|
150
161
|
const { formatEnvironments, getBlockletMeta, validateOwner } = util;
|
|
151
162
|
|
|
152
163
|
const statusLock = new Lock('blocklet-status-lock');
|
|
164
|
+
const limitSync = pLimit(1);
|
|
153
165
|
|
|
154
166
|
const getHooksOutputFiles = (blocklet) => ({
|
|
155
167
|
output: path.join(blocklet.env.logsDir, 'output.log'),
|
|
@@ -168,57 +180,7 @@ const pm2StatusMap = {
|
|
|
168
180
|
*/
|
|
169
181
|
const getBlockletEngineNameByPlatform = (blockletMeta) => getBlockletEngine(blockletMeta).interpreter;
|
|
170
182
|
|
|
171
|
-
|
|
172
|
-
* @param {{
|
|
173
|
-
* newBlocklet,
|
|
174
|
-
* oldBlocklet,
|
|
175
|
-
* context: { forceStartProcessIds?: string[] },
|
|
176
|
-
* }}
|
|
177
|
-
* @returns {{ installedComponentNames: string[], skippedProcessIds: string[] }}
|
|
178
|
-
*/
|
|
179
|
-
const getComponentChangedInfoForUpgrade = ({ newBlocklet, oldBlocklet, context = {} }) => {
|
|
180
|
-
const { forceStartProcessIds = [] } = context;
|
|
181
|
-
const idMap = {};
|
|
182
|
-
const skippedProcessIds = [];
|
|
183
|
-
const installedComponentNames = [];
|
|
184
|
-
|
|
185
|
-
forEachBlockletSync(oldBlocklet, (b, { ancestors }) => {
|
|
186
|
-
if (b.meta.dist?.integrity) {
|
|
187
|
-
idMap[getComponentProcessId(b, ancestors)] = b.meta.dist?.integrity;
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
forEachBlockletSync(newBlocklet, (b, { ancestors }) => {
|
|
192
|
-
const id = getComponentProcessId(b, ancestors);
|
|
193
|
-
if (forceStartProcessIds.includes(id)) {
|
|
194
|
-
installedComponentNames.push(b.meta.title);
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (!b.meta.dist?.integrity || b.meta.dist.integrity === idMap[id]) {
|
|
199
|
-
skippedProcessIds.push(id);
|
|
200
|
-
} else {
|
|
201
|
-
installedComponentNames.push(b.meta.title);
|
|
202
|
-
}
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
return { skippedProcessIds, installedComponentNames };
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
class BlockletManager extends BaseBlockletManager {
|
|
209
|
-
/**
|
|
210
|
-
* Creates an instance of BlockletManager.
|
|
211
|
-
* @param {{
|
|
212
|
-
* dataDirs: ReturnType<typeof import('../../util/index').getDataDirs>,
|
|
213
|
-
* startQueue: ReturnType<typeof import('../../util/queue')>,
|
|
214
|
-
* installQueue: ReturnType<typeof import('../../util/queue')>,
|
|
215
|
-
* backupQueue: ReturnType<typeof import('../../util/queue')>,
|
|
216
|
-
* restoreQueue: ReturnType<typeof import('../../util/queue')>,
|
|
217
|
-
* daemon: boolean
|
|
218
|
-
* teamManager: import('../../team/manager.js')
|
|
219
|
-
* }} { dataDirs, startQueue, installQueue, backupQueue, restoreQueue, daemon = false, teamManager }
|
|
220
|
-
* @memberof BlockletManager
|
|
221
|
-
*/
|
|
183
|
+
class DiskBlockletManager extends BaseBlockletManager {
|
|
222
184
|
constructor({ dataDirs, startQueue, installQueue, backupQueue, restoreQueue, daemon = false, teamManager }) {
|
|
223
185
|
super();
|
|
224
186
|
|
|
@@ -469,7 +431,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
469
431
|
const componentDids = inputComponentDids?.length ? inputComponentDids : blocklet.children.map((x) => x.meta.did);
|
|
470
432
|
|
|
471
433
|
const tasks = componentDids.map((componentDid) =>
|
|
472
|
-
this._start({
|
|
434
|
+
this._start({ blocklet, throwOnError, checkHealthImmediately, e2eMode, componentDids: [componentDid] }, context)
|
|
473
435
|
);
|
|
474
436
|
|
|
475
437
|
return Promise.any(tasks).catch((err) => {
|
|
@@ -485,6 +447,8 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
485
447
|
// should check blocklet integrity
|
|
486
448
|
const blocklet1 = inputBlocklet || (await this.ensureBlocklet(did, { e2eMode }));
|
|
487
449
|
|
|
450
|
+
did = blocklet1.meta.did; // eslint-disable-line no-param-reassign
|
|
451
|
+
|
|
488
452
|
await this.checkControllerStatus(blocklet1, 'start');
|
|
489
453
|
|
|
490
454
|
// validate requirement and engine
|
|
@@ -507,7 +471,9 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
507
471
|
// blocklet may be manually stopped durning starting
|
|
508
472
|
// so error message would not be sent if blocklet is stopped
|
|
509
473
|
// so we need update status first
|
|
510
|
-
const doc1 = await states.blocklet.setBlockletStatus(did, BlockletStatus.starting, {
|
|
474
|
+
const doc1 = await states.blocklet.setBlockletStatus(did, BlockletStatus.starting, {
|
|
475
|
+
componentDids,
|
|
476
|
+
});
|
|
511
477
|
blocklet1.status = BlockletStatus.starting;
|
|
512
478
|
this.emit(BlockletEvents.statusChange, doc1);
|
|
513
479
|
|
|
@@ -579,9 +545,11 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
579
545
|
|
|
580
546
|
const error = Array.isArray(err) ? err[0] : err;
|
|
581
547
|
logger.error('Failed to start blocklet', { error, did, title: blocklet1.meta.title });
|
|
582
|
-
const description =
|
|
548
|
+
const description = `${getComponentNamesWithVersion(blocklet1, componentDids)} start failed for ${
|
|
549
|
+
blocklet1.meta.title
|
|
550
|
+
}: ${error.message}`;
|
|
583
551
|
this._createNotification(did, {
|
|
584
|
-
title: '
|
|
552
|
+
title: '',
|
|
585
553
|
description,
|
|
586
554
|
entityType: 'blocklet',
|
|
587
555
|
entityId: did,
|
|
@@ -590,7 +558,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
590
558
|
|
|
591
559
|
await this.deleteProcess({ did, componentDids });
|
|
592
560
|
const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.error, { componentDids });
|
|
593
|
-
this.emit(BlockletEvents.startFailed, { ...res, error: { message: error.message } });
|
|
561
|
+
this.emit(BlockletEvents.startFailed, { ...res, componentDids, error: { message: error.message } });
|
|
594
562
|
this.emit(BlockletEvents.statusChange, { ...res, error: { message: error.message } });
|
|
595
563
|
|
|
596
564
|
if (throwOnError) {
|
|
@@ -647,7 +615,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
647
615
|
this.emit(BlockletEvents.statusChange, res);
|
|
648
616
|
|
|
649
617
|
// send notification to wallet
|
|
650
|
-
this.emit(BlockletEvents.stopped, res);
|
|
618
|
+
this.emit(BlockletEvents.stopped, { ...res, componentDids });
|
|
651
619
|
|
|
652
620
|
this.emit(BlockletInternalEvents.componentsUpdated, {
|
|
653
621
|
appDid: blocklet.appDid,
|
|
@@ -739,9 +707,12 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
739
707
|
const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, { componentDids });
|
|
740
708
|
this.emit(BlockletEvents.statusChange, state);
|
|
741
709
|
|
|
710
|
+
const description = `${getComponentNamesWithVersion(result, componentDids)} restart failed for ${
|
|
711
|
+
result.meta.title
|
|
712
|
+
}: ${err.message || 'queue exception'}`;
|
|
742
713
|
this._createNotification(did, {
|
|
743
|
-
title: '
|
|
744
|
-
description
|
|
714
|
+
title: '',
|
|
715
|
+
description,
|
|
745
716
|
entityType: 'blocklet',
|
|
746
717
|
entityId: did,
|
|
747
718
|
severity: 'error',
|
|
@@ -941,23 +912,33 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
941
912
|
|
|
942
913
|
await this._updateDependents(rootDid);
|
|
943
914
|
|
|
915
|
+
// support edge case
|
|
916
|
+
if (newBlocklet.children.length === 0) {
|
|
917
|
+
await states.blocklet.setBlockletStatus(newBlocklet.meta.did, BlockletStatus.stopped);
|
|
918
|
+
}
|
|
919
|
+
|
|
944
920
|
this.emit(BlockletEvents.upgraded, { blocklet: newBlocklet, context: { ...context, createAuditLog: false } }); // trigger router refresh
|
|
945
921
|
|
|
946
922
|
this._createNotification(newBlocklet.meta.did, {
|
|
947
|
-
title: '
|
|
948
|
-
description:
|
|
923
|
+
title: '',
|
|
924
|
+
description: `${child.meta.title} is successfully deleted for ${newBlocklet.meta.title}.`,
|
|
949
925
|
entityType: 'blocklet',
|
|
950
926
|
entityId: newBlocklet.meta.did,
|
|
951
927
|
severity: 'success',
|
|
952
928
|
action: `/blocklets/${newBlocklet.meta.did}/components`,
|
|
953
929
|
});
|
|
954
930
|
|
|
931
|
+
this.emit(BlockletEvents.componentRemoved, {
|
|
932
|
+
...blocklet,
|
|
933
|
+
componentDids: [child.meta.did],
|
|
934
|
+
});
|
|
935
|
+
|
|
955
936
|
this.emit(BlockletInternalEvents.componentsUpdated, {
|
|
956
937
|
appDid: blocklet.appDid,
|
|
957
938
|
components: getComponentsInternalInfo(newBlocklet),
|
|
958
939
|
});
|
|
959
940
|
|
|
960
|
-
return newBlocklet;
|
|
941
|
+
return { ...newBlocklet, deletedComponent: child };
|
|
961
942
|
}
|
|
962
943
|
|
|
963
944
|
async cancelDownload({ did: inputDid }) {
|
|
@@ -1346,6 +1327,8 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1346
1327
|
return newState;
|
|
1347
1328
|
}
|
|
1348
1329
|
|
|
1330
|
+
// -------------------------------------
|
|
1331
|
+
|
|
1349
1332
|
// TODO: this method can be removed if title is not changed anymore
|
|
1350
1333
|
async updateComponentTitle({ did, rootDid: inputRootDid, title }) {
|
|
1351
1334
|
await titleSchema.validateAsync(title);
|
|
@@ -1784,7 +1767,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1784
1767
|
* @param {{
|
|
1785
1768
|
* blocklet: {},
|
|
1786
1769
|
* context: {},
|
|
1787
|
-
* postAction: 'install' | '
|
|
1770
|
+
* postAction: 'install' | 'upgradeComponent' | 'installComponent',
|
|
1788
1771
|
* oldBlocklet: {},
|
|
1789
1772
|
* throwOnError: Error,
|
|
1790
1773
|
* skipCheckStatusBeforeDownload: boolean,
|
|
@@ -1804,6 +1787,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1804
1787
|
componentDids,
|
|
1805
1788
|
skipCheckIntegrity,
|
|
1806
1789
|
} = params;
|
|
1790
|
+
logger.info('_downloadAndInstall', { did: blocklet?.meta?.did });
|
|
1807
1791
|
const { meta } = blocklet;
|
|
1808
1792
|
const { title, name, did, version } = meta;
|
|
1809
1793
|
|
|
@@ -1834,18 +1818,19 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1834
1818
|
}
|
|
1835
1819
|
|
|
1836
1820
|
// download bundle
|
|
1821
|
+
const childrenToDownload = (blocklet.children || []).filter((x) => {
|
|
1822
|
+
if (componentDids?.length) {
|
|
1823
|
+
return componentDids.includes(x.meta.did);
|
|
1824
|
+
}
|
|
1825
|
+
return x;
|
|
1826
|
+
});
|
|
1827
|
+
|
|
1837
1828
|
try {
|
|
1838
|
-
const blockletForDownload = {
|
|
1839
|
-
...blocklet,
|
|
1840
|
-
children: (blocklet.children || []).filter((x) => {
|
|
1841
|
-
if (componentDids?.length) {
|
|
1842
|
-
return componentDids.includes(x.meta.did);
|
|
1843
|
-
}
|
|
1844
|
-
return x;
|
|
1845
|
-
}),
|
|
1846
|
-
};
|
|
1847
1829
|
const { isCancelled } = await this._downloadBlocklet(
|
|
1848
|
-
|
|
1830
|
+
{
|
|
1831
|
+
...blocklet,
|
|
1832
|
+
children: childrenToDownload,
|
|
1833
|
+
},
|
|
1849
1834
|
Object.assign({}, context, { skipCheckIntegrity })
|
|
1850
1835
|
);
|
|
1851
1836
|
|
|
@@ -1875,8 +1860,10 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1875
1860
|
},
|
|
1876
1861
|
});
|
|
1877
1862
|
this._createNotification(did, {
|
|
1878
|
-
title: '
|
|
1879
|
-
description:
|
|
1863
|
+
title: '',
|
|
1864
|
+
description: `${childrenToDownload
|
|
1865
|
+
.map((x) => `${x.meta.title}@${x.meta.version}`)
|
|
1866
|
+
.join(', ')} download failed for ${title}: ${err.message}`,
|
|
1880
1867
|
entityType: 'blocklet',
|
|
1881
1868
|
entityId: did,
|
|
1882
1869
|
severity: 'error',
|
|
@@ -1909,12 +1896,18 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1909
1896
|
throw new Error('blocklet status changed durning download');
|
|
1910
1897
|
}
|
|
1911
1898
|
|
|
1912
|
-
if (postAction ===
|
|
1899
|
+
if (postAction === INSTALL_ACTIONS.INSTALL) {
|
|
1913
1900
|
const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.installing, { componentDids });
|
|
1914
1901
|
this.emit(BlockletEvents.statusChange, state);
|
|
1915
1902
|
}
|
|
1916
1903
|
|
|
1917
|
-
if (
|
|
1904
|
+
if (
|
|
1905
|
+
[
|
|
1906
|
+
INSTALL_ACTIONS.INSTALL_COMPONENT,
|
|
1907
|
+
INSTALL_ACTIONS.UPGRADE_COMPONENT,
|
|
1908
|
+
'upgrade', // for backward compatibility
|
|
1909
|
+
].includes(postAction)
|
|
1910
|
+
) {
|
|
1918
1911
|
const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.upgrading, { componentDids });
|
|
1919
1912
|
this.emit(BlockletEvents.statusChange, state);
|
|
1920
1913
|
}
|
|
@@ -1928,14 +1921,32 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1928
1921
|
// install
|
|
1929
1922
|
try {
|
|
1930
1923
|
// install blocklet
|
|
1931
|
-
if (postAction ===
|
|
1924
|
+
if (postAction === INSTALL_ACTIONS.INSTALL) {
|
|
1932
1925
|
await this._onInstall({ blocklet, componentDids, context, oldBlocklet });
|
|
1933
1926
|
return;
|
|
1934
1927
|
}
|
|
1935
1928
|
|
|
1936
1929
|
// upgrade blocklet
|
|
1937
|
-
if (
|
|
1938
|
-
|
|
1930
|
+
if (
|
|
1931
|
+
[
|
|
1932
|
+
INSTALL_ACTIONS.INSTALL_COMPONENT,
|
|
1933
|
+
INSTALL_ACTIONS.UPGRADE_COMPONENT,
|
|
1934
|
+
'upgrade', // for backward compatibility
|
|
1935
|
+
].includes(postAction)
|
|
1936
|
+
) {
|
|
1937
|
+
logger.info('do upgrade blocklet', { did, version, postAction, componentDids });
|
|
1938
|
+
|
|
1939
|
+
try {
|
|
1940
|
+
await this._upgradeBlocklet({
|
|
1941
|
+
newBlocklet: blocklet,
|
|
1942
|
+
oldBlocklet,
|
|
1943
|
+
componentDids,
|
|
1944
|
+
context,
|
|
1945
|
+
action: postAction,
|
|
1946
|
+
});
|
|
1947
|
+
} catch (err) {
|
|
1948
|
+
logger.error('blocklet onUpgrade error', { error: err });
|
|
1949
|
+
}
|
|
1939
1950
|
}
|
|
1940
1951
|
} catch (error) {
|
|
1941
1952
|
if (throwOnError) {
|
|
@@ -1973,22 +1984,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1973
1984
|
}
|
|
1974
1985
|
}
|
|
1975
1986
|
|
|
1976
|
-
async _onUpgrade({ oldBlocklet, newBlocklet, componentDids, context }) {
|
|
1977
|
-
const { version, did } = newBlocklet.meta;
|
|
1978
|
-
logger.info('do upgrade blocklet', { did, version });
|
|
1979
|
-
|
|
1980
|
-
try {
|
|
1981
|
-
await this._upgradeBlocklet({
|
|
1982
|
-
newBlocklet,
|
|
1983
|
-
oldBlocklet,
|
|
1984
|
-
componentDids,
|
|
1985
|
-
context,
|
|
1986
|
-
});
|
|
1987
|
-
} catch (err) {
|
|
1988
|
-
logger.error('blocklet onUpgrade error', { error: err });
|
|
1989
|
-
}
|
|
1990
|
-
}
|
|
1991
|
-
|
|
1992
1987
|
async _onRestart({ did, componentDids, context }) {
|
|
1993
1988
|
await this.stop({ did, componentDids, updateStatus: false }, context);
|
|
1994
1989
|
await this.start({ did, componentDids }, context);
|
|
@@ -2017,7 +2012,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2017
2012
|
});
|
|
2018
2013
|
|
|
2019
2014
|
this.emit(BlockletEvents.statusChange, res);
|
|
2020
|
-
this.emit(BlockletEvents.started, res);
|
|
2015
|
+
this.emit(BlockletEvents.started, { ...res, componentDids });
|
|
2021
2016
|
logger.info('blocklet healthy', { did, name, time: Date.now() - startedAt });
|
|
2022
2017
|
} catch (error) {
|
|
2023
2018
|
const status = await states.blocklet.getBlockletStatus(did);
|
|
@@ -2031,15 +2026,19 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2031
2026
|
await this.deleteProcess({ did, componentDids }, context);
|
|
2032
2027
|
const doc = await states.blocklet.setBlockletStatus(did, BlockletStatus.error, { componentDids });
|
|
2033
2028
|
|
|
2029
|
+
const description = `${getComponentNamesWithVersion(blocklet, componentDids)} start failed for ${title}: ${
|
|
2030
|
+
error.message
|
|
2031
|
+
}`;
|
|
2032
|
+
|
|
2034
2033
|
this._createNotification(did, {
|
|
2035
|
-
title: '
|
|
2036
|
-
description
|
|
2034
|
+
title: '',
|
|
2035
|
+
description,
|
|
2037
2036
|
entityType: 'blocklet',
|
|
2038
2037
|
entityId: did,
|
|
2039
2038
|
severity: 'error',
|
|
2040
2039
|
});
|
|
2041
2040
|
|
|
2042
|
-
this.emit(BlockletEvents.startFailed, {
|
|
2041
|
+
this.emit(BlockletEvents.startFailed, { ...doc, componentDids, error: { message: error.message } });
|
|
2043
2042
|
this.emit(BlockletEvents.statusChange, { ...doc, error });
|
|
2044
2043
|
|
|
2045
2044
|
if (throwOnError) {
|
|
@@ -2515,7 +2514,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2515
2514
|
const { name, version } = meta;
|
|
2516
2515
|
logger.error('failed to install blocklet', { name, did, version, error: err });
|
|
2517
2516
|
try {
|
|
2518
|
-
await this._rollback(
|
|
2517
|
+
await this._rollback(INSTALL_ACTIONS.INSTALL, did, oldBlocklet);
|
|
2519
2518
|
this.emit(BlockletEvents.installFailed, {
|
|
2520
2519
|
meta: { did },
|
|
2521
2520
|
error: {
|
|
@@ -2537,18 +2536,10 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2537
2536
|
}
|
|
2538
2537
|
}
|
|
2539
2538
|
|
|
2540
|
-
async _upgradeBlocklet({ newBlocklet, oldBlocklet, componentDids, context = {} }) {
|
|
2539
|
+
async _upgradeBlocklet({ newBlocklet, oldBlocklet, componentDids, action, context = {} }) {
|
|
2541
2540
|
const { meta, source, deployedFrom, children } = newBlocklet;
|
|
2542
2541
|
const { did, version, name, title } = meta;
|
|
2543
2542
|
|
|
2544
|
-
// ids
|
|
2545
|
-
const { skippedProcessIds, installedComponentNames } = getComponentChangedInfoForUpgrade({
|
|
2546
|
-
newBlocklet,
|
|
2547
|
-
oldBlocklet,
|
|
2548
|
-
context,
|
|
2549
|
-
});
|
|
2550
|
-
context.skippedProcessIds = skippedProcessIds;
|
|
2551
|
-
|
|
2552
2543
|
try {
|
|
2553
2544
|
// delete old process
|
|
2554
2545
|
try {
|
|
@@ -2605,7 +2596,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2605
2596
|
if (oldBlocklet.status === BlockletStatus.running) {
|
|
2606
2597
|
// start new process
|
|
2607
2598
|
await this.start({ did, componentDids }, context);
|
|
2608
|
-
logger.info('started blocklet for upgrading', { did, version });
|
|
2599
|
+
logger.info('started blocklet for upgrading', { did, version, componentDids });
|
|
2609
2600
|
} else {
|
|
2610
2601
|
const status =
|
|
2611
2602
|
oldBlocklet.status === BlockletStatus.installed ? BlockletStatus.installed : BlockletStatus.stopped;
|
|
@@ -2621,10 +2612,22 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2621
2612
|
this.refreshListCache();
|
|
2622
2613
|
|
|
2623
2614
|
try {
|
|
2624
|
-
this.emit(BlockletEvents.upgraded, { blocklet, context });
|
|
2615
|
+
this.emit(BlockletEvents.upgraded, { blocklet, context }); // trigger router refresh
|
|
2616
|
+
|
|
2617
|
+
const notificationEvent =
|
|
2618
|
+
action === INSTALL_ACTIONS.INSTALL_COMPONENT
|
|
2619
|
+
? BlockletEvents.componentInstalled
|
|
2620
|
+
: BlockletEvents.componentUpgraded;
|
|
2621
|
+
const actionName = action === INSTALL_ACTIONS.INSTALL_COMPONENT ? 'installed' : 'upgraded';
|
|
2622
|
+
|
|
2623
|
+
this.emit(notificationEvent, { ...blocklet, componentDids, context });
|
|
2624
|
+
|
|
2625
2625
|
this._createNotification(did, {
|
|
2626
|
-
title: '
|
|
2627
|
-
description:
|
|
2626
|
+
title: '',
|
|
2627
|
+
description: `${getComponentNamesWithVersion(
|
|
2628
|
+
newBlocklet,
|
|
2629
|
+
componentDids
|
|
2630
|
+
)} is ${actionName} successfully for ${title}`,
|
|
2628
2631
|
action: `/blocklets/${did}/overview`,
|
|
2629
2632
|
entityType: 'blocklet',
|
|
2630
2633
|
entityId: did,
|
|
@@ -2646,21 +2649,27 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2646
2649
|
|
|
2647
2650
|
return blocklet;
|
|
2648
2651
|
} catch (err) {
|
|
2649
|
-
const b = await this._rollback(
|
|
2652
|
+
const b = await this._rollback(action, did, oldBlocklet);
|
|
2650
2653
|
logger.error('failed to upgrade blocklet', { did, version, name, error: err });
|
|
2651
2654
|
|
|
2652
2655
|
this.emit(BlockletEvents.updated, b);
|
|
2653
2656
|
|
|
2654
|
-
|
|
2655
|
-
|
|
2657
|
+
const actionName = action === INSTALL_ACTIONS.INSTALL_COMPONENT ? 'install' : 'upgrade';
|
|
2658
|
+
const notificationEvent =
|
|
2659
|
+
action === INSTALL_ACTIONS.INSTALL_COMPONENT
|
|
2660
|
+
? BlockletEvents.componentInstallFailed
|
|
2661
|
+
: BlockletEvents.componentUpgradeFailed;
|
|
2662
|
+
|
|
2663
|
+
this.emit(notificationEvent, {
|
|
2664
|
+
blocklet: { ...oldBlocklet, componentDids, error: { message: err.message } },
|
|
2656
2665
|
context,
|
|
2657
2666
|
});
|
|
2658
2667
|
|
|
2659
2668
|
this._createNotification(did, {
|
|
2660
|
-
title: '
|
|
2661
|
-
description:
|
|
2669
|
+
title: '',
|
|
2670
|
+
description: `${getComponentNamesWithVersion(newBlocklet, componentDids)} ${actionName} failed for ${title}: ${
|
|
2662
2671
|
err.message
|
|
2663
|
-
}
|
|
2672
|
+
}.`,
|
|
2664
2673
|
entityType: 'blocklet',
|
|
2665
2674
|
entityId: did,
|
|
2666
2675
|
severity: 'error',
|
|
@@ -2734,12 +2743,12 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2734
2743
|
}
|
|
2735
2744
|
|
|
2736
2745
|
/**
|
|
2737
|
-
* @param {string} action install, upgrade,
|
|
2746
|
+
* @param {string} action install, upgrade, installComponent, upgradeComponent
|
|
2738
2747
|
* @param {string} did
|
|
2739
2748
|
* @param {object} oldBlocklet
|
|
2740
2749
|
*/
|
|
2741
2750
|
async _rollback(action, did, oldBlocklet) {
|
|
2742
|
-
if (action ===
|
|
2751
|
+
if (action === INSTALL_ACTIONS.INSTALL) {
|
|
2743
2752
|
const extraState = oldBlocklet?.extraState;
|
|
2744
2753
|
|
|
2745
2754
|
// rollback blocklet extra state
|
|
@@ -2753,7 +2762,13 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2753
2762
|
return this._deleteBlocklet({ did, keepData: true });
|
|
2754
2763
|
}
|
|
2755
2764
|
|
|
2756
|
-
if (
|
|
2765
|
+
if (
|
|
2766
|
+
[
|
|
2767
|
+
INSTALL_ACTIONS.INSTALL_COMPONENT,
|
|
2768
|
+
INSTALL_ACTIONS.UPGRADE_COMPONENT,
|
|
2769
|
+
'upgrade', // for backward compatibility
|
|
2770
|
+
].includes(action)
|
|
2771
|
+
) {
|
|
2757
2772
|
const { extraState, ...blocklet } = oldBlocklet;
|
|
2758
2773
|
// rollback blocklet state
|
|
2759
2774
|
const result = await states.blocklet.updateBlocklet(did, blocklet);
|
|
@@ -2944,7 +2959,19 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2944
2959
|
return;
|
|
2945
2960
|
}
|
|
2946
2961
|
|
|
2947
|
-
|
|
2962
|
+
let blockletUrl;
|
|
2963
|
+
try {
|
|
2964
|
+
const blocklet = await this.getBlocklet(did);
|
|
2965
|
+
if (blocklet) {
|
|
2966
|
+
const urls = blocklet.site?.domainAliases || [];
|
|
2967
|
+
const customUrl = urls.find((x) => !x.isProtected)?.value;
|
|
2968
|
+
blockletUrl = `http://${customUrl || getDidDomainForBlocklet({ appPid: blocklet.appPid })}`;
|
|
2969
|
+
}
|
|
2970
|
+
} catch (error) {
|
|
2971
|
+
logger.error('[_createNotification] get blocklet url failed', { error });
|
|
2972
|
+
}
|
|
2973
|
+
|
|
2974
|
+
await states.notification.create({ ...notification, blockletUrl });
|
|
2948
2975
|
} catch (error) {
|
|
2949
2976
|
logger.error('create notification failed', { error });
|
|
2950
2977
|
}
|
|
@@ -3258,7 +3285,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3258
3285
|
);
|
|
3259
3286
|
}
|
|
3260
3287
|
|
|
3261
|
-
async _restoreFromDisk(input) {
|
|
3288
|
+
async _restoreFromDisk(input, context) {
|
|
3262
3289
|
if (input.delay) {
|
|
3263
3290
|
await sleep(input.delay);
|
|
3264
3291
|
}
|
|
@@ -3282,6 +3309,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3282
3309
|
states,
|
|
3283
3310
|
move: true,
|
|
3284
3311
|
sync: false, // use queue to download and install application
|
|
3312
|
+
context,
|
|
3285
3313
|
});
|
|
3286
3314
|
removeRestoreDir();
|
|
3287
3315
|
} catch (error) {
|
|
@@ -3365,4 +3393,151 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3365
3393
|
}
|
|
3366
3394
|
}
|
|
3367
3395
|
|
|
3396
|
+
class FederatedBlockletManager extends DiskBlockletManager {
|
|
3397
|
+
/**
|
|
3398
|
+
* Joins federated login.
|
|
3399
|
+
* @param {{appUrl: string, did: string;}} options - The federated login appUrl.
|
|
3400
|
+
* @returns {Promise<any>} The data received from the appUrl.
|
|
3401
|
+
*/
|
|
3402
|
+
async joinFederatedLogin({ appUrl, did }) {
|
|
3403
|
+
const url = new URL(appUrl);
|
|
3404
|
+
url.pathname = `${WELLKNOWN_SERVICE_PATH_PREFIX}/api/federated/join`;
|
|
3405
|
+
|
|
3406
|
+
const blocklet = await this.getBlocklet(did);
|
|
3407
|
+
const nodeInfo = await states.node.read();
|
|
3408
|
+
const blockletInfo = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
3409
|
+
const { permanentWallet, wallet } = blockletInfo;
|
|
3410
|
+
const memberSite = {
|
|
3411
|
+
appId: wallet.address,
|
|
3412
|
+
appPid: permanentWallet.address,
|
|
3413
|
+
migratedFrom: blocklet.migratedFrom || [],
|
|
3414
|
+
appName: blockletInfo.name,
|
|
3415
|
+
appDescription: blockletInfo.description,
|
|
3416
|
+
appUrl: blockletInfo.appUrl,
|
|
3417
|
+
appLogo:
|
|
3418
|
+
blocklet.environmentObj.BLOCKLET_APP_LOGO ||
|
|
3419
|
+
normalizePathPrefix(`${WELLKNOWN_SERVICE_PATH_PREFIX}/blocklet/logo`) ||
|
|
3420
|
+
'/',
|
|
3421
|
+
appLogoRect: blocklet.environmentObj.BLOCKLET_APP_LOGO_RECT,
|
|
3422
|
+
did: blockletInfo.did,
|
|
3423
|
+
pk: permanentWallet.publicKey,
|
|
3424
|
+
serverId: nodeInfo.did,
|
|
3425
|
+
serverVersion: nodeInfo.version,
|
|
3426
|
+
version: blockletInfo.version,
|
|
3427
|
+
};
|
|
3428
|
+
|
|
3429
|
+
const { data } = await request.post(url.href, {
|
|
3430
|
+
// 初次申请时,member 不在站点群中,不需要对数据进行加密
|
|
3431
|
+
site: memberSite,
|
|
3432
|
+
});
|
|
3433
|
+
await states.blockletExtras.setSettings(blocklet.meta.did, {
|
|
3434
|
+
federated: {
|
|
3435
|
+
config: {
|
|
3436
|
+
appId: blocklet.appDid,
|
|
3437
|
+
appPid: blocklet.appPid || blocklet.appDid,
|
|
3438
|
+
isMaster: false,
|
|
3439
|
+
},
|
|
3440
|
+
sites: data.sites,
|
|
3441
|
+
},
|
|
3442
|
+
});
|
|
3443
|
+
|
|
3444
|
+
const newState = await this.getBlocklet(did);
|
|
3445
|
+
this.emit(BlockletEvents.updated, newState);
|
|
3446
|
+
return newState;
|
|
3447
|
+
}
|
|
3448
|
+
|
|
3449
|
+
async setFederated({ did, config }) {
|
|
3450
|
+
await states.blockletExtras.setSettings(did, { federated: config });
|
|
3451
|
+
|
|
3452
|
+
const newState = await this.getBlocklet(did);
|
|
3453
|
+
this.emit(BlockletEvents.updated, newState);
|
|
3454
|
+
return newState;
|
|
3455
|
+
}
|
|
3456
|
+
|
|
3457
|
+
async configFederated({ did, autoLogin = false }) {
|
|
3458
|
+
const blocklet = await this.getBlocklet(did);
|
|
3459
|
+
const federated = cloneDeep(blocklet.settings.federated || {});
|
|
3460
|
+
federated.config.autoLogin = autoLogin;
|
|
3461
|
+
await states.blockletExtras.setSettings(did, { federated });
|
|
3462
|
+
|
|
3463
|
+
const newState = await this.getBlocklet(did);
|
|
3464
|
+
this.emit(BlockletEvents.updated, newState);
|
|
3465
|
+
return newState;
|
|
3466
|
+
}
|
|
3467
|
+
|
|
3468
|
+
async auditFederatedLogin({ appId, did, status }) {
|
|
3469
|
+
const blocklet = await this.getBlocklet(did);
|
|
3470
|
+
|
|
3471
|
+
const federated = cloneDeep(blocklet.settings.federated || {});
|
|
3472
|
+
const memberSite = federated.sites.find((item) => item.appId === appId);
|
|
3473
|
+
memberSite.status = status;
|
|
3474
|
+
if ([null, undefined].includes(federated.config.isMaster)) {
|
|
3475
|
+
const masterSite = federated.sites.find((item) => item.appId === blocklet.meta.did);
|
|
3476
|
+
|
|
3477
|
+
masterSite.isMaster = true;
|
|
3478
|
+
federated.config.isMaster = true;
|
|
3479
|
+
}
|
|
3480
|
+
// 有审批操作的一方,自动成为 master
|
|
3481
|
+
const newState = await this.setFederated({
|
|
3482
|
+
did: blocklet.meta.did,
|
|
3483
|
+
config: federated,
|
|
3484
|
+
});
|
|
3485
|
+
const nodeInfo = await states.node.read();
|
|
3486
|
+
const { permanentWallet } = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
3487
|
+
let delegation;
|
|
3488
|
+
let roles = [];
|
|
3489
|
+
if (status === 'approved') {
|
|
3490
|
+
delegation = signV2(permanentWallet.address, permanentWallet.secretKey, {
|
|
3491
|
+
agentDid: `did:abt:${memberSite.appPid}`,
|
|
3492
|
+
permissions: [
|
|
3493
|
+
{
|
|
3494
|
+
role: 'DIDConnectAgent',
|
|
3495
|
+
claims: [
|
|
3496
|
+
'authPrincipal',
|
|
3497
|
+
'profile',
|
|
3498
|
+
'signature',
|
|
3499
|
+
'prepareTx',
|
|
3500
|
+
'agreement',
|
|
3501
|
+
'verifiableCredential',
|
|
3502
|
+
'asset',
|
|
3503
|
+
// 'keyPair',
|
|
3504
|
+
// 'encryptionKey',
|
|
3505
|
+
],
|
|
3506
|
+
},
|
|
3507
|
+
],
|
|
3508
|
+
exp: Math.floor(new Date().getTime() / 1000) + 86400 * 365 * 100, // valid for 100 year
|
|
3509
|
+
});
|
|
3510
|
+
roles = await this.teamManager.getRoles(blocklet.meta.did);
|
|
3511
|
+
}
|
|
3512
|
+
|
|
3513
|
+
await request.post(`${memberSite.appUrl}/${WELLKNOWN_SERVICE_PATH_PREFIX}/api/federated/audit-res`, {
|
|
3514
|
+
signer: permanentWallet.address,
|
|
3515
|
+
data: signV2(permanentWallet.address, permanentWallet.secretKey, {
|
|
3516
|
+
appId: blocklet.meta.did,
|
|
3517
|
+
status,
|
|
3518
|
+
delegation,
|
|
3519
|
+
roles: roles.map((item) => pick(item, ['name', 'title', 'description'])),
|
|
3520
|
+
}),
|
|
3521
|
+
});
|
|
3522
|
+
const waitingList = federated.sites
|
|
3523
|
+
.filter((item) => item.appId !== federated.config.appId)
|
|
3524
|
+
.map((item) => {
|
|
3525
|
+
return limitSync(() =>
|
|
3526
|
+
pRetry(
|
|
3527
|
+
() =>
|
|
3528
|
+
request.post(`${item.appUrl}/${WELLKNOWN_SERVICE_PATH_PREFIX}/api/federated/sync`, {
|
|
3529
|
+
signer: permanentWallet.address,
|
|
3530
|
+
data: signV2(permanentWallet.address, permanentWallet.secretKey, { sites: federated.sites }),
|
|
3531
|
+
}),
|
|
3532
|
+
{ retries: 3 }
|
|
3533
|
+
)
|
|
3534
|
+
);
|
|
3535
|
+
});
|
|
3536
|
+
await Promise.all(waitingList);
|
|
3537
|
+
return newState;
|
|
3538
|
+
}
|
|
3539
|
+
}
|
|
3540
|
+
|
|
3541
|
+
class BlockletManager extends FederatedBlockletManager {}
|
|
3542
|
+
|
|
3368
3543
|
module.exports = BlockletManager;
|