@abtnode/core 1.8.65-beta-f7af64a4 → 1.8.65-beta-bfcc12ce
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/blocklet/downloader/blocklet-downloader.js +33 -12
- package/lib/blocklet/manager/disk.js +163 -93
- package/lib/blocklet/manager/helper/install-from-backup.js +11 -4
- package/lib/blocklet/manager/helper/rollback-cache.js +41 -0
- package/lib/blocklet/storage/backup/blocklet-extras.js +4 -0
- package/lib/blocklet/storage/backup/blocklets.js +23 -29
- package/lib/blocklet/storage/backup/data.js +2 -2
- package/lib/blocklet/storage/backup/logs.js +3 -2
- package/lib/blocklet/storage/backup/spaces.js +3 -14
- package/lib/blocklet/storage/restore/blocklet-extras.js +8 -3
- package/lib/blocklet/storage/restore/blocklets.js +29 -11
- package/lib/blocklet/storage/restore/logs.js +21 -0
- package/lib/blocklet/storage/restore/spaces.js +6 -1
- package/lib/blocklet/storage/utils/hash.js +51 -0
- package/lib/blocklet/storage/utils/zip.js +43 -0
- package/lib/router/helper.js +64 -12
- package/lib/router/index.js +4 -0
- package/lib/states/blocklet.js +2 -2
- package/lib/util/blocklet.js +1 -1
- package/package.json +27 -25
|
@@ -32,7 +32,10 @@ const isMetaFileExist = (dir) => {
|
|
|
32
32
|
* }}
|
|
33
33
|
* @returns {boolean}
|
|
34
34
|
*/
|
|
35
|
-
const needDownload = (
|
|
35
|
+
const needDownload = (
|
|
36
|
+
component,
|
|
37
|
+
{ installDir, logger = defaultLogger, cachedBundles = [], skipCheckIntegrity } = {}
|
|
38
|
+
) => {
|
|
36
39
|
if (component.mode === BLOCKLET_MODES.DEVELOPMENT) {
|
|
37
40
|
return false;
|
|
38
41
|
}
|
|
@@ -62,10 +65,15 @@ const needDownload = (component, { installDir, logger = defaultLogger, cachedBun
|
|
|
62
65
|
return false;
|
|
63
66
|
}
|
|
64
67
|
|
|
68
|
+
if (skipCheckIntegrity) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
65
72
|
// check integrity
|
|
66
73
|
|
|
67
74
|
const cachedBundle = cachedBundles.find((x) => x.bundleId === bundleId);
|
|
68
75
|
|
|
76
|
+
// FIXME: 不是新安装的组件不应该 check, 不应该 block 安装成功 https://github.com/blocklet/launcher/actions/runs/4184577090/jobs/7250416272
|
|
69
77
|
if (!cachedBundle) {
|
|
70
78
|
return true;
|
|
71
79
|
}
|
|
@@ -84,10 +92,12 @@ const needDownload = (component, { installDir, logger = defaultLogger, cachedBun
|
|
|
84
92
|
class BlockletDownloader extends EventEmitter {
|
|
85
93
|
/**
|
|
86
94
|
* @param {{
|
|
95
|
+
* installDir: string,
|
|
96
|
+
* downloadDir: string,
|
|
87
97
|
* cache: {
|
|
88
98
|
* set: (key: string, value: any) => Promise
|
|
89
99
|
* get: (key: string) => Promise<any>
|
|
90
|
-
* }
|
|
100
|
+
* },
|
|
91
101
|
* }}
|
|
92
102
|
*/
|
|
93
103
|
constructor({ installDir, downloadDir, cache, logger = defaultLogger }) {
|
|
@@ -106,20 +116,25 @@ class BlockletDownloader extends EventEmitter {
|
|
|
106
116
|
|
|
107
117
|
/**
|
|
108
118
|
* @param {{}} blocklet
|
|
109
|
-
* @param {{
|
|
119
|
+
* @param {{
|
|
120
|
+
* preDownload: ({ downloadList: Array<meta>, downloadComponentIds: Array<string> }) => any
|
|
121
|
+
* postDownload: ({ downloadList: Array<meta>, downloadComponentIds: Array<string>, isCancelled: boolean }) => any
|
|
122
|
+
* skipCheckIntegrity: boolean
|
|
123
|
+
* }} [options={}]
|
|
110
124
|
* @return {*}
|
|
111
125
|
*/
|
|
112
|
-
async download(blocklet,
|
|
126
|
+
async download(blocklet, options = {}) {
|
|
113
127
|
const {
|
|
114
128
|
meta: { name, did },
|
|
115
129
|
} = blocklet;
|
|
116
130
|
|
|
117
131
|
this.logger.info('Download Blocklet', { name, did });
|
|
118
132
|
|
|
119
|
-
const { preDownload = () => {}, postDownload = () => {} } =
|
|
133
|
+
const { preDownload = () => {}, postDownload = () => {}, skipCheckIntegrity } = options;
|
|
120
134
|
|
|
121
135
|
const { downloadComponentIds, downloadList } = await this.getDownloadList({
|
|
122
136
|
blocklet,
|
|
137
|
+
skipCheckIntegrity,
|
|
123
138
|
});
|
|
124
139
|
|
|
125
140
|
await preDownload({ downloadList, downloadComponentIds });
|
|
@@ -133,13 +148,15 @@ class BlockletDownloader extends EventEmitter {
|
|
|
133
148
|
const tasks = [];
|
|
134
149
|
for (const meta of downloadList) {
|
|
135
150
|
const url = meta.dist.tarball;
|
|
136
|
-
tasks.push(this.bundleDownloader.download(meta, did, url,
|
|
151
|
+
tasks.push(this.bundleDownloader.download(meta, did, url, options));
|
|
137
152
|
}
|
|
138
153
|
const results = await Promise.all(tasks);
|
|
139
154
|
|
|
140
|
-
|
|
155
|
+
const isCancelled = results.some((x) => x.isCancelled);
|
|
156
|
+
|
|
157
|
+
await postDownload({ downloadList, downloadComponentIds, isCancelled });
|
|
141
158
|
|
|
142
|
-
if (
|
|
159
|
+
if (isCancelled) {
|
|
143
160
|
return { isCancelled: true };
|
|
144
161
|
}
|
|
145
162
|
} catch (error) {
|
|
@@ -160,11 +177,11 @@ class BlockletDownloader extends EventEmitter {
|
|
|
160
177
|
* blocklet;
|
|
161
178
|
* }}
|
|
162
179
|
* @returns {{
|
|
163
|
-
* downloadList: Array<
|
|
164
|
-
* downloadComponentIds: Array<string>;
|
|
180
|
+
* downloadList: Array<import('@abtnode/client').BlockletMeta>;
|
|
181
|
+
* downloadComponentIds: Array<string>; // like: "z8ia1i2yq67x39vruqQTbkVcwCnqRGx8zSPsJ/z8iZwubkNdA1BfEUwc5NJpY2Jnfm7yEbyvmKS"
|
|
165
182
|
* }}
|
|
166
183
|
*/
|
|
167
|
-
async getDownloadList({ blocklet }) {
|
|
184
|
+
async getDownloadList({ blocklet, skipCheckIntegrity }) {
|
|
168
185
|
const downloadComponentIds = [];
|
|
169
186
|
const metas = {};
|
|
170
187
|
|
|
@@ -178,7 +195,11 @@ class BlockletDownloader extends EventEmitter {
|
|
|
178
195
|
return;
|
|
179
196
|
}
|
|
180
197
|
|
|
181
|
-
const needComponentDownload = needDownload(component, {
|
|
198
|
+
const needComponentDownload = needDownload(component, {
|
|
199
|
+
installDir: this.installDir,
|
|
200
|
+
cachedBundles,
|
|
201
|
+
skipCheckIntegrity,
|
|
202
|
+
});
|
|
182
203
|
if (!needComponentDownload) {
|
|
183
204
|
this.logger.info(`skip download existed bundle ${bundleId}`, { source: component.source });
|
|
184
205
|
return;
|
|
@@ -129,6 +129,7 @@ const { SpacesRestore } = require('../storage/restore/spaces');
|
|
|
129
129
|
const installFromBackup = require('./helper/install-from-backup');
|
|
130
130
|
const { resolveDownload, resolveDiffDownload } = require('../downloader/resolve-download');
|
|
131
131
|
const BlockletDownloader = require('../downloader/blocklet-downloader');
|
|
132
|
+
const RollbackCache = require('./helper/rollback-cache');
|
|
132
133
|
|
|
133
134
|
const {
|
|
134
135
|
isInProgress,
|
|
@@ -218,6 +219,8 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
218
219
|
cache: states.cache,
|
|
219
220
|
});
|
|
220
221
|
|
|
222
|
+
this._rollbackCache = new RollbackCache({ dir: this.dataDirs.tmp });
|
|
223
|
+
|
|
221
224
|
if (daemon) {
|
|
222
225
|
blockletPm2Events.on('online', (data) => this._syncPm2Status('online', data.blockletDid));
|
|
223
226
|
blockletPm2Events.on('stop', (data) => this._syncPm2Status('stop', data.blockletDid));
|
|
@@ -255,6 +258,10 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
255
258
|
async install(params, context = {}) {
|
|
256
259
|
logger.debug('install blocklet', { params, context });
|
|
257
260
|
|
|
261
|
+
if (!params.controller && context?.user?.controller) {
|
|
262
|
+
params.controller = context.user.controller;
|
|
263
|
+
}
|
|
264
|
+
|
|
258
265
|
const info = await states.node.read();
|
|
259
266
|
|
|
260
267
|
context.headers = Object.assign(context?.headers || {}, {
|
|
@@ -839,12 +846,13 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
839
846
|
return newBlocklet;
|
|
840
847
|
}
|
|
841
848
|
|
|
849
|
+
// eslint-disable-next-line no-unused-vars
|
|
842
850
|
async cancelDownload({ did: inputDid }, context) {
|
|
843
851
|
try {
|
|
844
852
|
await statusLock.acquire();
|
|
845
853
|
const blocklet = await states.blocklet.getBlocklet(inputDid);
|
|
846
854
|
if (!blocklet) {
|
|
847
|
-
throw new Error(
|
|
855
|
+
throw new Error(`Can not cancel download for non-exist blocklet in database. did: ${inputDid}`);
|
|
848
856
|
}
|
|
849
857
|
|
|
850
858
|
const { name, did, version } = blocklet.meta;
|
|
@@ -854,15 +862,35 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
854
862
|
}
|
|
855
863
|
|
|
856
864
|
const job = await this.installQueue.get(did);
|
|
857
|
-
if (job) {
|
|
858
|
-
const { postAction, oldBlocklet } = job;
|
|
859
|
-
await this._rollback(postAction, did, oldBlocklet);
|
|
860
|
-
}
|
|
861
865
|
|
|
866
|
+
// cancel job
|
|
862
867
|
if (blocklet.status === BlockletStatus.downloading) {
|
|
863
|
-
|
|
868
|
+
try {
|
|
869
|
+
await this.blockletDownloader.cancelDownload(blocklet.meta.did);
|
|
870
|
+
} catch (error) {
|
|
871
|
+
logger.error('failed to exec blockletDownloader.download', { did: blocklet.meta.did, error });
|
|
872
|
+
}
|
|
864
873
|
} else if (blocklet.status === BlockletStatus.waiting) {
|
|
865
|
-
|
|
874
|
+
try {
|
|
875
|
+
await this.installQueue.cancel(blocklet.meta.did);
|
|
876
|
+
} catch (error) {
|
|
877
|
+
logger.error('failed to cancel waiting', { did: blocklet.meta.did, error });
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
// rollback
|
|
882
|
+
if (job) {
|
|
883
|
+
const { postAction, oldBlocklet } = job;
|
|
884
|
+
await this._rollback(postAction, did, oldBlocklet);
|
|
885
|
+
} else {
|
|
886
|
+
const data = await this._rollbackCache.restore({ did });
|
|
887
|
+
if (data) {
|
|
888
|
+
const { action, oldBlocklet } = data;
|
|
889
|
+
await this._rollback(action, did, oldBlocklet);
|
|
890
|
+
await this._rollbackCache.remove({ did });
|
|
891
|
+
} else {
|
|
892
|
+
throw new Error(`Cannot find rollback data in queue or backup file of blocklet ${inputDid}`);
|
|
893
|
+
}
|
|
866
894
|
}
|
|
867
895
|
|
|
868
896
|
logger.info('cancel download blocklet', { did, name, version, status: fromBlockletStatus(blocklet.status) });
|
|
@@ -870,7 +898,18 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
870
898
|
statusLock.release();
|
|
871
899
|
return blocklet;
|
|
872
900
|
} catch (error) {
|
|
873
|
-
|
|
901
|
+
try {
|
|
902
|
+
// fallback blocklet status to error
|
|
903
|
+
const blocklet = await states.blocklet.getBlocklet(inputDid);
|
|
904
|
+
if (blocklet) {
|
|
905
|
+
await states.blocklet.setBlockletStatus(blocklet.meta.did, BlockletStatus.error);
|
|
906
|
+
}
|
|
907
|
+
statusLock.release();
|
|
908
|
+
} catch (err) {
|
|
909
|
+
statusLock.release();
|
|
910
|
+
logger.error('Failed to fallback blocklet status to error on cancelDownload', { error });
|
|
911
|
+
}
|
|
912
|
+
|
|
874
913
|
throw error;
|
|
875
914
|
}
|
|
876
915
|
}
|
|
@@ -1425,6 +1464,9 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1425
1464
|
|
|
1426
1465
|
this.emit(BlockletEvents.statusChange, newBlocklet);
|
|
1427
1466
|
|
|
1467
|
+
// backup rollback data
|
|
1468
|
+
await this._rollbackCache.backup({ did, action, oldBlocklet });
|
|
1469
|
+
|
|
1428
1470
|
// add to queue
|
|
1429
1471
|
const ticket = this.installQueue.push(
|
|
1430
1472
|
{
|
|
@@ -1722,7 +1764,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1722
1764
|
async onJob(job) {
|
|
1723
1765
|
if (job.entity === 'blocklet') {
|
|
1724
1766
|
if (job.action === 'download') {
|
|
1725
|
-
await this.
|
|
1767
|
+
await this.downloadAndInstall(job);
|
|
1726
1768
|
}
|
|
1727
1769
|
if (job.action === 'restart') {
|
|
1728
1770
|
await this.onRestart(job);
|
|
@@ -1747,33 +1789,35 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1747
1789
|
* @return {*}
|
|
1748
1790
|
* @memberof BlockletManager
|
|
1749
1791
|
*/
|
|
1750
|
-
async
|
|
1751
|
-
const { blocklet, context, postAction, oldBlocklet, throwOnError } = params;
|
|
1792
|
+
async downloadAndInstall(params) {
|
|
1793
|
+
const { blocklet, context, postAction, oldBlocklet, throwOnError, skipCheckStatusBeforeDownload } = params;
|
|
1752
1794
|
const { meta } = blocklet;
|
|
1753
1795
|
const { name, did, version } = meta;
|
|
1754
1796
|
|
|
1755
1797
|
// check status
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
if (!b0) {
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1798
|
+
if (!skipCheckStatusBeforeDownload) {
|
|
1799
|
+
try {
|
|
1800
|
+
await statusLock.acquire();
|
|
1801
|
+
|
|
1802
|
+
const b0 = await states.blocklet.getBlocklet(did);
|
|
1803
|
+
if (!b0 || ![BlockletStatus.waiting].includes(b0.status)) {
|
|
1804
|
+
if (!b0) {
|
|
1805
|
+
throw new Error('blocklet does not exist before downloading');
|
|
1806
|
+
} else {
|
|
1807
|
+
throw new Error(`blocklet status is invalid before downloading: ${fromBlockletStatus(b0.status)}`);
|
|
1808
|
+
}
|
|
1765
1809
|
}
|
|
1810
|
+
statusLock.release();
|
|
1811
|
+
} catch (error) {
|
|
1812
|
+
statusLock.release();
|
|
1813
|
+
logger.error('Check blocklet status failed before downloading', {
|
|
1814
|
+
name,
|
|
1815
|
+
did,
|
|
1816
|
+
error,
|
|
1817
|
+
});
|
|
1818
|
+
await this._rollback(postAction, did, oldBlocklet);
|
|
1819
|
+
return;
|
|
1766
1820
|
}
|
|
1767
|
-
statusLock.release();
|
|
1768
|
-
} catch (error) {
|
|
1769
|
-
statusLock.release();
|
|
1770
|
-
logger.error('Check blocklet status failed before downloading', {
|
|
1771
|
-
name,
|
|
1772
|
-
did,
|
|
1773
|
-
error,
|
|
1774
|
-
});
|
|
1775
|
-
await this._rollback(postAction, did, oldBlocklet);
|
|
1776
|
-
return;
|
|
1777
1821
|
}
|
|
1778
1822
|
|
|
1779
1823
|
// download bundle
|
|
@@ -1783,8 +1827,16 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1783
1827
|
if (isCancelled) {
|
|
1784
1828
|
logger.info('Download was canceled', { name, did, version });
|
|
1785
1829
|
|
|
1786
|
-
|
|
1787
|
-
|
|
1830
|
+
// rollback on download cancelled
|
|
1831
|
+
await statusLock.acquire();
|
|
1832
|
+
try {
|
|
1833
|
+
if ((await states.blocklet.getBlockletStatus(did)) === BlockletStatus.downloading) {
|
|
1834
|
+
await this._rollback(postAction, did, oldBlocklet);
|
|
1835
|
+
}
|
|
1836
|
+
statusLock.release();
|
|
1837
|
+
} catch (error) {
|
|
1838
|
+
statusLock.release();
|
|
1839
|
+
logger.error('Rollback blocklet failed on download canceled', { postAction, name, did, version, error });
|
|
1788
1840
|
}
|
|
1789
1841
|
return;
|
|
1790
1842
|
}
|
|
@@ -1805,10 +1857,16 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1805
1857
|
severity: 'error',
|
|
1806
1858
|
});
|
|
1807
1859
|
|
|
1860
|
+
// rollback on download failed
|
|
1861
|
+
await statusLock.acquire();
|
|
1808
1862
|
try {
|
|
1809
|
-
await
|
|
1810
|
-
|
|
1811
|
-
|
|
1863
|
+
if ((await states.blocklet.getBlockletStatus(did)) === BlockletStatus.downloading) {
|
|
1864
|
+
await this._rollback(postAction, did, oldBlocklet);
|
|
1865
|
+
}
|
|
1866
|
+
statusLock.release();
|
|
1867
|
+
} catch (error) {
|
|
1868
|
+
statusLock.release();
|
|
1869
|
+
logger.error('Rollback blocklet failed on download failed', { postAction, name, did, version, error });
|
|
1812
1870
|
}
|
|
1813
1871
|
|
|
1814
1872
|
if (throwOnError) {
|
|
@@ -2336,13 +2394,20 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2336
2394
|
newBlocklet.deployedFrom = `Upload by ${context.user.fullName}`;
|
|
2337
2395
|
newBlocklet.children = await this._getChildrenForInstallation(meta);
|
|
2338
2396
|
await validateBlocklet(newBlocklet);
|
|
2339
|
-
await this._downloadBlocklet(newBlocklet);
|
|
2340
2397
|
|
|
2341
|
-
|
|
2398
|
+
// backup rollback data
|
|
2399
|
+
const action = 'upgrade';
|
|
2400
|
+
await this._rollbackCache.backup({ did: newBlocklet.meta.did, action, oldBlocklet });
|
|
2401
|
+
|
|
2402
|
+
await this.downloadAndInstall({
|
|
2403
|
+
blocklet: newBlocklet,
|
|
2342
2404
|
oldBlocklet,
|
|
2343
|
-
newBlocklet,
|
|
2344
2405
|
context: { ...context, forceStartProcessIds: [getComponentProcessId(newBlocklet)] },
|
|
2406
|
+
throwOnError: true,
|
|
2407
|
+
postAction: action,
|
|
2408
|
+
skipCheckStatusBeforeDownload: true,
|
|
2345
2409
|
});
|
|
2410
|
+
return this.getBlocklet(newBlocklet.meta.did);
|
|
2346
2411
|
}
|
|
2347
2412
|
|
|
2348
2413
|
// full deploy
|
|
@@ -2362,14 +2427,20 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2362
2427
|
newBlocklet.deployedFrom = `Upload by ${context.user.fullName}`;
|
|
2363
2428
|
newBlocklet.children = await this._getChildrenForInstallation(meta);
|
|
2364
2429
|
|
|
2365
|
-
|
|
2366
|
-
|
|
2430
|
+
// backup rollback data
|
|
2431
|
+
const action = 'upgrade';
|
|
2432
|
+
await this._rollbackCache.backup({ did: newBlocklet.meta.did, action, oldBlocklet });
|
|
2367
2433
|
|
|
2368
|
-
|
|
2434
|
+
await validateBlocklet(newBlocklet);
|
|
2435
|
+
await this.downloadAndInstall({
|
|
2436
|
+
blocklet: newBlocklet,
|
|
2369
2437
|
oldBlocklet,
|
|
2370
|
-
newBlocklet,
|
|
2371
2438
|
context: { ...context, forceStartProcessIds: [getComponentProcessId(newBlocklet)] },
|
|
2439
|
+
throwOnError: true,
|
|
2440
|
+
postAction: action,
|
|
2441
|
+
skipCheckStatusBeforeDownload: true,
|
|
2372
2442
|
});
|
|
2443
|
+
return this.getBlocklet(newBlocklet.meta.did);
|
|
2373
2444
|
}
|
|
2374
2445
|
|
|
2375
2446
|
// full deploy - install
|
|
@@ -2383,25 +2454,31 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2383
2454
|
children,
|
|
2384
2455
|
});
|
|
2385
2456
|
|
|
2457
|
+
const action = 'install';
|
|
2458
|
+
const oldState = { extraState: oldExtraState };
|
|
2386
2459
|
try {
|
|
2387
2460
|
await this._setConfigsFromMeta(meta.did);
|
|
2388
2461
|
await validateBlocklet(blocklet);
|
|
2389
2462
|
|
|
2390
2463
|
// check duplicate appSk
|
|
2391
2464
|
await checkDuplicateAppSk({ did: meta.did, states });
|
|
2392
|
-
|
|
2393
|
-
// download
|
|
2394
|
-
await this._downloadBlocklet(blocklet);
|
|
2395
|
-
|
|
2396
|
-
return this._installBlocklet({
|
|
2397
|
-
did: meta.did,
|
|
2398
|
-
context,
|
|
2399
|
-
oldBlocklet: { extraState: oldExtraState },
|
|
2400
|
-
});
|
|
2401
2465
|
} catch (error) {
|
|
2402
|
-
await this._rollback(
|
|
2466
|
+
await this._rollback(action, meta.did, oldState);
|
|
2403
2467
|
throw error;
|
|
2404
2468
|
}
|
|
2469
|
+
|
|
2470
|
+
// backup rollback data
|
|
2471
|
+
await this._rollbackCache.backup({ did: meta.did, action, oldBlocklet: oldState });
|
|
2472
|
+
|
|
2473
|
+
await this.downloadAndInstall({
|
|
2474
|
+
blocklet,
|
|
2475
|
+
oldBlocklet: oldState,
|
|
2476
|
+
context,
|
|
2477
|
+
throwOnError: true,
|
|
2478
|
+
postAction: action,
|
|
2479
|
+
skipCheckStatusBeforeDownload: true,
|
|
2480
|
+
});
|
|
2481
|
+
return this.getBlocklet(meta.did);
|
|
2405
2482
|
}
|
|
2406
2483
|
|
|
2407
2484
|
async _installComponentFromUpload({
|
|
@@ -2482,34 +2559,19 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2482
2559
|
|
|
2483
2560
|
await this._upsertDynamicNavigation(newBlocklet.meta.did, newChild, { skipNavigation });
|
|
2484
2561
|
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
await
|
|
2562
|
+
// backup rollback data
|
|
2563
|
+
const action = 'upgrade';
|
|
2564
|
+
await this._rollbackCache.backup({ did: newBlocklet.meta.did, action, oldBlocklet });
|
|
2488
2565
|
|
|
2489
|
-
|
|
2566
|
+
await this.downloadAndInstall({
|
|
2567
|
+
blocklet: newBlocklet,
|
|
2490
2568
|
oldBlocklet,
|
|
2491
|
-
newBlocklet,
|
|
2492
2569
|
context: { ...context, forceStartProcessIds: [getComponentProcessId(newChild, [newBlocklet])] },
|
|
2570
|
+
throwOnError: true,
|
|
2571
|
+
postAction: action,
|
|
2572
|
+
skipCheckStatusBeforeDownload: true,
|
|
2493
2573
|
});
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
/**
|
|
2497
|
-
* add to download job queue
|
|
2498
|
-
* @param {string} did blocklet did
|
|
2499
|
-
* @param {object} blocklet object
|
|
2500
|
-
*/
|
|
2501
|
-
async download(did, blocklet) {
|
|
2502
|
-
await states.blocklet.setBlockletStatus(did, BlockletStatus.waiting);
|
|
2503
|
-
this.installQueue.push(
|
|
2504
|
-
{
|
|
2505
|
-
entity: 'blocklet',
|
|
2506
|
-
action: 'download',
|
|
2507
|
-
id: did,
|
|
2508
|
-
blocklet: { ...blocklet },
|
|
2509
|
-
postAction: 'install',
|
|
2510
|
-
},
|
|
2511
|
-
did
|
|
2512
|
-
);
|
|
2574
|
+
return this.getBlocklet(newBlocklet.meta.did);
|
|
2513
2575
|
}
|
|
2514
2576
|
|
|
2515
2577
|
async prune() {
|
|
@@ -2732,6 +2794,11 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2732
2794
|
const blocklet1 = await states.blocklet.setBlockletStatus(did, BlockletStatus.waiting);
|
|
2733
2795
|
this.emit(BlockletEvents.added, blocklet1);
|
|
2734
2796
|
|
|
2797
|
+
const action = 'install';
|
|
2798
|
+
const oldBlocklet = {
|
|
2799
|
+
extraState: oldExtraState,
|
|
2800
|
+
};
|
|
2801
|
+
|
|
2735
2802
|
/** @type {{
|
|
2736
2803
|
* blocklet: any;
|
|
2737
2804
|
* oldBlocklet: {
|
|
@@ -2743,16 +2810,16 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2743
2810
|
* }} */
|
|
2744
2811
|
const downloadParams = {
|
|
2745
2812
|
blocklet: { ...blocklet1 },
|
|
2746
|
-
oldBlocklet: {
|
|
2747
|
-
children: children.filter((x) => x.dynamic), // let downloader skip re-downloading dynamic blocklet
|
|
2748
|
-
extraState: oldExtraState,
|
|
2749
|
-
},
|
|
2750
2813
|
context,
|
|
2751
|
-
postAction:
|
|
2814
|
+
postAction: action,
|
|
2815
|
+
oldBlocklet,
|
|
2752
2816
|
};
|
|
2753
2817
|
|
|
2818
|
+
// backup rollback data
|
|
2819
|
+
await this._rollbackCache.backup({ did, action, oldBlocklet });
|
|
2820
|
+
|
|
2754
2821
|
if (sync) {
|
|
2755
|
-
await this.
|
|
2822
|
+
await this.downloadAndInstall({ ...downloadParams, throwOnError: true });
|
|
2756
2823
|
return states.blocklet.getBlocklet(did);
|
|
2757
2824
|
}
|
|
2758
2825
|
|
|
@@ -2863,8 +2930,11 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2863
2930
|
postAction: action,
|
|
2864
2931
|
};
|
|
2865
2932
|
|
|
2933
|
+
// backup rollback data
|
|
2934
|
+
await this._rollbackCache.backup({ did, action, oldBlocklet });
|
|
2935
|
+
|
|
2866
2936
|
if (sync) {
|
|
2867
|
-
await this.
|
|
2937
|
+
await this.downloadAndInstall({ ...downloadParams, throwOnError: true });
|
|
2868
2938
|
return states.blocklet.getBlocklet(did);
|
|
2869
2939
|
}
|
|
2870
2940
|
const ticket = this.installQueue.push(
|
|
@@ -2961,6 +3031,8 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2961
3031
|
entityId: did,
|
|
2962
3032
|
severity: 'success',
|
|
2963
3033
|
});
|
|
3034
|
+
|
|
3035
|
+
await this._rollbackCache.remove({ did: blocklet.meta.did });
|
|
2964
3036
|
return blocklet;
|
|
2965
3037
|
} catch (err) {
|
|
2966
3038
|
const { meta } = await states.blocklet.getBlocklet(did);
|
|
@@ -3087,6 +3159,8 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3087
3159
|
// Update dynamic component meta in blocklet settings
|
|
3088
3160
|
await this._ensureDynamicChildrenInSettings(blocklet);
|
|
3089
3161
|
|
|
3162
|
+
await this._rollbackCache.remove({ did: blocklet.meta.did });
|
|
3163
|
+
|
|
3090
3164
|
return blocklet;
|
|
3091
3165
|
} catch (err) {
|
|
3092
3166
|
const b = await this._rollback(action, did, oldBlocklet);
|
|
@@ -3150,9 +3224,11 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3150
3224
|
});
|
|
3151
3225
|
this.emit(BlockletEvents.statusChange, blocklet1);
|
|
3152
3226
|
},
|
|
3153
|
-
postDownload: async () => {
|
|
3154
|
-
|
|
3155
|
-
|
|
3227
|
+
postDownload: async ({ isCancelled }) => {
|
|
3228
|
+
if (!isCancelled) {
|
|
3229
|
+
// since preferences only exist in blocklet bundle, we need to populate then after downloaded
|
|
3230
|
+
await this._setConfigsFromMeta(did);
|
|
3231
|
+
}
|
|
3156
3232
|
},
|
|
3157
3233
|
});
|
|
3158
3234
|
}
|
|
@@ -3170,12 +3246,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3170
3246
|
}
|
|
3171
3247
|
}
|
|
3172
3248
|
|
|
3173
|
-
// eslint-disable-next-line no-unused-vars
|
|
3174
|
-
async _cancelWaiting(blockletMeta, context) {
|
|
3175
|
-
const { did } = blockletMeta;
|
|
3176
|
-
return this.installQueue.cancel(did);
|
|
3177
|
-
}
|
|
3178
|
-
|
|
3179
3249
|
/**
|
|
3180
3250
|
* @param {string} action install, upgrade, downgrade
|
|
3181
3251
|
* @param {string} did
|
|
@@ -3343,7 +3413,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3343
3413
|
}
|
|
3344
3414
|
|
|
3345
3415
|
async _getBlockletForInstallation(did) {
|
|
3346
|
-
const blocklet = await states.blocklet.getBlocklet(did);
|
|
3416
|
+
const blocklet = await states.blocklet.getBlocklet(did, { decryptSk: false });
|
|
3347
3417
|
if (!blocklet) {
|
|
3348
3418
|
return null;
|
|
3349
3419
|
}
|
|
@@ -11,6 +11,14 @@ const logger = require('@abtnode/logger')('@abtnode/core:install-from-backup');
|
|
|
11
11
|
|
|
12
12
|
const { validateBlocklet, checkDuplicateAppSk, getAppDirs } = require('../../../util/blocklet');
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
*
|
|
16
|
+
* @param {{
|
|
17
|
+
* manager: import('../disk'),
|
|
18
|
+
* states: import('../../../states/index')
|
|
19
|
+
* }} param0
|
|
20
|
+
* @returns
|
|
21
|
+
*/
|
|
14
22
|
module.exports = async ({ url, blockletSecretKey, moveDir, context = {}, states, manager } = {}) => {
|
|
15
23
|
// TODO: support more url schema feature (http, did-spaces)
|
|
16
24
|
if (!url.startsWith('file://')) {
|
|
@@ -24,11 +32,7 @@ module.exports = async ({ url, blockletSecretKey, moveDir, context = {}, states,
|
|
|
24
32
|
}
|
|
25
33
|
|
|
26
34
|
// parse data from source dir
|
|
27
|
-
|
|
28
35
|
const srcBundleDirs = await getAppDirs(path.join(dir, 'blocklets'));
|
|
29
|
-
if (!srcBundleDirs.length) {
|
|
30
|
-
throw new Error(`bundle dirs does not found in ${dir}`);
|
|
31
|
-
}
|
|
32
36
|
|
|
33
37
|
const srcDataDir = path.join(dir, 'data');
|
|
34
38
|
|
|
@@ -61,6 +65,7 @@ module.exports = async ({ url, blockletSecretKey, moveDir, context = {}, states,
|
|
|
61
65
|
|
|
62
66
|
// FIXME: meta.did and meta.name should be dynamic created when installing multiple blocklet is supported
|
|
63
67
|
const existState = await states.blocklet.hasBlocklet(did);
|
|
68
|
+
|
|
64
69
|
if (existState) {
|
|
65
70
|
logger.error('blocklet is already exist', { did });
|
|
66
71
|
throw new Error('blocklet is already exist');
|
|
@@ -133,6 +138,8 @@ module.exports = async ({ url, blockletSecretKey, moveDir, context = {}, states,
|
|
|
133
138
|
logger.info(`bundle is ${moveDir ? 'moved' : 'copied'} successfully`, { installDir });
|
|
134
139
|
})
|
|
135
140
|
);
|
|
141
|
+
// 从 store 下载 blocklet
|
|
142
|
+
await manager.blockletDownloader.download(state, { skipCheckIntegrity: true });
|
|
136
143
|
|
|
137
144
|
// FIXME same as copy extra
|
|
138
145
|
const dataDir = path.join(manager.dataDirs.data, appName);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const DIR_NAME = 'rollback-cache';
|
|
5
|
+
const security = require('@abtnode/util/lib/security');
|
|
6
|
+
|
|
7
|
+
class RollbackCache {
|
|
8
|
+
constructor({ dir, dek }) {
|
|
9
|
+
this.dir = dir;
|
|
10
|
+
this.dek = dek;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async backup({ did, action, oldBlocklet }) {
|
|
14
|
+
const file = this._getFile(did);
|
|
15
|
+
const data = this.dek ? security.encrypt(JSON.stringify(oldBlocklet), did, this.dek) : oldBlocklet;
|
|
16
|
+
await fs.outputJSON(file, { action, oldBlocklet: data });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async remove({ did }) {
|
|
20
|
+
const file = this._getFile(did);
|
|
21
|
+
await fs.remove(file);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async restore({ did }) {
|
|
25
|
+
const file = this._getFile(did);
|
|
26
|
+
if (fs.existsSync(file)) {
|
|
27
|
+
const data = await fs.readJSON(file);
|
|
28
|
+
if (data?.oldBlocklet && this.dek) {
|
|
29
|
+
data.oldBlocklet = JSON.parse(security.decrypt(data.oldBlocklet, did, this.dek));
|
|
30
|
+
}
|
|
31
|
+
return data;
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
_getFile(did) {
|
|
37
|
+
return path.join(this.dir, DIR_NAME, `${did}.json`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = RollbackCache;
|
|
@@ -75,6 +75,10 @@ class BlockletExtrasBackup extends BaseBackup {
|
|
|
75
75
|
* @memberof BlockletExtrasBackup
|
|
76
76
|
*/
|
|
77
77
|
useBlockletEncryptConfigs(configs) {
|
|
78
|
+
if (isEmpty(configs)) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
78
82
|
// 准备加解密所需的参数
|
|
79
83
|
// @see: https://github.com/ArcBlock/blocklet-server/blob/f561ba7290285f2e23dccb6d5323eb4d43c3cc3e/core/state/lib/index.js#L59
|
|
80
84
|
const dek = readFileSync(join(this.serverDataDir, '.sock'));
|