@strapi/data-transfer 4.7.0-beta.0 → 4.7.0-exp.117579f4c13806c2cd518e7d7d2f9d0c8a20107d
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/engine/index.js +13 -3
- package/lib/errors/constants.d.ts +1 -1
- package/lib/strapi/providers/local-destination/index.d.ts +1 -1
- package/lib/strapi/providers/local-destination/index.js +5 -3
- package/lib/strapi/providers/remote-destination/index.d.ts +2 -0
- package/lib/strapi/providers/remote-destination/index.js +139 -54
- package/lib/strapi/providers/remote-destination/utils.js +2 -2
- package/lib/strapi/remote/controllers/push.d.ts +1 -0
- package/lib/strapi/remote/controllers/push.js +53 -32
- package/lib/strapi/remote/flows/default.d.ts +3 -0
- package/lib/strapi/remote/flows/default.js +41 -0
- package/lib/strapi/remote/flows/index.d.ts +18 -0
- package/lib/strapi/remote/flows/index.js +59 -0
- package/lib/strapi/remote/handlers.d.ts +1 -1
- package/lib/strapi/remote/handlers.js +126 -40
- package/lib/utils/transaction.js +21 -3
- package/package.json +5 -5
package/lib/engine/index.js
CHANGED
|
@@ -230,7 +230,7 @@ class TransferEngine {
|
|
|
230
230
|
const transform = __classPrivateFieldGet(this, _TransferEngine_instances, "m", _TransferEngine_createStageTransformStream).call(this, stage);
|
|
231
231
|
const tracker = __classPrivateFieldGet(this, _TransferEngine_instances, "m", _TransferEngine_progressTracker).call(this, stage, {
|
|
232
232
|
size: (value) => value.stats.size,
|
|
233
|
-
key: (value) => (0, path_1.extname)(value.filename)
|
|
233
|
+
key: (value) => (0, path_1.extname)(value.filename) || 'No extension',
|
|
234
234
|
});
|
|
235
235
|
await __classPrivateFieldGet(this, _TransferEngine_instances, "m", _TransferEngine_transferStage).call(this, { stage, source, destination, transform, tracker });
|
|
236
236
|
}
|
|
@@ -288,7 +288,7 @@ _TransferEngine_metadata = new WeakMap(), _TransferEngine_instances = new WeakSe
|
|
|
288
288
|
return stream;
|
|
289
289
|
}, _TransferEngine_updateTransferProgress = function _TransferEngine_updateTransferProgress(stage, data, aggregate) {
|
|
290
290
|
if (!this.progress.data[stage]) {
|
|
291
|
-
this.progress.data[stage] = { count: 0, bytes: 0 };
|
|
291
|
+
this.progress.data[stage] = { count: 0, bytes: 0, startTime: Date.now() };
|
|
292
292
|
}
|
|
293
293
|
const stageProgress = this.progress.data[stage];
|
|
294
294
|
if (!stageProgress) {
|
|
@@ -416,6 +416,12 @@ _TransferEngine_metadata = new WeakMap(), _TransferEngine_instances = new WeakSe
|
|
|
416
416
|
}
|
|
417
417
|
}, _TransferEngine_transferStage = async function _TransferEngine_transferStage(options) {
|
|
418
418
|
const { stage, source, destination, transform, tracker } = options;
|
|
419
|
+
const updateEndTime = () => {
|
|
420
|
+
const stageData = this.progress.data[stage];
|
|
421
|
+
if (stageData) {
|
|
422
|
+
stageData.endTime = Date.now();
|
|
423
|
+
}
|
|
424
|
+
};
|
|
419
425
|
if (!source || !destination || this.shouldSkipStage(stage)) {
|
|
420
426
|
// Wait until source and destination are closed
|
|
421
427
|
const results = await Promise.allSettled([source, destination].map((stream) => {
|
|
@@ -448,11 +454,15 @@ _TransferEngine_metadata = new WeakMap(), _TransferEngine_instances = new WeakSe
|
|
|
448
454
|
stream
|
|
449
455
|
.pipe(destination)
|
|
450
456
|
.on('error', (e) => {
|
|
457
|
+
updateEndTime();
|
|
451
458
|
__classPrivateFieldGet(this, _TransferEngine_instances, "m", _TransferEngine_reportError).call(this, e, 'error');
|
|
452
459
|
destination.destroy(e);
|
|
453
460
|
reject(e);
|
|
454
461
|
})
|
|
455
|
-
.on('close',
|
|
462
|
+
.on('close', () => {
|
|
463
|
+
updateEndTime();
|
|
464
|
+
resolve();
|
|
465
|
+
});
|
|
456
466
|
});
|
|
457
467
|
__classPrivateFieldGet(this, _TransferEngine_instances, "m", _TransferEngine_emitStageUpdate).call(this, 'finish', stage);
|
|
458
468
|
}, _TransferEngine_resolveProviderResource = async function _TransferEngine_resolveProviderResource() {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { ErrorDiagnosticSeverity } from '../engine/diagnostic';
|
|
2
2
|
export declare const SeverityKind: Record<string, ErrorDiagnosticSeverity>;
|
|
3
|
-
export declare type Severity = typeof SeverityKind[keyof typeof SeverityKind];
|
|
3
|
+
export declare type Severity = (typeof SeverityKind)[keyof typeof SeverityKind];
|
|
@@ -21,7 +21,7 @@ declare class LocalStrapiDestinationProvider implements IDestinationProvider {
|
|
|
21
21
|
constructor(options: ILocalStrapiDestinationProviderOptions);
|
|
22
22
|
bootstrap(): Promise<void>;
|
|
23
23
|
close(): Promise<void>;
|
|
24
|
-
rollback(): void
|
|
24
|
+
rollback(): Promise<void>;
|
|
25
25
|
beforeTransfer(): Promise<void>;
|
|
26
26
|
getMetadata(): IMetadata;
|
|
27
27
|
getSchemas(): import("lodash").Dictionary<Partial<import("@strapi/strapi").Schema>>;
|
|
@@ -73,8 +73,8 @@ class LocalStrapiDestinationProvider {
|
|
|
73
73
|
await this.strapi?.destroy();
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
|
-
rollback() {
|
|
77
|
-
this.transaction?.rollback();
|
|
76
|
+
async rollback() {
|
|
77
|
+
await this.transaction?.rollback();
|
|
78
78
|
}
|
|
79
79
|
async beforeTransfer() {
|
|
80
80
|
if (!this.strapi) {
|
|
@@ -151,7 +151,9 @@ class LocalStrapiDestinationProvider {
|
|
|
151
151
|
const writableStream = fse.createWriteStream(entryPath);
|
|
152
152
|
chunk.stream
|
|
153
153
|
.pipe(writableStream)
|
|
154
|
-
.on('close',
|
|
154
|
+
.on('close', () => {
|
|
155
|
+
callback(null);
|
|
156
|
+
})
|
|
155
157
|
.on('error', async (error) => {
|
|
156
158
|
const errorMessage = error.code === 'ENOSPC'
|
|
157
159
|
? " Your server doesn't have space to proceed with the import. "
|
|
@@ -19,12 +19,14 @@ declare class RemoteStrapiDestinationProvider implements IDestinationProvider {
|
|
|
19
19
|
options: IRemoteStrapiDestinationProviderOptions;
|
|
20
20
|
ws: WebSocket | null;
|
|
21
21
|
dispatcher: ReturnType<typeof createDispatcher> | null;
|
|
22
|
+
transferID: string | null;
|
|
22
23
|
constructor(options: IRemoteStrapiDestinationProviderOptions);
|
|
23
24
|
initTransfer(): Promise<string>;
|
|
24
25
|
bootstrap(): Promise<void>;
|
|
25
26
|
close(): Promise<void>;
|
|
26
27
|
getMetadata(): Promise<IMetadata | null> | null;
|
|
27
28
|
beforeTransfer(): Promise<void>;
|
|
29
|
+
rollback(): Promise<void>;
|
|
28
30
|
getSchemas(): Promise<Strapi.Schemas | null>;
|
|
29
31
|
createEntitiesWriteStream(): Writable;
|
|
30
32
|
createLinksWriteStream(): Writable;
|
|
@@ -4,15 +4,17 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
4
4
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
5
5
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
6
6
|
};
|
|
7
|
-
var _RemoteStrapiDestinationProvider_instances, _RemoteStrapiDestinationProvider_streamStep;
|
|
7
|
+
var _RemoteStrapiDestinationProvider_instances, _RemoteStrapiDestinationProvider_startStepOnce, _RemoteStrapiDestinationProvider_startStep, _RemoteStrapiDestinationProvider_endStep, _RemoteStrapiDestinationProvider_streamStep, _RemoteStrapiDestinationProvider_writeStream;
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.createRemoteStrapiDestinationProvider = void 0;
|
|
10
10
|
const ws_1 = require("ws");
|
|
11
11
|
const uuid_1 = require("uuid");
|
|
12
12
|
const stream_1 = require("stream");
|
|
13
|
+
const fp_1 = require("lodash/fp");
|
|
13
14
|
const utils_1 = require("./utils");
|
|
14
15
|
const constants_1 = require("../../remote/constants");
|
|
15
16
|
const providers_1 = require("../../../errors/providers");
|
|
17
|
+
const jsonLength = (obj) => Buffer.byteLength(JSON.stringify(obj));
|
|
16
18
|
class RemoteStrapiDestinationProvider {
|
|
17
19
|
constructor(options) {
|
|
18
20
|
_RemoteStrapiDestinationProvider_instances.add(this);
|
|
@@ -21,6 +23,7 @@ class RemoteStrapiDestinationProvider {
|
|
|
21
23
|
this.options = options;
|
|
22
24
|
this.ws = null;
|
|
23
25
|
this.dispatcher = null;
|
|
26
|
+
this.transferID = null;
|
|
24
27
|
}
|
|
25
28
|
async initTransfer() {
|
|
26
29
|
const { strategy, restore } = this.options;
|
|
@@ -28,17 +31,24 @@ class RemoteStrapiDestinationProvider {
|
|
|
28
31
|
return new Promise((resolve, reject) => {
|
|
29
32
|
this.ws
|
|
30
33
|
?.once('open', async () => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
try {
|
|
35
|
+
const query = this.dispatcher?.dispatchCommand({
|
|
36
|
+
command: 'init',
|
|
37
|
+
params: { options: { strategy, restore }, transfer: 'push' },
|
|
38
|
+
});
|
|
39
|
+
const res = (await query);
|
|
40
|
+
if (!res?.transferID) {
|
|
41
|
+
throw new providers_1.ProviderTransferError('Init failed, invalid response from the server');
|
|
42
|
+
}
|
|
43
|
+
resolve(res.transferID);
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
reject(e);
|
|
38
47
|
}
|
|
39
|
-
resolve(res.transferID);
|
|
40
48
|
})
|
|
41
|
-
.once('error',
|
|
49
|
+
.once('error', (message) => {
|
|
50
|
+
reject(message);
|
|
51
|
+
});
|
|
42
52
|
});
|
|
43
53
|
}
|
|
44
54
|
async bootstrap() {
|
|
@@ -76,12 +86,19 @@ class RemoteStrapiDestinationProvider {
|
|
|
76
86
|
}
|
|
77
87
|
this.ws = ws;
|
|
78
88
|
this.dispatcher = (0, utils_1.createDispatcher)(this.ws);
|
|
79
|
-
|
|
80
|
-
this.dispatcher.setTransferProperties({ id: transferID, kind: 'push' });
|
|
89
|
+
this.transferID = await this.initTransfer();
|
|
90
|
+
this.dispatcher.setTransferProperties({ id: this.transferID, kind: 'push' });
|
|
81
91
|
await this.dispatcher.dispatchTransferAction('bootstrap');
|
|
82
92
|
}
|
|
83
93
|
async close() {
|
|
84
|
-
|
|
94
|
+
// Gracefully close the remote transfer process
|
|
95
|
+
if (this.transferID && this.dispatcher) {
|
|
96
|
+
await this.dispatcher.dispatchTransferAction('close');
|
|
97
|
+
await this.dispatcher.dispatchCommand({
|
|
98
|
+
command: 'end',
|
|
99
|
+
params: { transferID: this.transferID },
|
|
100
|
+
});
|
|
101
|
+
}
|
|
85
102
|
await new Promise((resolve) => {
|
|
86
103
|
const { ws } = this;
|
|
87
104
|
if (!ws || ws.CLOSED) {
|
|
@@ -97,6 +114,9 @@ class RemoteStrapiDestinationProvider {
|
|
|
97
114
|
async beforeTransfer() {
|
|
98
115
|
await this.dispatcher?.dispatchTransferAction('beforeTransfer');
|
|
99
116
|
}
|
|
117
|
+
async rollback() {
|
|
118
|
+
await this.dispatcher?.dispatchTransferAction('rollback');
|
|
119
|
+
}
|
|
100
120
|
getSchemas() {
|
|
101
121
|
if (!this.dispatcher) {
|
|
102
122
|
return Promise.resolve(null);
|
|
@@ -104,65 +124,96 @@ class RemoteStrapiDestinationProvider {
|
|
|
104
124
|
return this.dispatcher.dispatchTransferAction('getSchemas');
|
|
105
125
|
}
|
|
106
126
|
createEntitiesWriteStream() {
|
|
107
|
-
return
|
|
108
|
-
objectMode: true,
|
|
109
|
-
write: async (entity, _encoding, callback) => {
|
|
110
|
-
const e = await __classPrivateFieldGet(this, _RemoteStrapiDestinationProvider_instances, "m", _RemoteStrapiDestinationProvider_streamStep).call(this, 'entities', entity);
|
|
111
|
-
callback(e);
|
|
112
|
-
},
|
|
113
|
-
});
|
|
127
|
+
return __classPrivateFieldGet(this, _RemoteStrapiDestinationProvider_instances, "m", _RemoteStrapiDestinationProvider_writeStream).call(this, 'entities');
|
|
114
128
|
}
|
|
115
129
|
createLinksWriteStream() {
|
|
116
|
-
return
|
|
117
|
-
objectMode: true,
|
|
118
|
-
write: async (link, _encoding, callback) => {
|
|
119
|
-
const e = await __classPrivateFieldGet(this, _RemoteStrapiDestinationProvider_instances, "m", _RemoteStrapiDestinationProvider_streamStep).call(this, 'links', link);
|
|
120
|
-
callback(e);
|
|
121
|
-
},
|
|
122
|
-
});
|
|
130
|
+
return __classPrivateFieldGet(this, _RemoteStrapiDestinationProvider_instances, "m", _RemoteStrapiDestinationProvider_writeStream).call(this, 'links');
|
|
123
131
|
}
|
|
124
132
|
createConfigurationWriteStream() {
|
|
125
|
-
return
|
|
126
|
-
objectMode: true,
|
|
127
|
-
write: async (configuration, _encoding, callback) => {
|
|
128
|
-
const e = await __classPrivateFieldGet(this, _RemoteStrapiDestinationProvider_instances, "m", _RemoteStrapiDestinationProvider_streamStep).call(this, 'configuration', configuration);
|
|
129
|
-
callback(e);
|
|
130
|
-
},
|
|
131
|
-
});
|
|
133
|
+
return __classPrivateFieldGet(this, _RemoteStrapiDestinationProvider_instances, "m", _RemoteStrapiDestinationProvider_writeStream).call(this, 'configuration');
|
|
132
134
|
}
|
|
133
135
|
createAssetsWriteStream() {
|
|
136
|
+
let batch = [];
|
|
137
|
+
let hasStarted = false;
|
|
138
|
+
const batchSize = 1024 * 1024; // 1MB;
|
|
139
|
+
const batchLength = () => {
|
|
140
|
+
return batch.reduce((acc, chunk) => (chunk.action === 'stream' ? acc + chunk.data.byteLength : acc), 0);
|
|
141
|
+
};
|
|
142
|
+
const startAssetsTransferOnce = __classPrivateFieldGet(this, _RemoteStrapiDestinationProvider_instances, "m", _RemoteStrapiDestinationProvider_startStepOnce).call(this, 'assets');
|
|
143
|
+
const flush = async () => {
|
|
144
|
+
await __classPrivateFieldGet(this, _RemoteStrapiDestinationProvider_instances, "m", _RemoteStrapiDestinationProvider_streamStep).call(this, 'assets', batch);
|
|
145
|
+
batch = [];
|
|
146
|
+
};
|
|
147
|
+
const safePush = async (chunk) => {
|
|
148
|
+
batch.push(chunk);
|
|
149
|
+
if (batchLength() >= batchSize) {
|
|
150
|
+
await flush();
|
|
151
|
+
}
|
|
152
|
+
};
|
|
134
153
|
return new stream_1.Writable({
|
|
135
154
|
objectMode: true,
|
|
136
155
|
final: async (callback) => {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
156
|
+
if (batch.length > 0) {
|
|
157
|
+
await flush();
|
|
158
|
+
}
|
|
159
|
+
if (hasStarted) {
|
|
160
|
+
await __classPrivateFieldGet(this, _RemoteStrapiDestinationProvider_instances, "m", _RemoteStrapiDestinationProvider_streamStep).call(this, 'assets', null);
|
|
161
|
+
const endStepError = await __classPrivateFieldGet(this, _RemoteStrapiDestinationProvider_instances, "m", _RemoteStrapiDestinationProvider_endStep).call(this, 'assets');
|
|
162
|
+
if (endStepError) {
|
|
163
|
+
return callback(endStepError);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return callback(null);
|
|
140
167
|
},
|
|
141
|
-
|
|
142
|
-
const
|
|
168
|
+
async write(asset, _encoding, callback) {
|
|
169
|
+
const startError = await startAssetsTransferOnce();
|
|
170
|
+
if (startError) {
|
|
171
|
+
return callback(startError);
|
|
172
|
+
}
|
|
173
|
+
hasStarted = true;
|
|
143
174
|
const assetID = (0, uuid_1.v4)();
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
assetID,
|
|
147
|
-
data: { filename, filepath, stats },
|
|
148
|
-
});
|
|
175
|
+
const { filename, filepath, stats, stream } = asset;
|
|
176
|
+
await safePush({ action: 'start', assetID, data: { filename, filepath, stats } });
|
|
149
177
|
for await (const chunk of stream) {
|
|
150
|
-
await
|
|
151
|
-
action: 'stream',
|
|
152
|
-
assetID,
|
|
153
|
-
data: chunk,
|
|
154
|
-
});
|
|
178
|
+
await safePush({ action: 'stream', assetID, data: chunk });
|
|
155
179
|
}
|
|
156
|
-
await
|
|
157
|
-
action: 'end',
|
|
158
|
-
assetID,
|
|
159
|
-
});
|
|
180
|
+
await safePush({ action: 'end', assetID });
|
|
160
181
|
callback();
|
|
161
182
|
},
|
|
162
183
|
});
|
|
163
184
|
}
|
|
164
185
|
}
|
|
165
|
-
_RemoteStrapiDestinationProvider_instances = new WeakSet(),
|
|
186
|
+
_RemoteStrapiDestinationProvider_instances = new WeakSet(), _RemoteStrapiDestinationProvider_startStepOnce = function _RemoteStrapiDestinationProvider_startStepOnce(stage) {
|
|
187
|
+
return (0, fp_1.once)(() => __classPrivateFieldGet(this, _RemoteStrapiDestinationProvider_instances, "m", _RemoteStrapiDestinationProvider_startStep).call(this, stage));
|
|
188
|
+
}, _RemoteStrapiDestinationProvider_startStep = async function _RemoteStrapiDestinationProvider_startStep(step) {
|
|
189
|
+
try {
|
|
190
|
+
await this.dispatcher?.dispatchTransferStep({ action: 'start', step });
|
|
191
|
+
}
|
|
192
|
+
catch (e) {
|
|
193
|
+
if (e instanceof Error) {
|
|
194
|
+
return e;
|
|
195
|
+
}
|
|
196
|
+
if (typeof e === 'string') {
|
|
197
|
+
return new providers_1.ProviderTransferError(e);
|
|
198
|
+
}
|
|
199
|
+
return new providers_1.ProviderTransferError('Unexpected error');
|
|
200
|
+
}
|
|
201
|
+
return null;
|
|
202
|
+
}, _RemoteStrapiDestinationProvider_endStep = async function _RemoteStrapiDestinationProvider_endStep(step) {
|
|
203
|
+
try {
|
|
204
|
+
await this.dispatcher?.dispatchTransferStep({ action: 'end', step });
|
|
205
|
+
}
|
|
206
|
+
catch (e) {
|
|
207
|
+
if (e instanceof Error) {
|
|
208
|
+
return e;
|
|
209
|
+
}
|
|
210
|
+
if (typeof e === 'string') {
|
|
211
|
+
return new providers_1.ProviderTransferError(e);
|
|
212
|
+
}
|
|
213
|
+
return new providers_1.ProviderTransferError('Unexpected error');
|
|
214
|
+
}
|
|
215
|
+
return null;
|
|
216
|
+
}, _RemoteStrapiDestinationProvider_streamStep = async function _RemoteStrapiDestinationProvider_streamStep(step, data) {
|
|
166
217
|
try {
|
|
167
218
|
await this.dispatcher?.dispatchTransferStep({ action: 'stream', step, data });
|
|
168
219
|
}
|
|
@@ -176,6 +227,40 @@ _RemoteStrapiDestinationProvider_instances = new WeakSet(), _RemoteStrapiDestina
|
|
|
176
227
|
return new providers_1.ProviderTransferError('Unexpected error');
|
|
177
228
|
}
|
|
178
229
|
return null;
|
|
230
|
+
}, _RemoteStrapiDestinationProvider_writeStream = function _RemoteStrapiDestinationProvider_writeStream(step) {
|
|
231
|
+
const batchSize = 1024 * 1024; // 1MB;
|
|
232
|
+
const startTransferOnce = __classPrivateFieldGet(this, _RemoteStrapiDestinationProvider_instances, "m", _RemoteStrapiDestinationProvider_startStepOnce).call(this, step);
|
|
233
|
+
let batch = [];
|
|
234
|
+
const batchLength = () => jsonLength(batch);
|
|
235
|
+
return new stream_1.Writable({
|
|
236
|
+
objectMode: true,
|
|
237
|
+
final: async (callback) => {
|
|
238
|
+
if (batch.length > 0) {
|
|
239
|
+
const streamError = await __classPrivateFieldGet(this, _RemoteStrapiDestinationProvider_instances, "m", _RemoteStrapiDestinationProvider_streamStep).call(this, step, batch);
|
|
240
|
+
batch = [];
|
|
241
|
+
if (streamError) {
|
|
242
|
+
return callback(streamError);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const e = await __classPrivateFieldGet(this, _RemoteStrapiDestinationProvider_instances, "m", _RemoteStrapiDestinationProvider_endStep).call(this, step);
|
|
246
|
+
callback(e);
|
|
247
|
+
},
|
|
248
|
+
write: async (chunk, _encoding, callback) => {
|
|
249
|
+
const startError = await startTransferOnce();
|
|
250
|
+
if (startError) {
|
|
251
|
+
return callback(startError);
|
|
252
|
+
}
|
|
253
|
+
batch.push(chunk);
|
|
254
|
+
if (batchLength() >= batchSize) {
|
|
255
|
+
const streamError = await __classPrivateFieldGet(this, _RemoteStrapiDestinationProvider_instances, "m", _RemoteStrapiDestinationProvider_streamStep).call(this, step, batch);
|
|
256
|
+
batch = [];
|
|
257
|
+
if (streamError) {
|
|
258
|
+
return callback(streamError);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
callback();
|
|
262
|
+
},
|
|
263
|
+
});
|
|
179
264
|
};
|
|
180
265
|
const createRemoteStrapiDestinationProvider = (options) => {
|
|
181
266
|
return new RemoteStrapiDestinationProvider(options);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createDispatcher = void 0;
|
|
4
4
|
const uuid_1 = require("uuid");
|
|
5
|
+
const providers_1 = require("../../../errors/providers");
|
|
5
6
|
const createDispatcher = (ws) => {
|
|
6
7
|
const state = {};
|
|
7
8
|
const dispatch = async (message, options = {}) => {
|
|
@@ -24,7 +25,7 @@ const createDispatcher = (ws) => {
|
|
|
24
25
|
const response = JSON.parse(raw.toString());
|
|
25
26
|
if (response.uuid === uuid) {
|
|
26
27
|
if (response.error) {
|
|
27
|
-
return reject(new
|
|
28
|
+
return reject(new providers_1.ProviderError('error', response.error.message));
|
|
28
29
|
}
|
|
29
30
|
resolve(response.data ?? null);
|
|
30
31
|
}
|
|
@@ -32,7 +33,6 @@ const createDispatcher = (ws) => {
|
|
|
32
33
|
ws.once('message', onResponse);
|
|
33
34
|
}
|
|
34
35
|
};
|
|
35
|
-
// TODO: What happens if the server sends another message (not a response to this message)
|
|
36
36
|
ws.once('message', onResponse);
|
|
37
37
|
});
|
|
38
38
|
};
|
|
@@ -34,58 +34,79 @@ const createPushController = (options) => {
|
|
|
34
34
|
async beforeTransfer() {
|
|
35
35
|
return provider.beforeTransfer();
|
|
36
36
|
},
|
|
37
|
+
async rollback() {
|
|
38
|
+
await provider.rollback();
|
|
39
|
+
},
|
|
37
40
|
},
|
|
38
41
|
transfer: {
|
|
39
|
-
async entities(
|
|
42
|
+
async entities(entities) {
|
|
40
43
|
if (!streams.entities) {
|
|
41
44
|
streams.entities = provider.createEntitiesWriteStream();
|
|
42
45
|
}
|
|
43
|
-
|
|
46
|
+
for (const entity of entities) {
|
|
47
|
+
if (streams.entities) {
|
|
48
|
+
await writeAsync(streams.entities, entity);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
44
51
|
},
|
|
45
|
-
async links(
|
|
52
|
+
async links(links) {
|
|
46
53
|
if (!streams.links) {
|
|
47
54
|
streams.links = await provider.createLinksWriteStream();
|
|
48
55
|
}
|
|
49
|
-
|
|
56
|
+
for (const link of links) {
|
|
57
|
+
if (streams.links) {
|
|
58
|
+
await writeAsync(streams.links, link);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
50
61
|
},
|
|
51
|
-
async configuration(
|
|
62
|
+
async configuration(configs) {
|
|
52
63
|
if (!streams.configuration) {
|
|
53
64
|
streams.configuration = await provider.createConfigurationWriteStream();
|
|
54
65
|
}
|
|
55
|
-
|
|
66
|
+
for (const config of configs) {
|
|
67
|
+
if (streams.configuration) {
|
|
68
|
+
await writeAsync(streams.configuration, config);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
56
71
|
},
|
|
57
|
-
async assets(
|
|
58
|
-
|
|
59
|
-
if (payload === null) {
|
|
72
|
+
async assets(payloads) {
|
|
73
|
+
if (payloads === null) {
|
|
60
74
|
streams.assets?.end();
|
|
61
75
|
return;
|
|
62
76
|
}
|
|
63
|
-
const { action, assetID } = payload;
|
|
64
77
|
if (!streams.assets) {
|
|
65
78
|
streams.assets = await provider.createAssetsWriteStream();
|
|
66
79
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
stream
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
80
|
+
for (const payload of payloads) {
|
|
81
|
+
if (streams.assets.closed) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const { action, assetID } = payload;
|
|
85
|
+
if (action === 'start' && streams.assets) {
|
|
86
|
+
assets[assetID] = { ...payload.data, stream: new stream_chain_1.PassThrough() };
|
|
87
|
+
writeAsync(streams.assets, assets[assetID]);
|
|
88
|
+
}
|
|
89
|
+
if (action === 'stream') {
|
|
90
|
+
// The buffer has gone through JSON operations and is now of shape { type: "Buffer"; data: UInt8Array }
|
|
91
|
+
// We need to transform it back into a Buffer instance
|
|
92
|
+
const rawBuffer = payload.data;
|
|
93
|
+
const chunk = Buffer.from(rawBuffer.data);
|
|
94
|
+
await writeAsync(assets[assetID].stream, chunk);
|
|
95
|
+
}
|
|
96
|
+
if (action === 'end') {
|
|
97
|
+
await new Promise((resolve, reject) => {
|
|
98
|
+
const { stream } = assets[assetID];
|
|
99
|
+
stream
|
|
100
|
+
.on('close', () => {
|
|
101
|
+
delete assets[assetID];
|
|
102
|
+
resolve();
|
|
103
|
+
})
|
|
104
|
+
.on('error', (e) => {
|
|
105
|
+
reject(e);
|
|
106
|
+
})
|
|
107
|
+
.end();
|
|
108
|
+
});
|
|
109
|
+
}
|
|
89
110
|
}
|
|
90
111
|
},
|
|
91
112
|
},
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = [
|
|
4
|
+
{
|
|
5
|
+
kind: 'action',
|
|
6
|
+
action: 'bootstrap',
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
kind: 'action',
|
|
10
|
+
action: 'init',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
kind: 'action',
|
|
14
|
+
action: 'beforeTransfer',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
kind: 'transfer',
|
|
18
|
+
stage: 'schemas',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
kind: 'transfer',
|
|
22
|
+
stage: 'entities',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
kind: 'transfer',
|
|
26
|
+
stage: 'assets',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
kind: 'transfer',
|
|
30
|
+
stage: 'links',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
kind: 'transfer',
|
|
34
|
+
stage: 'configuration',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
kind: 'action',
|
|
38
|
+
action: 'close',
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
//# sourceMappingURL=default.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { TransferStage } from '../../../../types';
|
|
2
|
+
export declare type Step = {
|
|
3
|
+
kind: 'action';
|
|
4
|
+
action: string;
|
|
5
|
+
} | {
|
|
6
|
+
kind: 'transfer';
|
|
7
|
+
stage: TransferStage;
|
|
8
|
+
locked?: boolean;
|
|
9
|
+
};
|
|
10
|
+
export { default as DEFAULT_TRANSFER_FLOW } from './default';
|
|
11
|
+
export declare const createFlow: (flow: readonly Step[]) => {
|
|
12
|
+
has(step: Step): boolean;
|
|
13
|
+
can(step: Step): boolean;
|
|
14
|
+
cannot(step: Step): boolean;
|
|
15
|
+
set(step: Step): any;
|
|
16
|
+
get(): Step | null;
|
|
17
|
+
};
|
|
18
|
+
export declare type TransferFlow = ReturnType<typeof createFlow>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createFlow = exports.DEFAULT_TRANSFER_FLOW = void 0;
|
|
7
|
+
var default_1 = require("./default");
|
|
8
|
+
Object.defineProperty(exports, "DEFAULT_TRANSFER_FLOW", { enumerable: true, get: function () { return __importDefault(default_1).default; } });
|
|
9
|
+
const createFlow = (flow) => {
|
|
10
|
+
const state = { step: null };
|
|
11
|
+
/**
|
|
12
|
+
* Equality check between two steps
|
|
13
|
+
*/
|
|
14
|
+
const stepEqual = (stepA, stepB) => {
|
|
15
|
+
if (stepA.kind === 'action' && stepB.kind === 'action') {
|
|
16
|
+
return stepA.action === stepB.action;
|
|
17
|
+
}
|
|
18
|
+
if (stepA.kind === 'transfer' && stepB.kind === 'transfer') {
|
|
19
|
+
return stepA.stage === stepB.stage;
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Find the index for a given step
|
|
25
|
+
*/
|
|
26
|
+
const findStepIndex = (step) => flow.findIndex((flowStep) => stepEqual(step, flowStep));
|
|
27
|
+
return {
|
|
28
|
+
has(step) {
|
|
29
|
+
return findStepIndex(step) !== -1;
|
|
30
|
+
},
|
|
31
|
+
can(step) {
|
|
32
|
+
if (state.step === null) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
const indexesDifference = findStepIndex(step) - findStepIndex(state.step);
|
|
36
|
+
// It's possible to send multiple time the same transfer step in a row
|
|
37
|
+
if (indexesDifference === 0 && step.kind === 'transfer') {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
return indexesDifference > 0;
|
|
41
|
+
},
|
|
42
|
+
cannot(step) {
|
|
43
|
+
return !this.can(step);
|
|
44
|
+
},
|
|
45
|
+
set(step) {
|
|
46
|
+
const canSwitch = this.can(step);
|
|
47
|
+
if (!canSwitch) {
|
|
48
|
+
throw new Error('Impossible to proceed to the given step');
|
|
49
|
+
}
|
|
50
|
+
state.step = step;
|
|
51
|
+
return this;
|
|
52
|
+
},
|
|
53
|
+
get() {
|
|
54
|
+
return state.step;
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
exports.createFlow = createFlow;
|
|
59
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Context } from 'koa';
|
|
2
2
|
import type { ServerOptions } from 'ws';
|
|
3
3
|
import { TRANSFER_METHODS } from './constants';
|
|
4
|
-
declare type TransferMethod = typeof TRANSFER_METHODS[number];
|
|
4
|
+
declare type TransferMethod = (typeof TRANSFER_METHODS)[number];
|
|
5
5
|
interface IHandlerOptions {
|
|
6
6
|
verify: (ctx: Context, scope?: TransferMethod) => Promise<void>;
|
|
7
7
|
server?: ServerOptions;
|
|
@@ -9,6 +9,7 @@ const ws_1 = require("ws");
|
|
|
9
9
|
const push_1 = __importDefault(require("./controllers/push"));
|
|
10
10
|
const providers_1 = require("../../errors/providers");
|
|
11
11
|
const constants_1 = require("./constants");
|
|
12
|
+
const flows_1 = require("./flows");
|
|
12
13
|
const createTransferHandler = (options) => {
|
|
13
14
|
const { verify, server: serverOptions } = options;
|
|
14
15
|
// Create the websocket server
|
|
@@ -24,6 +25,12 @@ const createTransferHandler = (options) => {
|
|
|
24
25
|
wss.emit('connection', ws, ctx.req);
|
|
25
26
|
const state = {};
|
|
26
27
|
let uuid;
|
|
28
|
+
function assertValidTransfer(transferState) {
|
|
29
|
+
const { transfer, controller } = transferState;
|
|
30
|
+
if (!controller || !transfer) {
|
|
31
|
+
throw new providers_1.ProviderTransferError('Invalid transfer process');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
27
34
|
/**
|
|
28
35
|
* Format error & message to follow the remote transfer protocol
|
|
29
36
|
*/
|
|
@@ -68,15 +75,28 @@ const createTransferHandler = (options) => {
|
|
|
68
75
|
}
|
|
69
76
|
}
|
|
70
77
|
};
|
|
71
|
-
const
|
|
72
|
-
await verifyAuth(state.transfer?.kind);
|
|
78
|
+
const cleanup = () => {
|
|
73
79
|
delete state.controller;
|
|
74
80
|
delete state.transfer;
|
|
81
|
+
};
|
|
82
|
+
const teardown = async () => {
|
|
83
|
+
if (state.controller) {
|
|
84
|
+
await state.controller.actions.rollback();
|
|
85
|
+
}
|
|
86
|
+
cleanup();
|
|
87
|
+
};
|
|
88
|
+
const end = async (msg) => {
|
|
89
|
+
await verifyAuth(state.transfer?.kind);
|
|
90
|
+
if (msg.params.transferID !== state.transfer?.id) {
|
|
91
|
+
throw new providers_1.ProviderTransferError('Bad transfer ID provided');
|
|
92
|
+
}
|
|
93
|
+
cleanup();
|
|
75
94
|
return { ok: true };
|
|
76
95
|
};
|
|
77
96
|
const init = async (msg) => {
|
|
78
|
-
// TODO:
|
|
79
|
-
|
|
97
|
+
// TODO: For push transfer, we'll probably have to trigger a
|
|
98
|
+
// maintenance mode to prevent other transfer at the same time.
|
|
99
|
+
if (state.transfer || state.controller) {
|
|
80
100
|
throw new providers_1.ProviderInitializationError('Transfer already in progres');
|
|
81
101
|
}
|
|
82
102
|
const { transfer } = msg.params;
|
|
@@ -97,9 +117,27 @@ const createTransferHandler = (options) => {
|
|
|
97
117
|
validTransfers: constants_1.TRANSFER_METHODS,
|
|
98
118
|
});
|
|
99
119
|
}
|
|
100
|
-
state.transfer = {
|
|
120
|
+
state.transfer = {
|
|
121
|
+
id: (0, crypto_1.randomUUID)(),
|
|
122
|
+
kind: transfer,
|
|
123
|
+
startedAt: Date.now(),
|
|
124
|
+
flow: (0, flows_1.createFlow)(flows_1.DEFAULT_TRANSFER_FLOW),
|
|
125
|
+
};
|
|
101
126
|
return { transferID: state.transfer.id };
|
|
102
127
|
};
|
|
128
|
+
const status = () => {
|
|
129
|
+
if (state.transfer) {
|
|
130
|
+
const { transfer } = state;
|
|
131
|
+
const elapsed = Date.now() - transfer.startedAt;
|
|
132
|
+
return {
|
|
133
|
+
active: true,
|
|
134
|
+
kind: transfer.kind,
|
|
135
|
+
startedAt: transfer.startedAt,
|
|
136
|
+
elapsed,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
return { active: false, kind: null, elapsed: null, startedAt: null };
|
|
140
|
+
};
|
|
103
141
|
/**
|
|
104
142
|
* On command message (init, end, status, ...)
|
|
105
143
|
*/
|
|
@@ -109,23 +147,24 @@ const createTransferHandler = (options) => {
|
|
|
109
147
|
await answer(() => init(msg));
|
|
110
148
|
}
|
|
111
149
|
if (command === 'end') {
|
|
112
|
-
await answer(
|
|
150
|
+
await answer(() => {
|
|
151
|
+
assertValidTransfer(state);
|
|
152
|
+
end(msg);
|
|
153
|
+
});
|
|
113
154
|
}
|
|
114
155
|
if (command === 'status') {
|
|
115
|
-
await
|
|
116
|
-
command,
|
|
117
|
-
validCommands: ['init', 'end', 'status'],
|
|
118
|
-
}));
|
|
156
|
+
await answer(status);
|
|
119
157
|
}
|
|
120
158
|
};
|
|
121
159
|
const onTransferCommand = async (msg) => {
|
|
160
|
+
assertValidTransfer(state);
|
|
122
161
|
const { transferID, kind } = msg;
|
|
123
|
-
const { controller } = state;
|
|
124
|
-
await verifyAuth(
|
|
162
|
+
const { controller, transfer } = state;
|
|
163
|
+
await verifyAuth(transfer.kind);
|
|
125
164
|
// TODO: (re)move this check
|
|
126
165
|
// It shouldn't be possible to start a pull transfer for now, so reaching
|
|
127
166
|
// this code should be impossible too, but this has been added by security
|
|
128
|
-
if (
|
|
167
|
+
if (transfer.kind === 'pull') {
|
|
129
168
|
return callback(new providers_1.ProviderTransferError('Pull transfer not implemented'));
|
|
130
169
|
}
|
|
131
170
|
if (!controller) {
|
|
@@ -143,51 +182,98 @@ const createTransferHandler = (options) => {
|
|
|
143
182
|
validActions: Object.keys(controller.actions),
|
|
144
183
|
}));
|
|
145
184
|
}
|
|
185
|
+
const step = { kind: 'action', action };
|
|
186
|
+
const isStepRegistered = transfer.flow.has(step);
|
|
187
|
+
if (isStepRegistered) {
|
|
188
|
+
if (transfer.flow.cannot(step)) {
|
|
189
|
+
return callback(new providers_1.ProviderTransferError(`Invalid action "${action}" found for the current flow `, {
|
|
190
|
+
action,
|
|
191
|
+
}));
|
|
192
|
+
}
|
|
193
|
+
transfer.flow.set(step);
|
|
194
|
+
}
|
|
146
195
|
await answer(() => controller.actions[action]());
|
|
147
196
|
}
|
|
148
197
|
// Transfer
|
|
149
198
|
else if (kind === 'step') {
|
|
150
199
|
// We can only have push transfer message for the moment
|
|
151
200
|
const message = msg;
|
|
152
|
-
|
|
201
|
+
const currentStep = transfer.flow.get();
|
|
202
|
+
const step = { kind: 'transfer', stage: message.step };
|
|
203
|
+
// Lock the current transfer stage
|
|
153
204
|
if (message.action === 'start') {
|
|
154
|
-
|
|
205
|
+
if (currentStep?.kind === 'transfer' && currentStep.locked) {
|
|
206
|
+
return callback(new providers_1.ProviderTransferError(`It's not possible to start a new transfer stage (${message.step}) while another one is in progress (${currentStep.stage})`));
|
|
207
|
+
}
|
|
208
|
+
if (transfer.flow.cannot(step)) {
|
|
209
|
+
return callback(new providers_1.ProviderTransferError(`Invalid stage (${message.step}) provided for the current flow`, { step }));
|
|
210
|
+
}
|
|
211
|
+
transfer?.flow.set({ ...step, locked: true });
|
|
212
|
+
return callback(null, { ok: true });
|
|
155
213
|
}
|
|
156
|
-
// Stream
|
|
157
|
-
|
|
214
|
+
// Stream operation on the current transfer stage
|
|
215
|
+
if (message.action === 'stream') {
|
|
216
|
+
if (currentStep?.kind === 'transfer' && !currentStep.locked) {
|
|
217
|
+
return callback(new providers_1.ProviderTransferError(`You need to initialize the transfer stage (${message.step}) before starting to stream data`));
|
|
218
|
+
}
|
|
219
|
+
if (transfer?.flow.cannot(step)) {
|
|
220
|
+
return callback(new providers_1.ProviderTransferError(`Invalid stage (${message.step}) provided for the current flow`, { step }));
|
|
221
|
+
}
|
|
158
222
|
await answer(() => controller.transfer[message.step]?.(message.data));
|
|
159
223
|
}
|
|
160
|
-
//
|
|
161
|
-
|
|
162
|
-
//
|
|
224
|
+
// Unlock the current transfer stage
|
|
225
|
+
if (message.action === 'end') {
|
|
226
|
+
// Cannot unlock if not locked (aka: started)
|
|
227
|
+
if (currentStep?.kind === 'transfer' && !currentStep.locked) {
|
|
228
|
+
return callback(new providers_1.ProviderTransferError(`You need to initialize the transfer stage (${message.step}) before ending it`));
|
|
229
|
+
}
|
|
230
|
+
// Cannot unlock if invalid step provided
|
|
231
|
+
if (transfer?.flow.cannot(step)) {
|
|
232
|
+
return callback(new providers_1.ProviderTransferError(`Invalid stage (${message.step}) provided for the current flow`, { step }));
|
|
233
|
+
}
|
|
234
|
+
transfer?.flow.set({ ...step, locked: false });
|
|
235
|
+
return callback(null, { ok: true });
|
|
163
236
|
}
|
|
164
237
|
}
|
|
165
238
|
};
|
|
166
|
-
ws.on('close', () => {
|
|
167
|
-
teardown();
|
|
239
|
+
ws.on('close', async () => {
|
|
240
|
+
await teardown();
|
|
168
241
|
});
|
|
169
|
-
ws.on('error', (e) => {
|
|
170
|
-
teardown();
|
|
242
|
+
ws.on('error', async (e) => {
|
|
243
|
+
await teardown();
|
|
171
244
|
strapi.log.error(e);
|
|
172
245
|
});
|
|
173
246
|
ws.on('message', async (raw) => {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
247
|
+
try {
|
|
248
|
+
const msg = JSON.parse(raw.toString());
|
|
249
|
+
if (!msg.uuid) {
|
|
250
|
+
await callback(new providers_1.ProviderTransferError('Missing uuid in message'));
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
uuid = msg.uuid;
|
|
254
|
+
// Regular command message (init, end, status)
|
|
255
|
+
if (msg.type === 'command') {
|
|
256
|
+
await onCommand(msg);
|
|
257
|
+
}
|
|
258
|
+
// Transfer message (the transfer must be initialized first)
|
|
259
|
+
else if (msg.type === 'transfer') {
|
|
260
|
+
await onTransferCommand(msg);
|
|
261
|
+
}
|
|
262
|
+
// Invalid messages
|
|
263
|
+
else {
|
|
264
|
+
await callback(new providers_1.ProviderTransferError('Bad request'));
|
|
265
|
+
}
|
|
187
266
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
267
|
+
catch (e) {
|
|
268
|
+
// Only known errors should be returned to client
|
|
269
|
+
if (e instanceof providers_1.ProviderError || e instanceof SyntaxError) {
|
|
270
|
+
await callback(e);
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
// TODO: log error to server?
|
|
274
|
+
// Unknown errors should not be sent to client
|
|
275
|
+
await callback(new providers_1.ProviderTransferError('Unknown transfer error'));
|
|
276
|
+
}
|
|
191
277
|
}
|
|
192
278
|
});
|
|
193
279
|
});
|
package/lib/utils/transaction.js
CHANGED
|
@@ -13,12 +13,26 @@ const createTransaction = (strapi) => {
|
|
|
13
13
|
resume?.();
|
|
14
14
|
});
|
|
15
15
|
e.on('close', () => {
|
|
16
|
+
e.removeAllListeners('rollback');
|
|
17
|
+
e.removeAllListeners('spawn');
|
|
16
18
|
done = true;
|
|
17
19
|
resume?.();
|
|
18
20
|
});
|
|
19
21
|
strapi.db.transaction(async ({ trx, rollback }) => {
|
|
20
|
-
e.
|
|
21
|
-
|
|
22
|
+
e.once('rollback', async () => {
|
|
23
|
+
e.removeAllListeners('close');
|
|
24
|
+
e.removeAllListeners('spawn');
|
|
25
|
+
try {
|
|
26
|
+
await rollback();
|
|
27
|
+
e.emit('rollback_completed');
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
e.emit('rollback_failed');
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
done = true;
|
|
34
|
+
resume?.();
|
|
35
|
+
}
|
|
22
36
|
});
|
|
23
37
|
while (!done) {
|
|
24
38
|
while (fns.length) {
|
|
@@ -62,7 +76,11 @@ const createTransaction = (strapi) => {
|
|
|
62
76
|
return e.emit('close');
|
|
63
77
|
},
|
|
64
78
|
rollback() {
|
|
65
|
-
return
|
|
79
|
+
return new Promise((resolve) => {
|
|
80
|
+
e.emit('rollback');
|
|
81
|
+
e.once('rollback_failed', () => resolve(false));
|
|
82
|
+
e.once('rollback_completed', () => resolve(true));
|
|
83
|
+
});
|
|
66
84
|
},
|
|
67
85
|
};
|
|
68
86
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strapi/data-transfer",
|
|
3
|
-
"version": "4.7.0-
|
|
3
|
+
"version": "4.7.0-exp.117579f4c13806c2cd518e7d7d2f9d0c8a20107d",
|
|
4
4
|
"description": "Data transfer capabilities for Strapi",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"strapi",
|
|
@@ -39,12 +39,12 @@
|
|
|
39
39
|
"lib": "./lib"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@strapi/logger": "4.7.0-
|
|
43
|
-
"@strapi/strapi": "4.7.0-
|
|
42
|
+
"@strapi/logger": "4.7.0-exp.117579f4c13806c2cd518e7d7d2f9d0c8a20107d",
|
|
43
|
+
"@strapi/strapi": "4.7.0-exp.117579f4c13806c2cd518e7d7d2f9d0c8a20107d",
|
|
44
44
|
"chalk": "4.1.2",
|
|
45
45
|
"fs-extra": "10.0.0",
|
|
46
46
|
"lodash": "4.17.21",
|
|
47
|
-
"prettier": "2.
|
|
47
|
+
"prettier": "2.8.4",
|
|
48
48
|
"semver": "7.3.8",
|
|
49
49
|
"stream-chain": "2.2.5",
|
|
50
50
|
"stream-json": "1.7.4",
|
|
@@ -73,5 +73,5 @@
|
|
|
73
73
|
"node": ">=14.19.1 <=18.x.x",
|
|
74
74
|
"npm": ">=6.0.0"
|
|
75
75
|
},
|
|
76
|
-
"gitHead": "
|
|
76
|
+
"gitHead": "117579f4c13806c2cd518e7d7d2f9d0c8a20107d"
|
|
77
77
|
}
|