@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
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { Transform, PassThrough, Writable, Readable, Duplex, pipeline } from 'stream';
|
|
1
|
+
import { Transform, PassThrough, Writable, Readable, Duplex, pipeline as pipeline$1 } from 'stream';
|
|
2
|
+
import { pipeline } from 'stream/promises';
|
|
2
3
|
import path, { extname, join, posix } from 'path';
|
|
3
4
|
import { EOL } from 'os';
|
|
4
5
|
import { chain } from 'stream-chain';
|
|
@@ -633,8 +634,7 @@ const TRANSFER_STAGES = Object.freeze([
|
|
|
633
634
|
};
|
|
634
635
|
const DEFAULT_VERSION_STRATEGY = 'ignore';
|
|
635
636
|
const DEFAULT_SCHEMA_STRATEGY = 'strict';
|
|
636
|
-
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"),
|
|
637
|
-
_currentStream = /*#__PURE__*/ _class_private_field_loose_key$6("_currentStream"), /**
|
|
637
|
+
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"), /**
|
|
638
638
|
* Create and return a transform stream based on the given stage and options.
|
|
639
639
|
*
|
|
640
640
|
* Allowed transformations includes 'filter' and 'map'.
|
|
@@ -749,11 +749,9 @@ class TransferEngine {
|
|
|
749
749
|
}
|
|
750
750
|
// Cause an ongoing transfer to abort gracefully
|
|
751
751
|
async abortTransfer() {
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
}
|
|
756
|
-
_class_private_field_loose_base$6(this, _currentStream)[_currentStream].destroy(err);
|
|
752
|
+
_class_private_field_loose_base$6(this, _aborted)[_aborted] = true;
|
|
753
|
+
_class_private_field_loose_base$6(this, _currentStreamController)[_currentStreamController]?.abort();
|
|
754
|
+
throw new TransferEngineError('fatal', 'Transfer aborted.');
|
|
757
755
|
}
|
|
758
756
|
async init() {
|
|
759
757
|
// Resolve providers' resource and store
|
|
@@ -1063,7 +1061,11 @@ class TransferEngine {
|
|
|
1063
1061
|
writable: true,
|
|
1064
1062
|
value: void 0
|
|
1065
1063
|
});
|
|
1066
|
-
Object.defineProperty(this,
|
|
1064
|
+
Object.defineProperty(this, _currentStreamController, {
|
|
1065
|
+
writable: true,
|
|
1066
|
+
value: void 0
|
|
1067
|
+
});
|
|
1068
|
+
Object.defineProperty(this, _aborted, {
|
|
1067
1069
|
writable: true,
|
|
1068
1070
|
value: void 0
|
|
1069
1071
|
});
|
|
@@ -1073,6 +1075,7 @@ class TransferEngine {
|
|
|
1073
1075
|
schemaDiff: [],
|
|
1074
1076
|
errors: {}
|
|
1075
1077
|
};
|
|
1078
|
+
_class_private_field_loose_base$6(this, _aborted)[_aborted] = false;
|
|
1076
1079
|
this.diagnostics = createDiagnosticReporter();
|
|
1077
1080
|
validateProvider('source', sourceProvider);
|
|
1078
1081
|
validateProvider('destination', destinationProvider);
|
|
@@ -1271,6 +1274,9 @@ function assertSchemasMatching(sourceSchemas, destinationSchemas) {
|
|
|
1271
1274
|
}
|
|
1272
1275
|
}
|
|
1273
1276
|
async function transferStage(options) {
|
|
1277
|
+
if (_class_private_field_loose_base$6(this, _aborted)[_aborted]) {
|
|
1278
|
+
throw new TransferEngineError('fatal', 'Transfer aborted.');
|
|
1279
|
+
}
|
|
1274
1280
|
const { stage, source, destination, transform, tracker } = options;
|
|
1275
1281
|
const updateEndTime = ()=>{
|
|
1276
1282
|
const stageData = this.progress.data[stage];
|
|
@@ -1302,27 +1308,42 @@ async function transferStage(options) {
|
|
|
1302
1308
|
return;
|
|
1303
1309
|
}
|
|
1304
1310
|
_class_private_field_loose_base$6(this, _emitStageUpdate)[_emitStageUpdate]('start', stage);
|
|
1305
|
-
|
|
1306
|
-
|
|
1311
|
+
try {
|
|
1312
|
+
const streams = [
|
|
1313
|
+
source
|
|
1314
|
+
];
|
|
1307
1315
|
if (transform) {
|
|
1308
|
-
|
|
1316
|
+
streams.push(transform);
|
|
1309
1317
|
}
|
|
1310
1318
|
if (tracker) {
|
|
1311
|
-
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1319
|
+
streams.push(tracker);
|
|
1320
|
+
}
|
|
1321
|
+
streams.push(destination);
|
|
1322
|
+
// NOTE: to debug/confirm backpressure issues from misbehaving stream, uncomment the following lines
|
|
1323
|
+
// source.on('pause', () => console.log(`[${stage}] Source paused due to backpressure`));
|
|
1324
|
+
// source.on('resume', () => console.log(`[${stage}] Source resumed`));
|
|
1325
|
+
// destination.on('drain', () =>
|
|
1326
|
+
// console.log(`[${stage}] Destination drained, resuming data flow`)
|
|
1327
|
+
// );
|
|
1328
|
+
// destination.on('error', (err) => console.error(`[${stage}] Destination error:`, err));
|
|
1329
|
+
const controller = new AbortController();
|
|
1330
|
+
const { signal } = controller;
|
|
1331
|
+
// Store the controller so you can cancel later
|
|
1332
|
+
_class_private_field_loose_base$6(this, _currentStreamController)[_currentStreamController] = controller;
|
|
1333
|
+
await pipeline(streams, {
|
|
1334
|
+
signal
|
|
1323
1335
|
});
|
|
1324
|
-
|
|
1325
|
-
|
|
1336
|
+
_class_private_field_loose_base$6(this, _emitStageUpdate)[_emitStageUpdate]('finish', stage);
|
|
1337
|
+
} catch (e) {
|
|
1338
|
+
updateEndTime();
|
|
1339
|
+
_class_private_field_loose_base$6(this, _emitStageUpdate)[_emitStageUpdate]('error', stage);
|
|
1340
|
+
this.reportError(e, 'error');
|
|
1341
|
+
if (!destination.destroyed) {
|
|
1342
|
+
destination.destroy(e);
|
|
1343
|
+
}
|
|
1344
|
+
} finally{
|
|
1345
|
+
updateEndTime();
|
|
1346
|
+
}
|
|
1326
1347
|
}
|
|
1327
1348
|
async function resolveProviderResource() {
|
|
1328
1349
|
const sourceMetadata = await this.sourceProvider.getMetadata();
|
|
@@ -1894,9 +1915,7 @@ const createLinkQuery = (strapi, trx)=>{
|
|
|
1894
1915
|
assignOrderColumns();
|
|
1895
1916
|
const qb = connection.insert(payload).into(addSchema(joinTable.name));
|
|
1896
1917
|
if (trx) {
|
|
1897
|
-
await
|
|
1898
|
-
await qb.transacting(nestedTrx);
|
|
1899
|
-
});
|
|
1918
|
+
await qb.transacting(trx);
|
|
1900
1919
|
}
|
|
1901
1920
|
}
|
|
1902
1921
|
if ('morphColumn' in attribute && attribute.morphColumn) {
|
|
@@ -2349,7 +2368,7 @@ class LocalStrapiDestinationProvider {
|
|
|
2349
2368
|
const provider = strapi.config.get('plugin::upload').provider;
|
|
2350
2369
|
const fileId = fileEntitiesMapper?.[uploadData.id];
|
|
2351
2370
|
if (!fileId) {
|
|
2352
|
-
callback(new Error(`File ID not found for ID: ${uploadData.id}`));
|
|
2371
|
+
return callback(new Error(`File ID not found for ID: ${uploadData.id}`));
|
|
2353
2372
|
}
|
|
2354
2373
|
try {
|
|
2355
2374
|
await strapi.plugin('upload').provider.uploadStream(uploadData);
|
|
@@ -2400,9 +2419,9 @@ class LocalStrapiDestinationProvider {
|
|
|
2400
2419
|
provider
|
|
2401
2420
|
}
|
|
2402
2421
|
});
|
|
2403
|
-
callback();
|
|
2422
|
+
return callback();
|
|
2404
2423
|
} catch (error) {
|
|
2405
|
-
callback(new Error(`Error while uploading asset ${chunk.filename} ${error}`));
|
|
2424
|
+
return callback(new Error(`Error while uploading asset ${chunk.filename} ${error}`));
|
|
2406
2425
|
}
|
|
2407
2426
|
});
|
|
2408
2427
|
}
|
|
@@ -2824,7 +2843,11 @@ function _class_private_field_loose_key$4(name) {
|
|
|
2824
2843
|
const createLocalStrapiSourceProvider = (options)=>{
|
|
2825
2844
|
return new LocalStrapiSourceProvider(options);
|
|
2826
2845
|
};
|
|
2827
|
-
var _diagnostics$4 = /*#__PURE__*/ _class_private_field_loose_key$4("_diagnostics"), _reportInfo$4 = /*#__PURE__*/ _class_private_field_loose_key$4("_reportInfo")
|
|
2846
|
+
var _diagnostics$4 = /*#__PURE__*/ _class_private_field_loose_key$4("_diagnostics"), _reportInfo$4 = /*#__PURE__*/ _class_private_field_loose_key$4("_reportInfo"), /**
|
|
2847
|
+
* Reports an error to the diagnostic reporter.
|
|
2848
|
+
*/ _reportError = /*#__PURE__*/ _class_private_field_loose_key$4("_reportError"), /**
|
|
2849
|
+
* Handles errors that occur in read streams.
|
|
2850
|
+
*/ _handleStreamError = /*#__PURE__*/ _class_private_field_loose_key$4("_handleStreamError");
|
|
2828
2851
|
class LocalStrapiSourceProvider {
|
|
2829
2852
|
async bootstrap(diagnostics) {
|
|
2830
2853
|
_class_private_field_loose_base$4(this, _diagnostics$4)[_diagnostics$4] = diagnostics;
|
|
@@ -2886,12 +2909,22 @@ class LocalStrapiSourceProvider {
|
|
|
2886
2909
|
createAssetsReadStream() {
|
|
2887
2910
|
assertValidStrapi(this.strapi, 'Not able to stream assets');
|
|
2888
2911
|
_class_private_field_loose_base$4(this, _reportInfo$4)[_reportInfo$4]('creating assets read stream');
|
|
2889
|
-
|
|
2912
|
+
const stream = createAssetsStream(this.strapi);
|
|
2913
|
+
stream.on('error', (err)=>{
|
|
2914
|
+
_class_private_field_loose_base$4(this, _handleStreamError)[_handleStreamError]('assets', err);
|
|
2915
|
+
});
|
|
2916
|
+
return stream;
|
|
2890
2917
|
}
|
|
2891
2918
|
constructor(options){
|
|
2892
2919
|
Object.defineProperty(this, _reportInfo$4, {
|
|
2893
2920
|
value: reportInfo$4
|
|
2894
2921
|
});
|
|
2922
|
+
Object.defineProperty(this, _reportError, {
|
|
2923
|
+
value: reportError
|
|
2924
|
+
});
|
|
2925
|
+
Object.defineProperty(this, _handleStreamError, {
|
|
2926
|
+
value: handleStreamError
|
|
2927
|
+
});
|
|
2895
2928
|
Object.defineProperty(this, _diagnostics$4, {
|
|
2896
2929
|
writable: true,
|
|
2897
2930
|
value: void 0
|
|
@@ -2911,6 +2944,29 @@ function reportInfo$4(message) {
|
|
|
2911
2944
|
kind: 'info'
|
|
2912
2945
|
});
|
|
2913
2946
|
}
|
|
2947
|
+
function reportError(message, error) {
|
|
2948
|
+
_class_private_field_loose_base$4(this, _diagnostics$4)[_diagnostics$4]?.report({
|
|
2949
|
+
details: {
|
|
2950
|
+
createdAt: new Date(),
|
|
2951
|
+
message,
|
|
2952
|
+
error,
|
|
2953
|
+
severity: 'fatal',
|
|
2954
|
+
name: error.name
|
|
2955
|
+
},
|
|
2956
|
+
kind: 'error'
|
|
2957
|
+
});
|
|
2958
|
+
}
|
|
2959
|
+
function handleStreamError(streamType, err) {
|
|
2960
|
+
const { message, stack } = err;
|
|
2961
|
+
const errorMessage = `[Data transfer] Error in ${streamType} read stream: ${message}`;
|
|
2962
|
+
const formattedError = {
|
|
2963
|
+
message: errorMessage,
|
|
2964
|
+
stack,
|
|
2965
|
+
timestamp: new Date().toISOString()
|
|
2966
|
+
};
|
|
2967
|
+
this.strapi?.log.error(formattedError);
|
|
2968
|
+
_class_private_field_loose_base$4(this, _reportError)[_reportError](formattedError.message, err);
|
|
2969
|
+
}
|
|
2914
2970
|
|
|
2915
2971
|
const createDispatcher = (ws, retryMessageOptions = {
|
|
2916
2972
|
retryMessageMaxRetries: 5,
|
|
@@ -3074,17 +3130,6 @@ const connectToWebsocket = (address, options, diagnostics)=>{
|
|
|
3074
3130
|
const trimTrailingSlash = (input)=>{
|
|
3075
3131
|
return input.replace(/\/$/, '');
|
|
3076
3132
|
};
|
|
3077
|
-
const wait = (ms)=>{
|
|
3078
|
-
return new Promise((resolve)=>{
|
|
3079
|
-
setTimeout(resolve, ms);
|
|
3080
|
-
});
|
|
3081
|
-
};
|
|
3082
|
-
const waitUntil = async (test, interval)=>{
|
|
3083
|
-
while(!test()){
|
|
3084
|
-
await wait(interval);
|
|
3085
|
-
}
|
|
3086
|
-
return Promise.resolve();
|
|
3087
|
-
};
|
|
3088
3133
|
|
|
3089
3134
|
const TRANSFER_PATH = '/transfer/runner';
|
|
3090
3135
|
const TRANSFER_METHODS = [
|
|
@@ -3505,6 +3550,17 @@ class RemoteStrapiSourceProvider {
|
|
|
3505
3550
|
});
|
|
3506
3551
|
// Init the asset map
|
|
3507
3552
|
const assets = {};
|
|
3553
|
+
// Watch for stalled assets; if we don't receive a chunk within timeout, abort transfer
|
|
3554
|
+
const resetTimeout = (assetID)=>{
|
|
3555
|
+
if (assets[assetID].timeout) {
|
|
3556
|
+
clearTimeout(assets[assetID].timeout);
|
|
3557
|
+
}
|
|
3558
|
+
assets[assetID].timeout = setTimeout(()=>{
|
|
3559
|
+
_class_private_field_loose_base$2(this, _reportInfo$2)[_reportInfo$2](`Asset ${assetID} transfer stalled, aborting.`);
|
|
3560
|
+
assets[assetID].status = 'errored';
|
|
3561
|
+
assets[assetID].stream.destroy(new Error(`Asset ${assetID} transfer timed out`));
|
|
3562
|
+
}, this.options.streamTimeout);
|
|
3563
|
+
};
|
|
3508
3564
|
stream/**
|
|
3509
3565
|
* Process a payload of many transfer assets and performs the following tasks:
|
|
3510
3566
|
* - Start: creates a stream for new assets.
|
|
@@ -3515,56 +3571,46 @@ class RemoteStrapiSourceProvider {
|
|
|
3515
3571
|
const { action, assetID } = item;
|
|
3516
3572
|
// Creates the stream to send the incoming asset through
|
|
3517
3573
|
if (action === 'start') {
|
|
3518
|
-
//
|
|
3574
|
+
// if a transfer has already been started for the same asset ID, something is wrong
|
|
3519
3575
|
if (assets[assetID]) {
|
|
3520
|
-
|
|
3576
|
+
throw new Error(`Asset ${assetID} already started`);
|
|
3521
3577
|
}
|
|
3578
|
+
_class_private_field_loose_base$2(this, _reportInfo$2)[_reportInfo$2](`Asset ${assetID} starting`);
|
|
3522
3579
|
// Register the asset
|
|
3523
3580
|
assets[assetID] = {
|
|
3524
3581
|
...item.data,
|
|
3525
3582
|
stream: new PassThrough(),
|
|
3526
|
-
status: '
|
|
3583
|
+
status: 'ok',
|
|
3527
3584
|
queue: []
|
|
3528
3585
|
};
|
|
3586
|
+
resetTimeout(assetID);
|
|
3529
3587
|
// Connect the individual asset stream to the main asset stage stream
|
|
3530
3588
|
// Note: nothing is transferred until data chunks are fed to the asset stream
|
|
3531
3589
|
await this.writeAsync(pass, assets[assetID]);
|
|
3532
|
-
} else if (action === 'stream') {
|
|
3533
|
-
// If the asset hasn't been registered, or if it's been closed already,
|
|
3590
|
+
} else if (action === 'stream' || action === 'end') {
|
|
3591
|
+
// If the asset hasn't been registered, or if it's been closed already, something is wrong
|
|
3534
3592
|
if (!assets[assetID]) {
|
|
3535
|
-
|
|
3593
|
+
throw new Error(`No id matching ${assetID} for stream action`);
|
|
3536
3594
|
}
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
// The resource is busy, queue the current chunk so that it gets transferred as soon as possible
|
|
3543
|
-
case 'busy':
|
|
3544
|
-
assets[assetID].queue.push(item);
|
|
3545
|
-
break;
|
|
3595
|
+
// On every action, reset the timeout timer
|
|
3596
|
+
if (action === 'stream') {
|
|
3597
|
+
resetTimeout(assetID);
|
|
3598
|
+
} else {
|
|
3599
|
+
clearTimeout(assets[assetID].timeout);
|
|
3546
3600
|
}
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
if (!assets[assetID]) {
|
|
3550
|
-
continue;
|
|
3601
|
+
if (assets[assetID].status === 'closed') {
|
|
3602
|
+
throw new Error(`Asset ${assetID} is closed`);
|
|
3551
3603
|
}
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
waitUntil(()=>assets[assetID].status !== 'busy', 100),
|
|
3563
|
-
// Or: if the last chunks are still not processed after ten seconds
|
|
3564
|
-
wait(10000)
|
|
3565
|
-
]);
|
|
3566
|
-
await closeAssetStream(assetID);
|
|
3567
|
-
break;
|
|
3604
|
+
assets[assetID].queue.push(item);
|
|
3605
|
+
}
|
|
3606
|
+
}
|
|
3607
|
+
// each new payload will start new processQueue calls, which may cause some extra calls
|
|
3608
|
+
// it's essentially saying "start processing this asset again, I added more data to the queue"
|
|
3609
|
+
for(const assetID in assets){
|
|
3610
|
+
if (Object.prototype.hasOwnProperty.call(assets, assetID)) {
|
|
3611
|
+
const asset = assets[assetID];
|
|
3612
|
+
if (asset.queue?.length > 0) {
|
|
3613
|
+
await processQueue(assetID);
|
|
3568
3614
|
}
|
|
3569
3615
|
}
|
|
3570
3616
|
}
|
|
@@ -3572,38 +3618,48 @@ class RemoteStrapiSourceProvider {
|
|
|
3572
3618
|
pass.end();
|
|
3573
3619
|
});
|
|
3574
3620
|
/**
|
|
3575
|
-
*
|
|
3576
|
-
|
|
3621
|
+
* Start processing the queue for a given assetID
|
|
3622
|
+
*
|
|
3623
|
+
* Even though this is a loop that attempts to process the entire queue, it is safe to call this more than once
|
|
3624
|
+
* for the same asset id because the queue is shared globally, the items are shifted off, and immediately written
|
|
3625
|
+
*/ const processQueue = async (id)=>{
|
|
3577
3626
|
if (!assets[id]) {
|
|
3578
3627
|
throw new Error(`Failed to write asset chunk for "${id}". Asset not found.`);
|
|
3579
3628
|
}
|
|
3580
|
-
const
|
|
3581
|
-
|
|
3629
|
+
const asset = assets[id];
|
|
3630
|
+
const { status: currentStatus } = asset;
|
|
3631
|
+
if ([
|
|
3632
|
+
'closed',
|
|
3633
|
+
'errored'
|
|
3634
|
+
].includes(currentStatus)) {
|
|
3582
3635
|
throw new Error(`Failed to write asset chunk for "${id}". The asset is currently "${currentStatus}"`);
|
|
3583
3636
|
}
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3637
|
+
while(asset.queue.length > 0){
|
|
3638
|
+
const data = asset.queue.shift();
|
|
3639
|
+
if (!data) {
|
|
3640
|
+
throw new Error(`Invalid chunk found for ${id}`);
|
|
3641
|
+
}
|
|
3642
|
+
try {
|
|
3643
|
+
// if this is an end chunk, close the asset stream
|
|
3644
|
+
if (data.action === 'end') {
|
|
3645
|
+
_class_private_field_loose_base$2(this, _reportInfo$2)[_reportInfo$2](`Ending asset stream for ${id}`);
|
|
3646
|
+
await closeAssetStream(id);
|
|
3647
|
+
break; // Exit the loop after closing the stream
|
|
3648
|
+
}
|
|
3649
|
+
// Save the current chunk
|
|
3650
|
+
await writeChunkToStream(id, data);
|
|
3651
|
+
} catch {
|
|
3652
|
+
if (!assets[id]) {
|
|
3653
|
+
throw new Error(`No id matching ${id} for writeAssetChunk`);
|
|
3654
|
+
}
|
|
3595
3655
|
}
|
|
3596
|
-
// Unlock the asset
|
|
3597
|
-
assets[id].status = 'idle';
|
|
3598
|
-
} catch {
|
|
3599
|
-
assets[id].status = 'errored';
|
|
3600
3656
|
}
|
|
3601
3657
|
};
|
|
3602
3658
|
/**
|
|
3603
3659
|
* Writes a chunk of data to the asset's stream.
|
|
3604
3660
|
*
|
|
3605
3661
|
* Only check if the targeted asset exists, no other validation is done.
|
|
3606
|
-
*/ const
|
|
3662
|
+
*/ const writeChunkToStream = async (id, data)=>{
|
|
3607
3663
|
const asset = assets[id];
|
|
3608
3664
|
if (!asset) {
|
|
3609
3665
|
throw new Error(`Failed to write asset chunk for "${id}". Asset not found.`);
|
|
@@ -3624,9 +3680,11 @@ class RemoteStrapiSourceProvider {
|
|
|
3624
3680
|
await new Promise((resolve, reject)=>{
|
|
3625
3681
|
const { stream } = assets[id];
|
|
3626
3682
|
stream.on('close', ()=>{
|
|
3627
|
-
delete assets[id];
|
|
3628
3683
|
resolve();
|
|
3629
|
-
}).on('error',
|
|
3684
|
+
}).on('error', (e)=>{
|
|
3685
|
+
assets[id].status = 'errored';
|
|
3686
|
+
reject(new Error(`Failed to close asset "${id}". Asset stream error: ${e.toString()}`));
|
|
3687
|
+
}).end();
|
|
3630
3688
|
});
|
|
3631
3689
|
};
|
|
3632
3690
|
return pass;
|
|
@@ -3741,6 +3799,9 @@ class RemoteStrapiSourceProvider {
|
|
|
3741
3799
|
});
|
|
3742
3800
|
this.name = 'source::remote-strapi';
|
|
3743
3801
|
this.type = 'source';
|
|
3802
|
+
this.defaultOptions = {
|
|
3803
|
+
streamTimeout: 15000
|
|
3804
|
+
};
|
|
3744
3805
|
this.writeAsync = (stream, data)=>{
|
|
3745
3806
|
return new Promise((resolve, reject)=>{
|
|
3746
3807
|
stream.write(data, (error)=>{
|
|
@@ -3751,7 +3812,10 @@ class RemoteStrapiSourceProvider {
|
|
|
3751
3812
|
});
|
|
3752
3813
|
});
|
|
3753
3814
|
};
|
|
3754
|
-
this.options =
|
|
3815
|
+
this.options = {
|
|
3816
|
+
...this.defaultOptions,
|
|
3817
|
+
...options
|
|
3818
|
+
};
|
|
3755
3819
|
this.ws = null;
|
|
3756
3820
|
this.dispatcher = null;
|
|
3757
3821
|
}
|
|
@@ -4684,6 +4748,18 @@ const createPullController = handlerControllerFactory((proto)=>({
|
|
|
4684
4748
|
kind: 'warning'
|
|
4685
4749
|
});
|
|
4686
4750
|
},
|
|
4751
|
+
onError (error) {
|
|
4752
|
+
this.diagnostics?.report({
|
|
4753
|
+
details: {
|
|
4754
|
+
message: error.message,
|
|
4755
|
+
error,
|
|
4756
|
+
createdAt: new Date(),
|
|
4757
|
+
name: error.name,
|
|
4758
|
+
severity: 'fatal'
|
|
4759
|
+
},
|
|
4760
|
+
kind: 'error'
|
|
4761
|
+
});
|
|
4762
|
+
},
|
|
4687
4763
|
assertValidTransferAction (action) {
|
|
4688
4764
|
// Abstract the constant to string[] to allow looser check on the given action
|
|
4689
4765
|
const validActions = VALID_TRANSFER_ACTIONS;
|
|
@@ -4757,6 +4833,15 @@ const createPullController = handlerControllerFactory((proto)=>({
|
|
|
4757
4833
|
let batch = [];
|
|
4758
4834
|
const stream = this.streams?.[stage];
|
|
4759
4835
|
const batchLength = ()=>Buffer.byteLength(JSON.stringify(batch));
|
|
4836
|
+
const maybeConfirm = async (data)=>{
|
|
4837
|
+
try {
|
|
4838
|
+
await this.confirm(data);
|
|
4839
|
+
} catch (error) {
|
|
4840
|
+
// Handle the error, log it, or take other appropriate actions
|
|
4841
|
+
strapi?.log.error(`[Data transfer] Message confirmation failed: ${error?.message}`);
|
|
4842
|
+
this.onError(error);
|
|
4843
|
+
}
|
|
4844
|
+
};
|
|
4760
4845
|
const sendBatch = async ()=>{
|
|
4761
4846
|
await this.confirm({
|
|
4762
4847
|
type: 'transfer',
|
|
@@ -4765,6 +4850,7 @@ const createPullController = handlerControllerFactory((proto)=>({
|
|
|
4765
4850
|
error: null,
|
|
4766
4851
|
id
|
|
4767
4852
|
});
|
|
4853
|
+
batch = [];
|
|
4768
4854
|
};
|
|
4769
4855
|
if (!stream) {
|
|
4770
4856
|
throw new ProviderTransferError(`No available stream found for ${stage}`);
|
|
@@ -4775,7 +4861,6 @@ const createPullController = handlerControllerFactory((proto)=>({
|
|
|
4775
4861
|
batch.push(chunk);
|
|
4776
4862
|
if (batchLength() >= batchSize) {
|
|
4777
4863
|
await sendBatch();
|
|
4778
|
-
batch = [];
|
|
4779
4864
|
}
|
|
4780
4865
|
} else {
|
|
4781
4866
|
await this.confirm({
|
|
@@ -4791,7 +4876,6 @@ const createPullController = handlerControllerFactory((proto)=>({
|
|
|
4791
4876
|
}
|
|
4792
4877
|
if (batch.length > 0 && stage !== 'assets') {
|
|
4793
4878
|
await sendBatch();
|
|
4794
|
-
batch = [];
|
|
4795
4879
|
}
|
|
4796
4880
|
await this.confirm({
|
|
4797
4881
|
type: 'transfer',
|
|
@@ -4801,7 +4885,8 @@ const createPullController = handlerControllerFactory((proto)=>({
|
|
|
4801
4885
|
id
|
|
4802
4886
|
});
|
|
4803
4887
|
} catch (e) {
|
|
4804
|
-
|
|
4888
|
+
// TODO: if this confirm fails, can we abort the whole transfer?
|
|
4889
|
+
await maybeConfirm({
|
|
4805
4890
|
type: 'transfer',
|
|
4806
4891
|
data: null,
|
|
4807
4892
|
ended: true,
|
|
@@ -4850,7 +4935,7 @@ const createPullController = handlerControllerFactory((proto)=>({
|
|
|
4850
4935
|
};
|
|
4851
4936
|
const BATCH_MAX_SIZE = 1024 * 1024; // 1MB
|
|
4852
4937
|
if (!assets) {
|
|
4853
|
-
throw new Error('
|
|
4938
|
+
throw new Error('Assets read stream could not be created');
|
|
4854
4939
|
}
|
|
4855
4940
|
/**
|
|
4856
4941
|
* Generates batches of 1MB of data from the assets stream to avoid
|
|
@@ -5101,7 +5186,7 @@ class LocalFileSourceProvider {
|
|
|
5101
5186
|
});
|
|
5102
5187
|
const loadAssetMetadata = _class_private_field_loose_base$1(this, _loadAssetMetadata)[_loadAssetMetadata].bind(this);
|
|
5103
5188
|
_class_private_field_loose_base$1(this, _reportInfo$1)[_reportInfo$1]('creating assets read stream');
|
|
5104
|
-
pipeline([
|
|
5189
|
+
pipeline$1([
|
|
5105
5190
|
inStream,
|
|
5106
5191
|
new tar.Parse({
|
|
5107
5192
|
// find only files in the assets/uploads folder
|
|
@@ -5212,7 +5297,7 @@ function streamJsonlDirectory(directory) {
|
|
|
5212
5297
|
const outStream = new PassThrough({
|
|
5213
5298
|
objectMode: true
|
|
5214
5299
|
});
|
|
5215
|
-
pipeline([
|
|
5300
|
+
pipeline$1([
|
|
5216
5301
|
inStream,
|
|
5217
5302
|
new tar.Parse({
|
|
5218
5303
|
filter (filePath, entry) {
|
|
@@ -5253,7 +5338,7 @@ function streamJsonlDirectory(directory) {
|
|
|
5253
5338
|
}
|
|
5254
5339
|
async function parseJSONFile(fileStream, filePath) {
|
|
5255
5340
|
return new Promise((resolve, reject)=>{
|
|
5256
|
-
pipeline([
|
|
5341
|
+
pipeline$1([
|
|
5257
5342
|
fileStream,
|
|
5258
5343
|
// Custom backup archive parsing
|
|
5259
5344
|
new tar.Parse({
|