@strapi/data-transfer 5.10.3 → 5.11.0
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/dist/engine/index.d.ts.map +1 -1
- package/dist/index.js +191 -106
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +195 -110
- package/dist/index.mjs.map +1 -1
- package/dist/strapi/providers/local-source/index.d.ts.map +1 -1
- package/dist/strapi/providers/remote-source/index.d.ts +2 -0
- package/dist/strapi/providers/remote-source/index.d.ts.map +1 -1
- package/dist/strapi/queries/link.d.ts.map +1 -1
- package/dist/strapi/remote/handlers/pull.d.ts.map +1 -1
- package/package.json +6 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/engine/index.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAiC,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/engine/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var stream$1 = require('stream');
|
|
4
|
+
var promises = require('stream/promises');
|
|
4
5
|
var path = require('path');
|
|
5
6
|
var os = require('os');
|
|
6
7
|
var streamChain = require('stream-chain');
|
|
@@ -654,8 +655,7 @@ const TRANSFER_STAGES = Object.freeze([
|
|
|
654
655
|
};
|
|
655
656
|
const DEFAULT_VERSION_STRATEGY = 'ignore';
|
|
656
657
|
const DEFAULT_SCHEMA_STRATEGY = 'strict';
|
|
657
|
-
var _metadata$1 = /*#__PURE__*/ _class_private_field_loose_key$6("_metadata"), _schema = /*#__PURE__*/ _class_private_field_loose_key$6("_schema"), _handlers = /*#__PURE__*/ _class_private_field_loose_key$6("_handlers"),
|
|
658
|
-
_currentStream = /*#__PURE__*/ _class_private_field_loose_key$6("_currentStream"), /**
|
|
658
|
+
var _metadata$1 = /*#__PURE__*/ _class_private_field_loose_key$6("_metadata"), _schema = /*#__PURE__*/ _class_private_field_loose_key$6("_schema"), _handlers = /*#__PURE__*/ _class_private_field_loose_key$6("_handlers"), _currentStreamController = /*#__PURE__*/ _class_private_field_loose_key$6("_currentStreamController"), _aborted = /*#__PURE__*/ _class_private_field_loose_key$6("_aborted"), /**
|
|
659
659
|
* Create and return a transform stream based on the given stage and options.
|
|
660
660
|
*
|
|
661
661
|
* Allowed transformations includes 'filter' and 'map'.
|
|
@@ -770,11 +770,9 @@ class TransferEngine {
|
|
|
770
770
|
}
|
|
771
771
|
// Cause an ongoing transfer to abort gracefully
|
|
772
772
|
async abortTransfer() {
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
}
|
|
777
|
-
_class_private_field_loose_base$6(this, _currentStream)[_currentStream].destroy(err);
|
|
773
|
+
_class_private_field_loose_base$6(this, _aborted)[_aborted] = true;
|
|
774
|
+
_class_private_field_loose_base$6(this, _currentStreamController)[_currentStreamController]?.abort();
|
|
775
|
+
throw new TransferEngineError('fatal', 'Transfer aborted.');
|
|
778
776
|
}
|
|
779
777
|
async init() {
|
|
780
778
|
// Resolve providers' resource and store
|
|
@@ -1084,7 +1082,11 @@ class TransferEngine {
|
|
|
1084
1082
|
writable: true,
|
|
1085
1083
|
value: void 0
|
|
1086
1084
|
});
|
|
1087
|
-
Object.defineProperty(this,
|
|
1085
|
+
Object.defineProperty(this, _currentStreamController, {
|
|
1086
|
+
writable: true,
|
|
1087
|
+
value: void 0
|
|
1088
|
+
});
|
|
1089
|
+
Object.defineProperty(this, _aborted, {
|
|
1088
1090
|
writable: true,
|
|
1089
1091
|
value: void 0
|
|
1090
1092
|
});
|
|
@@ -1094,6 +1096,7 @@ class TransferEngine {
|
|
|
1094
1096
|
schemaDiff: [],
|
|
1095
1097
|
errors: {}
|
|
1096
1098
|
};
|
|
1099
|
+
_class_private_field_loose_base$6(this, _aborted)[_aborted] = false;
|
|
1097
1100
|
this.diagnostics = createDiagnosticReporter();
|
|
1098
1101
|
validateProvider('source', sourceProvider);
|
|
1099
1102
|
validateProvider('destination', destinationProvider);
|
|
@@ -1292,6 +1295,9 @@ function assertSchemasMatching(sourceSchemas, destinationSchemas) {
|
|
|
1292
1295
|
}
|
|
1293
1296
|
}
|
|
1294
1297
|
async function transferStage(options) {
|
|
1298
|
+
if (_class_private_field_loose_base$6(this, _aborted)[_aborted]) {
|
|
1299
|
+
throw new TransferEngineError('fatal', 'Transfer aborted.');
|
|
1300
|
+
}
|
|
1295
1301
|
const { stage, source, destination, transform, tracker } = options;
|
|
1296
1302
|
const updateEndTime = ()=>{
|
|
1297
1303
|
const stageData = this.progress.data[stage];
|
|
@@ -1323,27 +1329,42 @@ async function transferStage(options) {
|
|
|
1323
1329
|
return;
|
|
1324
1330
|
}
|
|
1325
1331
|
_class_private_field_loose_base$6(this, _emitStageUpdate)[_emitStageUpdate]('start', stage);
|
|
1326
|
-
|
|
1327
|
-
|
|
1332
|
+
try {
|
|
1333
|
+
const streams = [
|
|
1334
|
+
source
|
|
1335
|
+
];
|
|
1328
1336
|
if (transform) {
|
|
1329
|
-
|
|
1337
|
+
streams.push(transform);
|
|
1330
1338
|
}
|
|
1331
1339
|
if (tracker) {
|
|
1332
|
-
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1340
|
+
streams.push(tracker);
|
|
1341
|
+
}
|
|
1342
|
+
streams.push(destination);
|
|
1343
|
+
// NOTE: to debug/confirm backpressure issues from misbehaving stream, uncomment the following lines
|
|
1344
|
+
// source.on('pause', () => console.log(`[${stage}] Source paused due to backpressure`));
|
|
1345
|
+
// source.on('resume', () => console.log(`[${stage}] Source resumed`));
|
|
1346
|
+
// destination.on('drain', () =>
|
|
1347
|
+
// console.log(`[${stage}] Destination drained, resuming data flow`)
|
|
1348
|
+
// );
|
|
1349
|
+
// destination.on('error', (err) => console.error(`[${stage}] Destination error:`, err));
|
|
1350
|
+
const controller = new AbortController();
|
|
1351
|
+
const { signal } = controller;
|
|
1352
|
+
// Store the controller so you can cancel later
|
|
1353
|
+
_class_private_field_loose_base$6(this, _currentStreamController)[_currentStreamController] = controller;
|
|
1354
|
+
await promises.pipeline(streams, {
|
|
1355
|
+
signal
|
|
1344
1356
|
});
|
|
1345
|
-
|
|
1346
|
-
|
|
1357
|
+
_class_private_field_loose_base$6(this, _emitStageUpdate)[_emitStageUpdate]('finish', stage);
|
|
1358
|
+
} catch (e) {
|
|
1359
|
+
updateEndTime();
|
|
1360
|
+
_class_private_field_loose_base$6(this, _emitStageUpdate)[_emitStageUpdate]('error', stage);
|
|
1361
|
+
this.reportError(e, 'error');
|
|
1362
|
+
if (!destination.destroyed) {
|
|
1363
|
+
destination.destroy(e);
|
|
1364
|
+
}
|
|
1365
|
+
} finally{
|
|
1366
|
+
updateEndTime();
|
|
1367
|
+
}
|
|
1347
1368
|
}
|
|
1348
1369
|
async function resolveProviderResource() {
|
|
1349
1370
|
const sourceMetadata = await this.sourceProvider.getMetadata();
|
|
@@ -1915,9 +1936,7 @@ const createLinkQuery = (strapi, trx)=>{
|
|
|
1915
1936
|
assignOrderColumns();
|
|
1916
1937
|
const qb = connection.insert(payload).into(addSchema(joinTable.name));
|
|
1917
1938
|
if (trx) {
|
|
1918
|
-
await
|
|
1919
|
-
await qb.transacting(nestedTrx);
|
|
1920
|
-
});
|
|
1939
|
+
await qb.transacting(trx);
|
|
1921
1940
|
}
|
|
1922
1941
|
}
|
|
1923
1942
|
if ('morphColumn' in attribute && attribute.morphColumn) {
|
|
@@ -2370,7 +2389,7 @@ class LocalStrapiDestinationProvider {
|
|
|
2370
2389
|
const provider = strapi.config.get('plugin::upload').provider;
|
|
2371
2390
|
const fileId = fileEntitiesMapper?.[uploadData.id];
|
|
2372
2391
|
if (!fileId) {
|
|
2373
|
-
callback(new Error(`File ID not found for ID: ${uploadData.id}`));
|
|
2392
|
+
return callback(new Error(`File ID not found for ID: ${uploadData.id}`));
|
|
2374
2393
|
}
|
|
2375
2394
|
try {
|
|
2376
2395
|
await strapi.plugin('upload').provider.uploadStream(uploadData);
|
|
@@ -2421,9 +2440,9 @@ class LocalStrapiDestinationProvider {
|
|
|
2421
2440
|
provider
|
|
2422
2441
|
}
|
|
2423
2442
|
});
|
|
2424
|
-
callback();
|
|
2443
|
+
return callback();
|
|
2425
2444
|
} catch (error) {
|
|
2426
|
-
callback(new Error(`Error while uploading asset ${chunk.filename} ${error}`));
|
|
2445
|
+
return callback(new Error(`Error while uploading asset ${chunk.filename} ${error}`));
|
|
2427
2446
|
}
|
|
2428
2447
|
});
|
|
2429
2448
|
}
|
|
@@ -2845,7 +2864,11 @@ function _class_private_field_loose_key$4(name) {
|
|
|
2845
2864
|
const createLocalStrapiSourceProvider = (options)=>{
|
|
2846
2865
|
return new LocalStrapiSourceProvider(options);
|
|
2847
2866
|
};
|
|
2848
|
-
var _diagnostics$4 = /*#__PURE__*/ _class_private_field_loose_key$4("_diagnostics"), _reportInfo$4 = /*#__PURE__*/ _class_private_field_loose_key$4("_reportInfo")
|
|
2867
|
+
var _diagnostics$4 = /*#__PURE__*/ _class_private_field_loose_key$4("_diagnostics"), _reportInfo$4 = /*#__PURE__*/ _class_private_field_loose_key$4("_reportInfo"), /**
|
|
2868
|
+
* Reports an error to the diagnostic reporter.
|
|
2869
|
+
*/ _reportError = /*#__PURE__*/ _class_private_field_loose_key$4("_reportError"), /**
|
|
2870
|
+
* Handles errors that occur in read streams.
|
|
2871
|
+
*/ _handleStreamError = /*#__PURE__*/ _class_private_field_loose_key$4("_handleStreamError");
|
|
2849
2872
|
class LocalStrapiSourceProvider {
|
|
2850
2873
|
async bootstrap(diagnostics) {
|
|
2851
2874
|
_class_private_field_loose_base$4(this, _diagnostics$4)[_diagnostics$4] = diagnostics;
|
|
@@ -2907,12 +2930,22 @@ class LocalStrapiSourceProvider {
|
|
|
2907
2930
|
createAssetsReadStream() {
|
|
2908
2931
|
assertValidStrapi(this.strapi, 'Not able to stream assets');
|
|
2909
2932
|
_class_private_field_loose_base$4(this, _reportInfo$4)[_reportInfo$4]('creating assets read stream');
|
|
2910
|
-
|
|
2933
|
+
const stream = createAssetsStream(this.strapi);
|
|
2934
|
+
stream.on('error', (err)=>{
|
|
2935
|
+
_class_private_field_loose_base$4(this, _handleStreamError)[_handleStreamError]('assets', err);
|
|
2936
|
+
});
|
|
2937
|
+
return stream;
|
|
2911
2938
|
}
|
|
2912
2939
|
constructor(options){
|
|
2913
2940
|
Object.defineProperty(this, _reportInfo$4, {
|
|
2914
2941
|
value: reportInfo$4
|
|
2915
2942
|
});
|
|
2943
|
+
Object.defineProperty(this, _reportError, {
|
|
2944
|
+
value: reportError
|
|
2945
|
+
});
|
|
2946
|
+
Object.defineProperty(this, _handleStreamError, {
|
|
2947
|
+
value: handleStreamError
|
|
2948
|
+
});
|
|
2916
2949
|
Object.defineProperty(this, _diagnostics$4, {
|
|
2917
2950
|
writable: true,
|
|
2918
2951
|
value: void 0
|
|
@@ -2932,6 +2965,29 @@ function reportInfo$4(message) {
|
|
|
2932
2965
|
kind: 'info'
|
|
2933
2966
|
});
|
|
2934
2967
|
}
|
|
2968
|
+
function reportError(message, error) {
|
|
2969
|
+
_class_private_field_loose_base$4(this, _diagnostics$4)[_diagnostics$4]?.report({
|
|
2970
|
+
details: {
|
|
2971
|
+
createdAt: new Date(),
|
|
2972
|
+
message,
|
|
2973
|
+
error,
|
|
2974
|
+
severity: 'fatal',
|
|
2975
|
+
name: error.name
|
|
2976
|
+
},
|
|
2977
|
+
kind: 'error'
|
|
2978
|
+
});
|
|
2979
|
+
}
|
|
2980
|
+
function handleStreamError(streamType, err) {
|
|
2981
|
+
const { message, stack } = err;
|
|
2982
|
+
const errorMessage = `[Data transfer] Error in ${streamType} read stream: ${message}`;
|
|
2983
|
+
const formattedError = {
|
|
2984
|
+
message: errorMessage,
|
|
2985
|
+
stack,
|
|
2986
|
+
timestamp: new Date().toISOString()
|
|
2987
|
+
};
|
|
2988
|
+
this.strapi?.log.error(formattedError);
|
|
2989
|
+
_class_private_field_loose_base$4(this, _reportError)[_reportError](formattedError.message, err);
|
|
2990
|
+
}
|
|
2935
2991
|
|
|
2936
2992
|
const createDispatcher = (ws, retryMessageOptions = {
|
|
2937
2993
|
retryMessageMaxRetries: 5,
|
|
@@ -3095,17 +3151,6 @@ const connectToWebsocket = (address, options, diagnostics)=>{
|
|
|
3095
3151
|
const trimTrailingSlash = (input)=>{
|
|
3096
3152
|
return input.replace(/\/$/, '');
|
|
3097
3153
|
};
|
|
3098
|
-
const wait = (ms)=>{
|
|
3099
|
-
return new Promise((resolve)=>{
|
|
3100
|
-
setTimeout(resolve, ms);
|
|
3101
|
-
});
|
|
3102
|
-
};
|
|
3103
|
-
const waitUntil = async (test, interval)=>{
|
|
3104
|
-
while(!test()){
|
|
3105
|
-
await wait(interval);
|
|
3106
|
-
}
|
|
3107
|
-
return Promise.resolve();
|
|
3108
|
-
};
|
|
3109
3154
|
|
|
3110
3155
|
const TRANSFER_PATH = '/transfer/runner';
|
|
3111
3156
|
const TRANSFER_METHODS = [
|
|
@@ -3526,6 +3571,17 @@ class RemoteStrapiSourceProvider {
|
|
|
3526
3571
|
});
|
|
3527
3572
|
// Init the asset map
|
|
3528
3573
|
const assets = {};
|
|
3574
|
+
// Watch for stalled assets; if we don't receive a chunk within timeout, abort transfer
|
|
3575
|
+
const resetTimeout = (assetID)=>{
|
|
3576
|
+
if (assets[assetID].timeout) {
|
|
3577
|
+
clearTimeout(assets[assetID].timeout);
|
|
3578
|
+
}
|
|
3579
|
+
assets[assetID].timeout = setTimeout(()=>{
|
|
3580
|
+
_class_private_field_loose_base$2(this, _reportInfo$2)[_reportInfo$2](`Asset ${assetID} transfer stalled, aborting.`);
|
|
3581
|
+
assets[assetID].status = 'errored';
|
|
3582
|
+
assets[assetID].stream.destroy(new Error(`Asset ${assetID} transfer timed out`));
|
|
3583
|
+
}, this.options.streamTimeout);
|
|
3584
|
+
};
|
|
3529
3585
|
stream/**
|
|
3530
3586
|
* Process a payload of many transfer assets and performs the following tasks:
|
|
3531
3587
|
* - Start: creates a stream for new assets.
|
|
@@ -3536,56 +3592,46 @@ class RemoteStrapiSourceProvider {
|
|
|
3536
3592
|
const { action, assetID } = item;
|
|
3537
3593
|
// Creates the stream to send the incoming asset through
|
|
3538
3594
|
if (action === 'start') {
|
|
3539
|
-
//
|
|
3595
|
+
// if a transfer has already been started for the same asset ID, something is wrong
|
|
3540
3596
|
if (assets[assetID]) {
|
|
3541
|
-
|
|
3597
|
+
throw new Error(`Asset ${assetID} already started`);
|
|
3542
3598
|
}
|
|
3599
|
+
_class_private_field_loose_base$2(this, _reportInfo$2)[_reportInfo$2](`Asset ${assetID} starting`);
|
|
3543
3600
|
// Register the asset
|
|
3544
3601
|
assets[assetID] = {
|
|
3545
3602
|
...item.data,
|
|
3546
3603
|
stream: new stream$1.PassThrough(),
|
|
3547
|
-
status: '
|
|
3604
|
+
status: 'ok',
|
|
3548
3605
|
queue: []
|
|
3549
3606
|
};
|
|
3607
|
+
resetTimeout(assetID);
|
|
3550
3608
|
// Connect the individual asset stream to the main asset stage stream
|
|
3551
3609
|
// Note: nothing is transferred until data chunks are fed to the asset stream
|
|
3552
3610
|
await this.writeAsync(pass, assets[assetID]);
|
|
3553
|
-
} else if (action === 'stream') {
|
|
3554
|
-
// If the asset hasn't been registered, or if it's been closed already,
|
|
3611
|
+
} else if (action === 'stream' || action === 'end') {
|
|
3612
|
+
// If the asset hasn't been registered, or if it's been closed already, something is wrong
|
|
3555
3613
|
if (!assets[assetID]) {
|
|
3556
|
-
|
|
3614
|
+
throw new Error(`No id matching ${assetID} for stream action`);
|
|
3557
3615
|
}
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
// The resource is busy, queue the current chunk so that it gets transferred as soon as possible
|
|
3564
|
-
case 'busy':
|
|
3565
|
-
assets[assetID].queue.push(item);
|
|
3566
|
-
break;
|
|
3616
|
+
// On every action, reset the timeout timer
|
|
3617
|
+
if (action === 'stream') {
|
|
3618
|
+
resetTimeout(assetID);
|
|
3619
|
+
} else {
|
|
3620
|
+
clearTimeout(assets[assetID].timeout);
|
|
3567
3621
|
}
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
if (!assets[assetID]) {
|
|
3571
|
-
continue;
|
|
3622
|
+
if (assets[assetID].status === 'closed') {
|
|
3623
|
+
throw new Error(`Asset ${assetID} is closed`);
|
|
3572
3624
|
}
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
waitUntil(()=>assets[assetID].status !== 'busy', 100),
|
|
3584
|
-
// Or: if the last chunks are still not processed after ten seconds
|
|
3585
|
-
wait(10000)
|
|
3586
|
-
]);
|
|
3587
|
-
await closeAssetStream(assetID);
|
|
3588
|
-
break;
|
|
3625
|
+
assets[assetID].queue.push(item);
|
|
3626
|
+
}
|
|
3627
|
+
}
|
|
3628
|
+
// each new payload will start new processQueue calls, which may cause some extra calls
|
|
3629
|
+
// it's essentially saying "start processing this asset again, I added more data to the queue"
|
|
3630
|
+
for(const assetID in assets){
|
|
3631
|
+
if (Object.prototype.hasOwnProperty.call(assets, assetID)) {
|
|
3632
|
+
const asset = assets[assetID];
|
|
3633
|
+
if (asset.queue?.length > 0) {
|
|
3634
|
+
await processQueue(assetID);
|
|
3589
3635
|
}
|
|
3590
3636
|
}
|
|
3591
3637
|
}
|
|
@@ -3593,38 +3639,48 @@ class RemoteStrapiSourceProvider {
|
|
|
3593
3639
|
pass.end();
|
|
3594
3640
|
});
|
|
3595
3641
|
/**
|
|
3596
|
-
*
|
|
3597
|
-
|
|
3642
|
+
* Start processing the queue for a given assetID
|
|
3643
|
+
*
|
|
3644
|
+
* Even though this is a loop that attempts to process the entire queue, it is safe to call this more than once
|
|
3645
|
+
* for the same asset id because the queue is shared globally, the items are shifted off, and immediately written
|
|
3646
|
+
*/ const processQueue = async (id)=>{
|
|
3598
3647
|
if (!assets[id]) {
|
|
3599
3648
|
throw new Error(`Failed to write asset chunk for "${id}". Asset not found.`);
|
|
3600
3649
|
}
|
|
3601
|
-
const
|
|
3602
|
-
|
|
3650
|
+
const asset = assets[id];
|
|
3651
|
+
const { status: currentStatus } = asset;
|
|
3652
|
+
if ([
|
|
3653
|
+
'closed',
|
|
3654
|
+
'errored'
|
|
3655
|
+
].includes(currentStatus)) {
|
|
3603
3656
|
throw new Error(`Failed to write asset chunk for "${id}". The asset is currently "${currentStatus}"`);
|
|
3604
3657
|
}
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3658
|
+
while(asset.queue.length > 0){
|
|
3659
|
+
const data = asset.queue.shift();
|
|
3660
|
+
if (!data) {
|
|
3661
|
+
throw new Error(`Invalid chunk found for ${id}`);
|
|
3662
|
+
}
|
|
3663
|
+
try {
|
|
3664
|
+
// if this is an end chunk, close the asset stream
|
|
3665
|
+
if (data.action === 'end') {
|
|
3666
|
+
_class_private_field_loose_base$2(this, _reportInfo$2)[_reportInfo$2](`Ending asset stream for ${id}`);
|
|
3667
|
+
await closeAssetStream(id);
|
|
3668
|
+
break; // Exit the loop after closing the stream
|
|
3669
|
+
}
|
|
3670
|
+
// Save the current chunk
|
|
3671
|
+
await writeChunkToStream(id, data);
|
|
3672
|
+
} catch {
|
|
3673
|
+
if (!assets[id]) {
|
|
3674
|
+
throw new Error(`No id matching ${id} for writeAssetChunk`);
|
|
3675
|
+
}
|
|
3616
3676
|
}
|
|
3617
|
-
// Unlock the asset
|
|
3618
|
-
assets[id].status = 'idle';
|
|
3619
|
-
} catch {
|
|
3620
|
-
assets[id].status = 'errored';
|
|
3621
3677
|
}
|
|
3622
3678
|
};
|
|
3623
3679
|
/**
|
|
3624
3680
|
* Writes a chunk of data to the asset's stream.
|
|
3625
3681
|
*
|
|
3626
3682
|
* Only check if the targeted asset exists, no other validation is done.
|
|
3627
|
-
*/ const
|
|
3683
|
+
*/ const writeChunkToStream = async (id, data)=>{
|
|
3628
3684
|
const asset = assets[id];
|
|
3629
3685
|
if (!asset) {
|
|
3630
3686
|
throw new Error(`Failed to write asset chunk for "${id}". Asset not found.`);
|
|
@@ -3645,9 +3701,11 @@ class RemoteStrapiSourceProvider {
|
|
|
3645
3701
|
await new Promise((resolve, reject)=>{
|
|
3646
3702
|
const { stream } = assets[id];
|
|
3647
3703
|
stream.on('close', ()=>{
|
|
3648
|
-
delete assets[id];
|
|
3649
3704
|
resolve();
|
|
3650
|
-
}).on('error',
|
|
3705
|
+
}).on('error', (e)=>{
|
|
3706
|
+
assets[id].status = 'errored';
|
|
3707
|
+
reject(new Error(`Failed to close asset "${id}". Asset stream error: ${e.toString()}`));
|
|
3708
|
+
}).end();
|
|
3651
3709
|
});
|
|
3652
3710
|
};
|
|
3653
3711
|
return pass;
|
|
@@ -3762,6 +3820,9 @@ class RemoteStrapiSourceProvider {
|
|
|
3762
3820
|
});
|
|
3763
3821
|
this.name = 'source::remote-strapi';
|
|
3764
3822
|
this.type = 'source';
|
|
3823
|
+
this.defaultOptions = {
|
|
3824
|
+
streamTimeout: 15000
|
|
3825
|
+
};
|
|
3765
3826
|
this.writeAsync = (stream, data)=>{
|
|
3766
3827
|
return new Promise((resolve, reject)=>{
|
|
3767
3828
|
stream.write(data, (error)=>{
|
|
@@ -3772,7 +3833,10 @@ class RemoteStrapiSourceProvider {
|
|
|
3772
3833
|
});
|
|
3773
3834
|
});
|
|
3774
3835
|
};
|
|
3775
|
-
this.options =
|
|
3836
|
+
this.options = {
|
|
3837
|
+
...this.defaultOptions,
|
|
3838
|
+
...options
|
|
3839
|
+
};
|
|
3776
3840
|
this.ws = null;
|
|
3777
3841
|
this.dispatcher = null;
|
|
3778
3842
|
}
|
|
@@ -4705,6 +4769,18 @@ const createPullController = handlerControllerFactory((proto)=>({
|
|
|
4705
4769
|
kind: 'warning'
|
|
4706
4770
|
});
|
|
4707
4771
|
},
|
|
4772
|
+
onError (error) {
|
|
4773
|
+
this.diagnostics?.report({
|
|
4774
|
+
details: {
|
|
4775
|
+
message: error.message,
|
|
4776
|
+
error,
|
|
4777
|
+
createdAt: new Date(),
|
|
4778
|
+
name: error.name,
|
|
4779
|
+
severity: 'fatal'
|
|
4780
|
+
},
|
|
4781
|
+
kind: 'error'
|
|
4782
|
+
});
|
|
4783
|
+
},
|
|
4708
4784
|
assertValidTransferAction (action) {
|
|
4709
4785
|
// Abstract the constant to string[] to allow looser check on the given action
|
|
4710
4786
|
const validActions = VALID_TRANSFER_ACTIONS;
|
|
@@ -4778,6 +4854,15 @@ const createPullController = handlerControllerFactory((proto)=>({
|
|
|
4778
4854
|
let batch = [];
|
|
4779
4855
|
const stream = this.streams?.[stage];
|
|
4780
4856
|
const batchLength = ()=>Buffer.byteLength(JSON.stringify(batch));
|
|
4857
|
+
const maybeConfirm = async (data)=>{
|
|
4858
|
+
try {
|
|
4859
|
+
await this.confirm(data);
|
|
4860
|
+
} catch (error) {
|
|
4861
|
+
// Handle the error, log it, or take other appropriate actions
|
|
4862
|
+
strapi?.log.error(`[Data transfer] Message confirmation failed: ${error?.message}`);
|
|
4863
|
+
this.onError(error);
|
|
4864
|
+
}
|
|
4865
|
+
};
|
|
4781
4866
|
const sendBatch = async ()=>{
|
|
4782
4867
|
await this.confirm({
|
|
4783
4868
|
type: 'transfer',
|
|
@@ -4786,6 +4871,7 @@ const createPullController = handlerControllerFactory((proto)=>({
|
|
|
4786
4871
|
error: null,
|
|
4787
4872
|
id
|
|
4788
4873
|
});
|
|
4874
|
+
batch = [];
|
|
4789
4875
|
};
|
|
4790
4876
|
if (!stream) {
|
|
4791
4877
|
throw new ProviderTransferError(`No available stream found for ${stage}`);
|
|
@@ -4796,7 +4882,6 @@ const createPullController = handlerControllerFactory((proto)=>({
|
|
|
4796
4882
|
batch.push(chunk);
|
|
4797
4883
|
if (batchLength() >= batchSize) {
|
|
4798
4884
|
await sendBatch();
|
|
4799
|
-
batch = [];
|
|
4800
4885
|
}
|
|
4801
4886
|
} else {
|
|
4802
4887
|
await this.confirm({
|
|
@@ -4812,7 +4897,6 @@ const createPullController = handlerControllerFactory((proto)=>({
|
|
|
4812
4897
|
}
|
|
4813
4898
|
if (batch.length > 0 && stage !== 'assets') {
|
|
4814
4899
|
await sendBatch();
|
|
4815
|
-
batch = [];
|
|
4816
4900
|
}
|
|
4817
4901
|
await this.confirm({
|
|
4818
4902
|
type: 'transfer',
|
|
@@ -4822,7 +4906,8 @@ const createPullController = handlerControllerFactory((proto)=>({
|
|
|
4822
4906
|
id
|
|
4823
4907
|
});
|
|
4824
4908
|
} catch (e) {
|
|
4825
|
-
|
|
4909
|
+
// TODO: if this confirm fails, can we abort the whole transfer?
|
|
4910
|
+
await maybeConfirm({
|
|
4826
4911
|
type: 'transfer',
|
|
4827
4912
|
data: null,
|
|
4828
4913
|
ended: true,
|
|
@@ -4871,7 +4956,7 @@ const createPullController = handlerControllerFactory((proto)=>({
|
|
|
4871
4956
|
};
|
|
4872
4957
|
const BATCH_MAX_SIZE = 1024 * 1024; // 1MB
|
|
4873
4958
|
if (!assets) {
|
|
4874
|
-
throw new Error('
|
|
4959
|
+
throw new Error('Assets read stream could not be created');
|
|
4875
4960
|
}
|
|
4876
4961
|
/**
|
|
4877
4962
|
* Generates batches of 1MB of data from the assets stream to avoid
|