@abtnode/core 1.16.14-beta-a898bfcb → 1.16.14-beta-d802cd3c
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 +312 -140
- 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 +12 -2
- package/lib/util/get-domain-for-blocklet.js +2 -2
- package/lib/util/launcher.js +11 -5
- package/lib/util/log.js +403 -0
- package/lib/util/store.js +7 -7
- package/lib/webhook/index.js +25 -14
- package/package.json +35 -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,22 +471,12 @@ 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
|
|
|
514
|
-
if (blocklet1.mode === BLOCKLET_MODES.DEVELOPMENT) {
|
|
515
|
-
const { logsDir } = blocklet1.env;
|
|
516
|
-
|
|
517
|
-
try {
|
|
518
|
-
fs.removeSync(logsDir);
|
|
519
|
-
fs.mkdirSync(logsDir, { recursive: true });
|
|
520
|
-
} catch {
|
|
521
|
-
// Windows && Node.js 18.x 下会发生删除错误(ENOTEMPTY)
|
|
522
|
-
// 但是这个错误并不影响后续逻辑,所以这里对这个错误做了 catch
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
|
|
526
480
|
const blocklet = await ensureAppPortsNotOccupied({ blocklet: blocklet1, componentDids, states, manager: this });
|
|
527
481
|
|
|
528
482
|
const getHookFn =
|
|
@@ -579,9 +533,11 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
579
533
|
|
|
580
534
|
const error = Array.isArray(err) ? err[0] : err;
|
|
581
535
|
logger.error('Failed to start blocklet', { error, did, title: blocklet1.meta.title });
|
|
582
|
-
const description =
|
|
536
|
+
const description = `${getComponentNamesWithVersion(blocklet1, componentDids)} start failed for ${
|
|
537
|
+
blocklet1.meta.title
|
|
538
|
+
}: ${error.message}`;
|
|
583
539
|
this._createNotification(did, {
|
|
584
|
-
title: '
|
|
540
|
+
title: '',
|
|
585
541
|
description,
|
|
586
542
|
entityType: 'blocklet',
|
|
587
543
|
entityId: did,
|
|
@@ -590,7 +546,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
590
546
|
|
|
591
547
|
await this.deleteProcess({ did, componentDids });
|
|
592
548
|
const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.error, { componentDids });
|
|
593
|
-
this.emit(BlockletEvents.startFailed, { ...res, error: { message: error.message } });
|
|
549
|
+
this.emit(BlockletEvents.startFailed, { ...res, componentDids, error: { message: error.message } });
|
|
594
550
|
this.emit(BlockletEvents.statusChange, { ...res, error: { message: error.message } });
|
|
595
551
|
|
|
596
552
|
if (throwOnError) {
|
|
@@ -647,7 +603,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
647
603
|
this.emit(BlockletEvents.statusChange, res);
|
|
648
604
|
|
|
649
605
|
// send notification to wallet
|
|
650
|
-
this.emit(BlockletEvents.stopped, res);
|
|
606
|
+
this.emit(BlockletEvents.stopped, { ...res, componentDids });
|
|
651
607
|
|
|
652
608
|
this.emit(BlockletInternalEvents.componentsUpdated, {
|
|
653
609
|
appDid: blocklet.appDid,
|
|
@@ -739,9 +695,12 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
739
695
|
const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped, { componentDids });
|
|
740
696
|
this.emit(BlockletEvents.statusChange, state);
|
|
741
697
|
|
|
698
|
+
const description = `${getComponentNamesWithVersion(result, componentDids)} restart failed for ${
|
|
699
|
+
result.meta.title
|
|
700
|
+
}: ${err.message || 'queue exception'}`;
|
|
742
701
|
this._createNotification(did, {
|
|
743
|
-
title: '
|
|
744
|
-
description
|
|
702
|
+
title: '',
|
|
703
|
+
description,
|
|
745
704
|
entityType: 'blocklet',
|
|
746
705
|
entityId: did,
|
|
747
706
|
severity: 'error',
|
|
@@ -941,23 +900,33 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
941
900
|
|
|
942
901
|
await this._updateDependents(rootDid);
|
|
943
902
|
|
|
903
|
+
// support edge case
|
|
904
|
+
if (newBlocklet.children.length === 0) {
|
|
905
|
+
await states.blocklet.setBlockletStatus(newBlocklet.meta.did, BlockletStatus.stopped);
|
|
906
|
+
}
|
|
907
|
+
|
|
944
908
|
this.emit(BlockletEvents.upgraded, { blocklet: newBlocklet, context: { ...context, createAuditLog: false } }); // trigger router refresh
|
|
945
909
|
|
|
946
910
|
this._createNotification(newBlocklet.meta.did, {
|
|
947
|
-
title: '
|
|
948
|
-
description:
|
|
911
|
+
title: '',
|
|
912
|
+
description: `${child.meta.title} is successfully deleted for ${newBlocklet.meta.title}.`,
|
|
949
913
|
entityType: 'blocklet',
|
|
950
914
|
entityId: newBlocklet.meta.did,
|
|
951
915
|
severity: 'success',
|
|
952
916
|
action: `/blocklets/${newBlocklet.meta.did}/components`,
|
|
953
917
|
});
|
|
954
918
|
|
|
919
|
+
this.emit(BlockletEvents.componentRemoved, {
|
|
920
|
+
...blocklet,
|
|
921
|
+
componentDids: [child.meta.did],
|
|
922
|
+
});
|
|
923
|
+
|
|
955
924
|
this.emit(BlockletInternalEvents.componentsUpdated, {
|
|
956
925
|
appDid: blocklet.appDid,
|
|
957
926
|
components: getComponentsInternalInfo(newBlocklet),
|
|
958
927
|
});
|
|
959
928
|
|
|
960
|
-
return newBlocklet;
|
|
929
|
+
return { ...newBlocklet, deletedComponent: child };
|
|
961
930
|
}
|
|
962
931
|
|
|
963
932
|
async cancelDownload({ did: inputDid }) {
|
|
@@ -1346,6 +1315,8 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1346
1315
|
return newState;
|
|
1347
1316
|
}
|
|
1348
1317
|
|
|
1318
|
+
// -------------------------------------
|
|
1319
|
+
|
|
1349
1320
|
// TODO: this method can be removed if title is not changed anymore
|
|
1350
1321
|
async updateComponentTitle({ did, rootDid: inputRootDid, title }) {
|
|
1351
1322
|
await titleSchema.validateAsync(title);
|
|
@@ -1527,7 +1498,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1527
1498
|
addToUpdates(component.meta.did, status);
|
|
1528
1499
|
logger.info('will sync status from pm2', { did, status, oldStatus, componentDid: component.meta.did });
|
|
1529
1500
|
}
|
|
1530
|
-
} catch {
|
|
1501
|
+
} catch (error) {
|
|
1531
1502
|
if (
|
|
1532
1503
|
![
|
|
1533
1504
|
BlockletStatus.added,
|
|
@@ -1536,11 +1507,21 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1536
1507
|
BlockletStatus.installing,
|
|
1537
1508
|
BlockletStatus.installed,
|
|
1538
1509
|
BlockletStatus.upgrading,
|
|
1539
|
-
].includes(component.status)
|
|
1510
|
+
].includes(component.status) &&
|
|
1511
|
+
(error.code !== 'BLOCKLET_PROCESS_404' ||
|
|
1512
|
+
![BlockletStatus.stopped, BlockletStatus.error].includes(component.status))
|
|
1540
1513
|
) {
|
|
1514
|
+
const oldStatus = component.status;
|
|
1541
1515
|
const status = BlockletStatus.stopped;
|
|
1542
1516
|
component.status = status;
|
|
1543
1517
|
addToUpdates(component.meta.did, status);
|
|
1518
|
+
logger.info('will sync status from pm2', {
|
|
1519
|
+
did,
|
|
1520
|
+
status,
|
|
1521
|
+
oldStatus,
|
|
1522
|
+
componentDid: component.meta.did,
|
|
1523
|
+
error: error.message,
|
|
1524
|
+
});
|
|
1544
1525
|
}
|
|
1545
1526
|
}
|
|
1546
1527
|
},
|
|
@@ -1784,7 +1765,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1784
1765
|
* @param {{
|
|
1785
1766
|
* blocklet: {},
|
|
1786
1767
|
* context: {},
|
|
1787
|
-
* postAction: 'install' | '
|
|
1768
|
+
* postAction: 'install' | 'upgradeComponent' | 'installComponent',
|
|
1788
1769
|
* oldBlocklet: {},
|
|
1789
1770
|
* throwOnError: Error,
|
|
1790
1771
|
* skipCheckStatusBeforeDownload: boolean,
|
|
@@ -1835,18 +1816,19 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1835
1816
|
}
|
|
1836
1817
|
|
|
1837
1818
|
// download bundle
|
|
1819
|
+
const childrenToDownload = (blocklet.children || []).filter((x) => {
|
|
1820
|
+
if (componentDids?.length) {
|
|
1821
|
+
return componentDids.includes(x.meta.did);
|
|
1822
|
+
}
|
|
1823
|
+
return x;
|
|
1824
|
+
});
|
|
1825
|
+
|
|
1838
1826
|
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
1827
|
const { isCancelled } = await this._downloadBlocklet(
|
|
1849
|
-
|
|
1828
|
+
{
|
|
1829
|
+
...blocklet,
|
|
1830
|
+
children: childrenToDownload,
|
|
1831
|
+
},
|
|
1850
1832
|
Object.assign({}, context, { skipCheckIntegrity })
|
|
1851
1833
|
);
|
|
1852
1834
|
|
|
@@ -1876,8 +1858,10 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1876
1858
|
},
|
|
1877
1859
|
});
|
|
1878
1860
|
this._createNotification(did, {
|
|
1879
|
-
title: '
|
|
1880
|
-
description:
|
|
1861
|
+
title: '',
|
|
1862
|
+
description: `${childrenToDownload
|
|
1863
|
+
.map((x) => `${x.meta.title}@${x.meta.version}`)
|
|
1864
|
+
.join(', ')} download failed for ${title}: ${err.message}`,
|
|
1881
1865
|
entityType: 'blocklet',
|
|
1882
1866
|
entityId: did,
|
|
1883
1867
|
severity: 'error',
|
|
@@ -1910,12 +1894,18 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1910
1894
|
throw new Error('blocklet status changed durning download');
|
|
1911
1895
|
}
|
|
1912
1896
|
|
|
1913
|
-
if (postAction ===
|
|
1897
|
+
if (postAction === INSTALL_ACTIONS.INSTALL) {
|
|
1914
1898
|
const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.installing, { componentDids });
|
|
1915
1899
|
this.emit(BlockletEvents.statusChange, state);
|
|
1916
1900
|
}
|
|
1917
1901
|
|
|
1918
|
-
if (
|
|
1902
|
+
if (
|
|
1903
|
+
[
|
|
1904
|
+
INSTALL_ACTIONS.INSTALL_COMPONENT,
|
|
1905
|
+
INSTALL_ACTIONS.UPGRADE_COMPONENT,
|
|
1906
|
+
'upgrade', // for backward compatibility
|
|
1907
|
+
].includes(postAction)
|
|
1908
|
+
) {
|
|
1919
1909
|
const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.upgrading, { componentDids });
|
|
1920
1910
|
this.emit(BlockletEvents.statusChange, state);
|
|
1921
1911
|
}
|
|
@@ -1929,14 +1919,32 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1929
1919
|
// install
|
|
1930
1920
|
try {
|
|
1931
1921
|
// install blocklet
|
|
1932
|
-
if (postAction ===
|
|
1922
|
+
if (postAction === INSTALL_ACTIONS.INSTALL) {
|
|
1933
1923
|
await this._onInstall({ blocklet, componentDids, context, oldBlocklet });
|
|
1934
1924
|
return;
|
|
1935
1925
|
}
|
|
1936
1926
|
|
|
1937
1927
|
// upgrade blocklet
|
|
1938
|
-
if (
|
|
1939
|
-
|
|
1928
|
+
if (
|
|
1929
|
+
[
|
|
1930
|
+
INSTALL_ACTIONS.INSTALL_COMPONENT,
|
|
1931
|
+
INSTALL_ACTIONS.UPGRADE_COMPONENT,
|
|
1932
|
+
'upgrade', // for backward compatibility
|
|
1933
|
+
].includes(postAction)
|
|
1934
|
+
) {
|
|
1935
|
+
logger.info('do upgrade blocklet', { did, version, postAction, componentDids });
|
|
1936
|
+
|
|
1937
|
+
try {
|
|
1938
|
+
await this._upgradeBlocklet({
|
|
1939
|
+
newBlocklet: blocklet,
|
|
1940
|
+
oldBlocklet,
|
|
1941
|
+
componentDids,
|
|
1942
|
+
context,
|
|
1943
|
+
action: postAction,
|
|
1944
|
+
});
|
|
1945
|
+
} catch (err) {
|
|
1946
|
+
logger.error('blocklet onUpgrade error', { error: err });
|
|
1947
|
+
}
|
|
1940
1948
|
}
|
|
1941
1949
|
} catch (error) {
|
|
1942
1950
|
if (throwOnError) {
|
|
@@ -1974,22 +1982,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1974
1982
|
}
|
|
1975
1983
|
}
|
|
1976
1984
|
|
|
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
1985
|
async _onRestart({ did, componentDids, context }) {
|
|
1994
1986
|
await this.stop({ did, componentDids, updateStatus: false }, context);
|
|
1995
1987
|
await this.start({ did, componentDids }, context);
|
|
@@ -2018,7 +2010,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2018
2010
|
});
|
|
2019
2011
|
|
|
2020
2012
|
this.emit(BlockletEvents.statusChange, res);
|
|
2021
|
-
this.emit(BlockletEvents.started, res);
|
|
2013
|
+
this.emit(BlockletEvents.started, { ...res, componentDids });
|
|
2022
2014
|
logger.info('blocklet healthy', { did, name, time: Date.now() - startedAt });
|
|
2023
2015
|
} catch (error) {
|
|
2024
2016
|
const status = await states.blocklet.getBlockletStatus(did);
|
|
@@ -2032,15 +2024,19 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2032
2024
|
await this.deleteProcess({ did, componentDids }, context);
|
|
2033
2025
|
const doc = await states.blocklet.setBlockletStatus(did, BlockletStatus.error, { componentDids });
|
|
2034
2026
|
|
|
2027
|
+
const description = `${getComponentNamesWithVersion(blocklet, componentDids)} start failed for ${title}: ${
|
|
2028
|
+
error.message
|
|
2029
|
+
}`;
|
|
2030
|
+
|
|
2035
2031
|
this._createNotification(did, {
|
|
2036
|
-
title: '
|
|
2037
|
-
description
|
|
2032
|
+
title: '',
|
|
2033
|
+
description,
|
|
2038
2034
|
entityType: 'blocklet',
|
|
2039
2035
|
entityId: did,
|
|
2040
2036
|
severity: 'error',
|
|
2041
2037
|
});
|
|
2042
2038
|
|
|
2043
|
-
this.emit(BlockletEvents.startFailed, {
|
|
2039
|
+
this.emit(BlockletEvents.startFailed, { ...doc, componentDids, error: { message: error.message } });
|
|
2044
2040
|
this.emit(BlockletEvents.statusChange, { ...doc, error });
|
|
2045
2041
|
|
|
2046
2042
|
if (throwOnError) {
|
|
@@ -2516,7 +2512,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2516
2512
|
const { name, version } = meta;
|
|
2517
2513
|
logger.error('failed to install blocklet', { name, did, version, error: err });
|
|
2518
2514
|
try {
|
|
2519
|
-
await this._rollback(
|
|
2515
|
+
await this._rollback(INSTALL_ACTIONS.INSTALL, did, oldBlocklet);
|
|
2520
2516
|
this.emit(BlockletEvents.installFailed, {
|
|
2521
2517
|
meta: { did },
|
|
2522
2518
|
error: {
|
|
@@ -2538,22 +2534,14 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2538
2534
|
}
|
|
2539
2535
|
}
|
|
2540
2536
|
|
|
2541
|
-
async _upgradeBlocklet({ newBlocklet, oldBlocklet, componentDids, context = {} }) {
|
|
2537
|
+
async _upgradeBlocklet({ newBlocklet, oldBlocklet, componentDids, action, context = {} }) {
|
|
2542
2538
|
const { meta, source, deployedFrom, children } = newBlocklet;
|
|
2543
2539
|
const { did, version, name, title } = meta;
|
|
2544
2540
|
|
|
2545
|
-
// ids
|
|
2546
|
-
const { skippedProcessIds, installedComponentNames } = getComponentChangedInfoForUpgrade({
|
|
2547
|
-
newBlocklet,
|
|
2548
|
-
oldBlocklet,
|
|
2549
|
-
context,
|
|
2550
|
-
});
|
|
2551
|
-
context.skippedProcessIds = skippedProcessIds;
|
|
2552
|
-
|
|
2553
2541
|
try {
|
|
2554
2542
|
// delete old process
|
|
2555
2543
|
try {
|
|
2556
|
-
await this.deleteProcess({ did }, context);
|
|
2544
|
+
await this.deleteProcess({ did, componentDids }, context);
|
|
2557
2545
|
logger.info('delete blocklet process for upgrading', { did, name });
|
|
2558
2546
|
} catch (err) {
|
|
2559
2547
|
logger.error('delete blocklet process for upgrading', { did, name, error: err });
|
|
@@ -2622,10 +2610,22 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2622
2610
|
this.refreshListCache();
|
|
2623
2611
|
|
|
2624
2612
|
try {
|
|
2625
|
-
this.emit(BlockletEvents.upgraded, { blocklet, context });
|
|
2613
|
+
this.emit(BlockletEvents.upgraded, { blocklet, context }); // trigger router refresh
|
|
2614
|
+
|
|
2615
|
+
const notificationEvent =
|
|
2616
|
+
action === INSTALL_ACTIONS.INSTALL_COMPONENT
|
|
2617
|
+
? BlockletEvents.componentInstalled
|
|
2618
|
+
: BlockletEvents.componentUpgraded;
|
|
2619
|
+
const actionName = action === INSTALL_ACTIONS.INSTALL_COMPONENT ? 'installed' : 'upgraded';
|
|
2620
|
+
|
|
2621
|
+
this.emit(notificationEvent, { ...blocklet, componentDids, context });
|
|
2622
|
+
|
|
2626
2623
|
this._createNotification(did, {
|
|
2627
|
-
title: '
|
|
2628
|
-
description:
|
|
2624
|
+
title: '',
|
|
2625
|
+
description: `${getComponentNamesWithVersion(
|
|
2626
|
+
newBlocklet,
|
|
2627
|
+
componentDids
|
|
2628
|
+
)} is ${actionName} successfully for ${title}`,
|
|
2629
2629
|
action: `/blocklets/${did}/overview`,
|
|
2630
2630
|
entityType: 'blocklet',
|
|
2631
2631
|
entityId: did,
|
|
@@ -2647,21 +2647,27 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2647
2647
|
|
|
2648
2648
|
return blocklet;
|
|
2649
2649
|
} catch (err) {
|
|
2650
|
-
const b = await this._rollback(
|
|
2650
|
+
const b = await this._rollback(action, did, oldBlocklet);
|
|
2651
2651
|
logger.error('failed to upgrade blocklet', { did, version, name, error: err });
|
|
2652
2652
|
|
|
2653
2653
|
this.emit(BlockletEvents.updated, b);
|
|
2654
2654
|
|
|
2655
|
-
|
|
2656
|
-
|
|
2655
|
+
const actionName = action === INSTALL_ACTIONS.INSTALL_COMPONENT ? 'install' : 'upgrade';
|
|
2656
|
+
const notificationEvent =
|
|
2657
|
+
action === INSTALL_ACTIONS.INSTALL_COMPONENT
|
|
2658
|
+
? BlockletEvents.componentInstallFailed
|
|
2659
|
+
: BlockletEvents.componentUpgradeFailed;
|
|
2660
|
+
|
|
2661
|
+
this.emit(notificationEvent, {
|
|
2662
|
+
blocklet: { ...oldBlocklet, componentDids, error: { message: err.message } },
|
|
2657
2663
|
context,
|
|
2658
2664
|
});
|
|
2659
2665
|
|
|
2660
2666
|
this._createNotification(did, {
|
|
2661
|
-
title: '
|
|
2662
|
-
description:
|
|
2667
|
+
title: '',
|
|
2668
|
+
description: `${getComponentNamesWithVersion(newBlocklet, componentDids)} ${actionName} failed for ${title}: ${
|
|
2663
2669
|
err.message
|
|
2664
|
-
}
|
|
2670
|
+
}.`,
|
|
2665
2671
|
entityType: 'blocklet',
|
|
2666
2672
|
entityId: did,
|
|
2667
2673
|
severity: 'error',
|
|
@@ -2735,12 +2741,12 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2735
2741
|
}
|
|
2736
2742
|
|
|
2737
2743
|
/**
|
|
2738
|
-
* @param {string} action install, upgrade,
|
|
2744
|
+
* @param {string} action install, upgrade, installComponent, upgradeComponent
|
|
2739
2745
|
* @param {string} did
|
|
2740
2746
|
* @param {object} oldBlocklet
|
|
2741
2747
|
*/
|
|
2742
2748
|
async _rollback(action, did, oldBlocklet) {
|
|
2743
|
-
if (action ===
|
|
2749
|
+
if (action === INSTALL_ACTIONS.INSTALL) {
|
|
2744
2750
|
const extraState = oldBlocklet?.extraState;
|
|
2745
2751
|
|
|
2746
2752
|
// rollback blocklet extra state
|
|
@@ -2754,7 +2760,13 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2754
2760
|
return this._deleteBlocklet({ did, keepData: true });
|
|
2755
2761
|
}
|
|
2756
2762
|
|
|
2757
|
-
if (
|
|
2763
|
+
if (
|
|
2764
|
+
[
|
|
2765
|
+
INSTALL_ACTIONS.INSTALL_COMPONENT,
|
|
2766
|
+
INSTALL_ACTIONS.UPGRADE_COMPONENT,
|
|
2767
|
+
'upgrade', // for backward compatibility
|
|
2768
|
+
].includes(action)
|
|
2769
|
+
) {
|
|
2758
2770
|
const { extraState, ...blocklet } = oldBlocklet;
|
|
2759
2771
|
// rollback blocklet state
|
|
2760
2772
|
const result = await states.blocklet.updateBlocklet(did, blocklet);
|
|
@@ -2945,7 +2957,19 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2945
2957
|
return;
|
|
2946
2958
|
}
|
|
2947
2959
|
|
|
2948
|
-
|
|
2960
|
+
let blockletUrl;
|
|
2961
|
+
try {
|
|
2962
|
+
const blocklet = await this.getBlocklet(did);
|
|
2963
|
+
if (blocklet) {
|
|
2964
|
+
const urls = blocklet.site?.domainAliases || [];
|
|
2965
|
+
const customUrl = urls.find((x) => !x.isProtected)?.value;
|
|
2966
|
+
blockletUrl = `http://${customUrl || getDidDomainForBlocklet({ appPid: blocklet.appPid })}`;
|
|
2967
|
+
}
|
|
2968
|
+
} catch (error) {
|
|
2969
|
+
logger.error('[_createNotification] get blocklet url failed', { error });
|
|
2970
|
+
}
|
|
2971
|
+
|
|
2972
|
+
await states.notification.create({ ...notification, blockletUrl });
|
|
2949
2973
|
} catch (error) {
|
|
2950
2974
|
logger.error('create notification failed', { error });
|
|
2951
2975
|
}
|
|
@@ -3259,7 +3283,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3259
3283
|
);
|
|
3260
3284
|
}
|
|
3261
3285
|
|
|
3262
|
-
async _restoreFromDisk(input) {
|
|
3286
|
+
async _restoreFromDisk(input, context) {
|
|
3263
3287
|
if (input.delay) {
|
|
3264
3288
|
await sleep(input.delay);
|
|
3265
3289
|
}
|
|
@@ -3283,6 +3307,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3283
3307
|
states,
|
|
3284
3308
|
move: true,
|
|
3285
3309
|
sync: false, // use queue to download and install application
|
|
3310
|
+
context,
|
|
3286
3311
|
});
|
|
3287
3312
|
removeRestoreDir();
|
|
3288
3313
|
} catch (error) {
|
|
@@ -3366,4 +3391,151 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3366
3391
|
}
|
|
3367
3392
|
}
|
|
3368
3393
|
|
|
3394
|
+
class FederatedBlockletManager extends DiskBlockletManager {
|
|
3395
|
+
/**
|
|
3396
|
+
* Joins federated login.
|
|
3397
|
+
* @param {{appUrl: string, did: string;}} options - The federated login appUrl.
|
|
3398
|
+
* @returns {Promise<any>} The data received from the appUrl.
|
|
3399
|
+
*/
|
|
3400
|
+
async joinFederatedLogin({ appUrl, did }) {
|
|
3401
|
+
const url = new URL(appUrl);
|
|
3402
|
+
url.pathname = `${WELLKNOWN_SERVICE_PATH_PREFIX}/api/federated/join`;
|
|
3403
|
+
|
|
3404
|
+
const blocklet = await this.getBlocklet(did);
|
|
3405
|
+
const nodeInfo = await states.node.read();
|
|
3406
|
+
const blockletInfo = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
3407
|
+
const { permanentWallet, wallet } = blockletInfo;
|
|
3408
|
+
const memberSite = {
|
|
3409
|
+
appId: wallet.address,
|
|
3410
|
+
appPid: permanentWallet.address,
|
|
3411
|
+
migratedFrom: blocklet.migratedFrom || [],
|
|
3412
|
+
appName: blockletInfo.name,
|
|
3413
|
+
appDescription: blockletInfo.description,
|
|
3414
|
+
appUrl: blockletInfo.appUrl,
|
|
3415
|
+
appLogo:
|
|
3416
|
+
blocklet.environmentObj.BLOCKLET_APP_LOGO ||
|
|
3417
|
+
normalizePathPrefix(`${WELLKNOWN_SERVICE_PATH_PREFIX}/blocklet/logo`) ||
|
|
3418
|
+
'/',
|
|
3419
|
+
appLogoRect: blocklet.environmentObj.BLOCKLET_APP_LOGO_RECT,
|
|
3420
|
+
did: blockletInfo.did,
|
|
3421
|
+
pk: permanentWallet.publicKey,
|
|
3422
|
+
serverId: nodeInfo.did,
|
|
3423
|
+
serverVersion: nodeInfo.version,
|
|
3424
|
+
version: blockletInfo.version,
|
|
3425
|
+
};
|
|
3426
|
+
|
|
3427
|
+
const { data } = await request.post(url.href, {
|
|
3428
|
+
// 初次申请时,member 不在站点群中,不需要对数据进行加密
|
|
3429
|
+
site: memberSite,
|
|
3430
|
+
});
|
|
3431
|
+
await states.blockletExtras.setSettings(blocklet.meta.did, {
|
|
3432
|
+
federated: {
|
|
3433
|
+
config: {
|
|
3434
|
+
appId: blocklet.appDid,
|
|
3435
|
+
appPid: blocklet.appPid || blocklet.appDid,
|
|
3436
|
+
isMaster: false,
|
|
3437
|
+
},
|
|
3438
|
+
sites: data.sites,
|
|
3439
|
+
},
|
|
3440
|
+
});
|
|
3441
|
+
|
|
3442
|
+
const newState = await this.getBlocklet(did);
|
|
3443
|
+
this.emit(BlockletEvents.updated, newState);
|
|
3444
|
+
return newState;
|
|
3445
|
+
}
|
|
3446
|
+
|
|
3447
|
+
async setFederated({ did, config }) {
|
|
3448
|
+
await states.blockletExtras.setSettings(did, { federated: config });
|
|
3449
|
+
|
|
3450
|
+
const newState = await this.getBlocklet(did);
|
|
3451
|
+
this.emit(BlockletEvents.updated, newState);
|
|
3452
|
+
return newState;
|
|
3453
|
+
}
|
|
3454
|
+
|
|
3455
|
+
async configFederated({ did, autoLogin = false }) {
|
|
3456
|
+
const blocklet = await this.getBlocklet(did);
|
|
3457
|
+
const federated = cloneDeep(blocklet.settings.federated || {});
|
|
3458
|
+
federated.config.autoLogin = autoLogin;
|
|
3459
|
+
await states.blockletExtras.setSettings(did, { federated });
|
|
3460
|
+
|
|
3461
|
+
const newState = await this.getBlocklet(did);
|
|
3462
|
+
this.emit(BlockletEvents.updated, newState);
|
|
3463
|
+
return newState;
|
|
3464
|
+
}
|
|
3465
|
+
|
|
3466
|
+
async auditFederatedLogin({ appId, did, status }) {
|
|
3467
|
+
const blocklet = await this.getBlocklet(did);
|
|
3468
|
+
|
|
3469
|
+
const federated = cloneDeep(blocklet.settings.federated || {});
|
|
3470
|
+
const memberSite = federated.sites.find((item) => item.appId === appId);
|
|
3471
|
+
memberSite.status = status;
|
|
3472
|
+
if ([null, undefined].includes(federated.config.isMaster)) {
|
|
3473
|
+
const masterSite = federated.sites.find((item) => item.appId === blocklet.meta.did);
|
|
3474
|
+
|
|
3475
|
+
masterSite.isMaster = true;
|
|
3476
|
+
federated.config.isMaster = true;
|
|
3477
|
+
}
|
|
3478
|
+
// 有审批操作的一方,自动成为 master
|
|
3479
|
+
const newState = await this.setFederated({
|
|
3480
|
+
did: blocklet.meta.did,
|
|
3481
|
+
config: federated,
|
|
3482
|
+
});
|
|
3483
|
+
const nodeInfo = await states.node.read();
|
|
3484
|
+
const { permanentWallet } = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
3485
|
+
let delegation;
|
|
3486
|
+
let roles = [];
|
|
3487
|
+
if (status === 'approved') {
|
|
3488
|
+
delegation = signV2(permanentWallet.address, permanentWallet.secretKey, {
|
|
3489
|
+
agentDid: `did:abt:${memberSite.appPid}`,
|
|
3490
|
+
permissions: [
|
|
3491
|
+
{
|
|
3492
|
+
role: 'DIDConnectAgent',
|
|
3493
|
+
claims: [
|
|
3494
|
+
'authPrincipal',
|
|
3495
|
+
'profile',
|
|
3496
|
+
'signature',
|
|
3497
|
+
'prepareTx',
|
|
3498
|
+
'agreement',
|
|
3499
|
+
'verifiableCredential',
|
|
3500
|
+
'asset',
|
|
3501
|
+
// 'keyPair',
|
|
3502
|
+
// 'encryptionKey',
|
|
3503
|
+
],
|
|
3504
|
+
},
|
|
3505
|
+
],
|
|
3506
|
+
exp: Math.floor(new Date().getTime() / 1000) + 86400 * 365 * 100, // valid for 100 year
|
|
3507
|
+
});
|
|
3508
|
+
roles = await this.teamManager.getRoles(blocklet.meta.did);
|
|
3509
|
+
}
|
|
3510
|
+
|
|
3511
|
+
await request.post(`${memberSite.appUrl}/${WELLKNOWN_SERVICE_PATH_PREFIX}/api/federated/audit-res`, {
|
|
3512
|
+
signer: permanentWallet.address,
|
|
3513
|
+
data: signV2(permanentWallet.address, permanentWallet.secretKey, {
|
|
3514
|
+
appId: blocklet.meta.did,
|
|
3515
|
+
status,
|
|
3516
|
+
delegation,
|
|
3517
|
+
roles: roles.map((item) => pick(item, ['name', 'title', 'description'])),
|
|
3518
|
+
}),
|
|
3519
|
+
});
|
|
3520
|
+
const waitingList = federated.sites
|
|
3521
|
+
.filter((item) => item.appId !== federated.config.appId)
|
|
3522
|
+
.map((item) => {
|
|
3523
|
+
return limitSync(() =>
|
|
3524
|
+
pRetry(
|
|
3525
|
+
() =>
|
|
3526
|
+
request.post(`${item.appUrl}/${WELLKNOWN_SERVICE_PATH_PREFIX}/api/federated/sync`, {
|
|
3527
|
+
signer: permanentWallet.address,
|
|
3528
|
+
data: signV2(permanentWallet.address, permanentWallet.secretKey, { sites: federated.sites }),
|
|
3529
|
+
}),
|
|
3530
|
+
{ retries: 3 }
|
|
3531
|
+
)
|
|
3532
|
+
);
|
|
3533
|
+
});
|
|
3534
|
+
await Promise.all(waitingList);
|
|
3535
|
+
return newState;
|
|
3536
|
+
}
|
|
3537
|
+
}
|
|
3538
|
+
|
|
3539
|
+
class BlockletManager extends FederatedBlockletManager {}
|
|
3540
|
+
|
|
3369
3541
|
module.exports = BlockletManager;
|