@abtnode/core 1.16.14-beta-a898bfcb → 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 +299 -125
- 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 +20 -5
- package/lib/blocklet/manager/helper/upgrade-components.js +14 -5
- package/lib/event.js +32 -32
- package/lib/index.js +4 -0
- package/lib/states/audit-log.js +64 -70
- package/lib/states/notification.js +2 -2
- package/lib/util/blocklet.js +11 -0
- 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 +31 -31
|
@@ -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,
|
|
@@ -1835,18 +1818,19 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1835
1818
|
}
|
|
1836
1819
|
|
|
1837
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
|
+
|
|
1838
1828
|
try {
|
|
1839
|
-
const blockletForDownload = {
|
|
1840
|
-
...blocklet,
|
|
1841
|
-
children: (blocklet.children || []).filter((x) => {
|
|
1842
|
-
if (componentDids?.length) {
|
|
1843
|
-
return componentDids.includes(x.meta.did);
|
|
1844
|
-
}
|
|
1845
|
-
return x;
|
|
1846
|
-
}),
|
|
1847
|
-
};
|
|
1848
1829
|
const { isCancelled } = await this._downloadBlocklet(
|
|
1849
|
-
|
|
1830
|
+
{
|
|
1831
|
+
...blocklet,
|
|
1832
|
+
children: childrenToDownload,
|
|
1833
|
+
},
|
|
1850
1834
|
Object.assign({}, context, { skipCheckIntegrity })
|
|
1851
1835
|
);
|
|
1852
1836
|
|
|
@@ -1876,8 +1860,10 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1876
1860
|
},
|
|
1877
1861
|
});
|
|
1878
1862
|
this._createNotification(did, {
|
|
1879
|
-
title: '
|
|
1880
|
-
description:
|
|
1863
|
+
title: '',
|
|
1864
|
+
description: `${childrenToDownload
|
|
1865
|
+
.map((x) => `${x.meta.title}@${x.meta.version}`)
|
|
1866
|
+
.join(', ')} download failed for ${title}: ${err.message}`,
|
|
1881
1867
|
entityType: 'blocklet',
|
|
1882
1868
|
entityId: did,
|
|
1883
1869
|
severity: 'error',
|
|
@@ -1910,12 +1896,18 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1910
1896
|
throw new Error('blocklet status changed durning download');
|
|
1911
1897
|
}
|
|
1912
1898
|
|
|
1913
|
-
if (postAction ===
|
|
1899
|
+
if (postAction === INSTALL_ACTIONS.INSTALL) {
|
|
1914
1900
|
const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.installing, { componentDids });
|
|
1915
1901
|
this.emit(BlockletEvents.statusChange, state);
|
|
1916
1902
|
}
|
|
1917
1903
|
|
|
1918
|
-
if (
|
|
1904
|
+
if (
|
|
1905
|
+
[
|
|
1906
|
+
INSTALL_ACTIONS.INSTALL_COMPONENT,
|
|
1907
|
+
INSTALL_ACTIONS.UPGRADE_COMPONENT,
|
|
1908
|
+
'upgrade', // for backward compatibility
|
|
1909
|
+
].includes(postAction)
|
|
1910
|
+
) {
|
|
1919
1911
|
const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.upgrading, { componentDids });
|
|
1920
1912
|
this.emit(BlockletEvents.statusChange, state);
|
|
1921
1913
|
}
|
|
@@ -1929,14 +1921,32 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1929
1921
|
// install
|
|
1930
1922
|
try {
|
|
1931
1923
|
// install blocklet
|
|
1932
|
-
if (postAction ===
|
|
1924
|
+
if (postAction === INSTALL_ACTIONS.INSTALL) {
|
|
1933
1925
|
await this._onInstall({ blocklet, componentDids, context, oldBlocklet });
|
|
1934
1926
|
return;
|
|
1935
1927
|
}
|
|
1936
1928
|
|
|
1937
1929
|
// upgrade blocklet
|
|
1938
|
-
if (
|
|
1939
|
-
|
|
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
|
+
}
|
|
1940
1950
|
}
|
|
1941
1951
|
} catch (error) {
|
|
1942
1952
|
if (throwOnError) {
|
|
@@ -1974,22 +1984,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1974
1984
|
}
|
|
1975
1985
|
}
|
|
1976
1986
|
|
|
1977
|
-
async _onUpgrade({ oldBlocklet, newBlocklet, componentDids, context }) {
|
|
1978
|
-
const { version, did } = newBlocklet.meta;
|
|
1979
|
-
logger.info('do upgrade blocklet', { did, version });
|
|
1980
|
-
|
|
1981
|
-
try {
|
|
1982
|
-
await this._upgradeBlocklet({
|
|
1983
|
-
newBlocklet,
|
|
1984
|
-
oldBlocklet,
|
|
1985
|
-
componentDids,
|
|
1986
|
-
context,
|
|
1987
|
-
});
|
|
1988
|
-
} catch (err) {
|
|
1989
|
-
logger.error('blocklet onUpgrade error', { error: err });
|
|
1990
|
-
}
|
|
1991
|
-
}
|
|
1992
|
-
|
|
1993
1987
|
async _onRestart({ did, componentDids, context }) {
|
|
1994
1988
|
await this.stop({ did, componentDids, updateStatus: false }, context);
|
|
1995
1989
|
await this.start({ did, componentDids }, context);
|
|
@@ -2018,7 +2012,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2018
2012
|
});
|
|
2019
2013
|
|
|
2020
2014
|
this.emit(BlockletEvents.statusChange, res);
|
|
2021
|
-
this.emit(BlockletEvents.started, res);
|
|
2015
|
+
this.emit(BlockletEvents.started, { ...res, componentDids });
|
|
2022
2016
|
logger.info('blocklet healthy', { did, name, time: Date.now() - startedAt });
|
|
2023
2017
|
} catch (error) {
|
|
2024
2018
|
const status = await states.blocklet.getBlockletStatus(did);
|
|
@@ -2032,15 +2026,19 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2032
2026
|
await this.deleteProcess({ did, componentDids }, context);
|
|
2033
2027
|
const doc = await states.blocklet.setBlockletStatus(did, BlockletStatus.error, { componentDids });
|
|
2034
2028
|
|
|
2029
|
+
const description = `${getComponentNamesWithVersion(blocklet, componentDids)} start failed for ${title}: ${
|
|
2030
|
+
error.message
|
|
2031
|
+
}`;
|
|
2032
|
+
|
|
2035
2033
|
this._createNotification(did, {
|
|
2036
|
-
title: '
|
|
2037
|
-
description
|
|
2034
|
+
title: '',
|
|
2035
|
+
description,
|
|
2038
2036
|
entityType: 'blocklet',
|
|
2039
2037
|
entityId: did,
|
|
2040
2038
|
severity: 'error',
|
|
2041
2039
|
});
|
|
2042
2040
|
|
|
2043
|
-
this.emit(BlockletEvents.startFailed, {
|
|
2041
|
+
this.emit(BlockletEvents.startFailed, { ...doc, componentDids, error: { message: error.message } });
|
|
2044
2042
|
this.emit(BlockletEvents.statusChange, { ...doc, error });
|
|
2045
2043
|
|
|
2046
2044
|
if (throwOnError) {
|
|
@@ -2516,7 +2514,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2516
2514
|
const { name, version } = meta;
|
|
2517
2515
|
logger.error('failed to install blocklet', { name, did, version, error: err });
|
|
2518
2516
|
try {
|
|
2519
|
-
await this._rollback(
|
|
2517
|
+
await this._rollback(INSTALL_ACTIONS.INSTALL, did, oldBlocklet);
|
|
2520
2518
|
this.emit(BlockletEvents.installFailed, {
|
|
2521
2519
|
meta: { did },
|
|
2522
2520
|
error: {
|
|
@@ -2538,18 +2536,10 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2538
2536
|
}
|
|
2539
2537
|
}
|
|
2540
2538
|
|
|
2541
|
-
async _upgradeBlocklet({ newBlocklet, oldBlocklet, componentDids, context = {} }) {
|
|
2539
|
+
async _upgradeBlocklet({ newBlocklet, oldBlocklet, componentDids, action, context = {} }) {
|
|
2542
2540
|
const { meta, source, deployedFrom, children } = newBlocklet;
|
|
2543
2541
|
const { did, version, name, title } = meta;
|
|
2544
2542
|
|
|
2545
|
-
// ids
|
|
2546
|
-
const { skippedProcessIds, installedComponentNames } = getComponentChangedInfoForUpgrade({
|
|
2547
|
-
newBlocklet,
|
|
2548
|
-
oldBlocklet,
|
|
2549
|
-
context,
|
|
2550
|
-
});
|
|
2551
|
-
context.skippedProcessIds = skippedProcessIds;
|
|
2552
|
-
|
|
2553
2543
|
try {
|
|
2554
2544
|
// delete old process
|
|
2555
2545
|
try {
|
|
@@ -2622,10 +2612,22 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2622
2612
|
this.refreshListCache();
|
|
2623
2613
|
|
|
2624
2614
|
try {
|
|
2625
|
-
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
|
+
|
|
2626
2625
|
this._createNotification(did, {
|
|
2627
|
-
title: '
|
|
2628
|
-
description:
|
|
2626
|
+
title: '',
|
|
2627
|
+
description: `${getComponentNamesWithVersion(
|
|
2628
|
+
newBlocklet,
|
|
2629
|
+
componentDids
|
|
2630
|
+
)} is ${actionName} successfully for ${title}`,
|
|
2629
2631
|
action: `/blocklets/${did}/overview`,
|
|
2630
2632
|
entityType: 'blocklet',
|
|
2631
2633
|
entityId: did,
|
|
@@ -2647,21 +2649,27 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2647
2649
|
|
|
2648
2650
|
return blocklet;
|
|
2649
2651
|
} catch (err) {
|
|
2650
|
-
const b = await this._rollback(
|
|
2652
|
+
const b = await this._rollback(action, did, oldBlocklet);
|
|
2651
2653
|
logger.error('failed to upgrade blocklet', { did, version, name, error: err });
|
|
2652
2654
|
|
|
2653
2655
|
this.emit(BlockletEvents.updated, b);
|
|
2654
2656
|
|
|
2655
|
-
|
|
2656
|
-
|
|
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 } },
|
|
2657
2665
|
context,
|
|
2658
2666
|
});
|
|
2659
2667
|
|
|
2660
2668
|
this._createNotification(did, {
|
|
2661
|
-
title: '
|
|
2662
|
-
description:
|
|
2669
|
+
title: '',
|
|
2670
|
+
description: `${getComponentNamesWithVersion(newBlocklet, componentDids)} ${actionName} failed for ${title}: ${
|
|
2663
2671
|
err.message
|
|
2664
|
-
}
|
|
2672
|
+
}.`,
|
|
2665
2673
|
entityType: 'blocklet',
|
|
2666
2674
|
entityId: did,
|
|
2667
2675
|
severity: 'error',
|
|
@@ -2735,12 +2743,12 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2735
2743
|
}
|
|
2736
2744
|
|
|
2737
2745
|
/**
|
|
2738
|
-
* @param {string} action install, upgrade,
|
|
2746
|
+
* @param {string} action install, upgrade, installComponent, upgradeComponent
|
|
2739
2747
|
* @param {string} did
|
|
2740
2748
|
* @param {object} oldBlocklet
|
|
2741
2749
|
*/
|
|
2742
2750
|
async _rollback(action, did, oldBlocklet) {
|
|
2743
|
-
if (action ===
|
|
2751
|
+
if (action === INSTALL_ACTIONS.INSTALL) {
|
|
2744
2752
|
const extraState = oldBlocklet?.extraState;
|
|
2745
2753
|
|
|
2746
2754
|
// rollback blocklet extra state
|
|
@@ -2754,7 +2762,13 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2754
2762
|
return this._deleteBlocklet({ did, keepData: true });
|
|
2755
2763
|
}
|
|
2756
2764
|
|
|
2757
|
-
if (
|
|
2765
|
+
if (
|
|
2766
|
+
[
|
|
2767
|
+
INSTALL_ACTIONS.INSTALL_COMPONENT,
|
|
2768
|
+
INSTALL_ACTIONS.UPGRADE_COMPONENT,
|
|
2769
|
+
'upgrade', // for backward compatibility
|
|
2770
|
+
].includes(action)
|
|
2771
|
+
) {
|
|
2758
2772
|
const { extraState, ...blocklet } = oldBlocklet;
|
|
2759
2773
|
// rollback blocklet state
|
|
2760
2774
|
const result = await states.blocklet.updateBlocklet(did, blocklet);
|
|
@@ -2945,7 +2959,19 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2945
2959
|
return;
|
|
2946
2960
|
}
|
|
2947
2961
|
|
|
2948
|
-
|
|
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 });
|
|
2949
2975
|
} catch (error) {
|
|
2950
2976
|
logger.error('create notification failed', { error });
|
|
2951
2977
|
}
|
|
@@ -3259,7 +3285,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3259
3285
|
);
|
|
3260
3286
|
}
|
|
3261
3287
|
|
|
3262
|
-
async _restoreFromDisk(input) {
|
|
3288
|
+
async _restoreFromDisk(input, context) {
|
|
3263
3289
|
if (input.delay) {
|
|
3264
3290
|
await sleep(input.delay);
|
|
3265
3291
|
}
|
|
@@ -3283,6 +3309,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3283
3309
|
states,
|
|
3284
3310
|
move: true,
|
|
3285
3311
|
sync: false, // use queue to download and install application
|
|
3312
|
+
context,
|
|
3286
3313
|
});
|
|
3287
3314
|
removeRestoreDir();
|
|
3288
3315
|
} catch (error) {
|
|
@@ -3366,4 +3393,151 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3366
3393
|
}
|
|
3367
3394
|
}
|
|
3368
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
|
+
|
|
3369
3543
|
module.exports = BlockletManager;
|