@strapi/data-transfer 4.9.0-alpha.0 → 4.9.0-beta.2
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.d.ts +2 -1
- package/lib/engine/index.js +67 -7
- package/lib/errors/constants.d.ts +1 -1
- package/lib/file/providers/destination/index.js +7 -0
- package/lib/strapi/index.d.ts +0 -1
- package/lib/strapi/index.js +1 -6
- package/lib/strapi/providers/index.d.ts +1 -0
- package/lib/strapi/providers/index.js +1 -0
- package/lib/strapi/providers/local-destination/index.d.ts +1 -1
- package/lib/strapi/providers/local-destination/index.js +9 -4
- package/lib/strapi/providers/remote-destination/index.d.ts +5 -8
- package/lib/strapi/providers/remote-destination/index.js +157 -59
- package/lib/strapi/providers/remote-source/index.d.ts +36 -0
- package/lib/strapi/providers/remote-source/index.js +228 -0
- package/lib/strapi/providers/{remote-destination/utils.d.ts → utils.d.ts} +3 -3
- package/lib/strapi/providers/{remote-destination/utils.js → utils.js} +2 -2
- package/lib/strapi/remote/constants.d.ts +4 -2
- package/lib/strapi/remote/constants.js +1 -1
- 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/abstract.d.ts +62 -0
- package/lib/strapi/remote/handlers/abstract.js +3 -0
- package/lib/strapi/remote/handlers/constants.d.ts +2 -0
- package/lib/strapi/remote/handlers/constants.js +5 -0
- package/lib/strapi/remote/handlers/index.d.ts +3 -0
- package/lib/strapi/remote/handlers/index.js +10 -0
- package/lib/strapi/remote/handlers/pull.d.ts +22 -0
- package/lib/strapi/remote/handlers/pull.js +186 -0
- package/lib/strapi/remote/handlers/push.d.ts +75 -0
- package/lib/strapi/remote/handlers/push.js +297 -0
- package/lib/strapi/remote/handlers/utils.d.ts +25 -0
- package/lib/strapi/remote/handlers/utils.js +181 -0
- package/lib/strapi/remote/index.d.ts +1 -2
- package/lib/strapi/remote/index.js +2 -3
- package/lib/utils/transaction.js +21 -3
- package/package.json +8 -7
- package/lib/strapi/register.d.ts +0 -7
- package/lib/strapi/register.js +0 -13
- package/lib/strapi/remote/controllers/index.d.ts +0 -1
- package/lib/strapi/remote/controllers/index.js +0 -18
- package/lib/strapi/remote/controllers/push.d.ts +0 -25
- package/lib/strapi/remote/controllers/push.js +0 -95
- package/lib/strapi/remote/handlers.d.ts +0 -3
- package/lib/strapi/remote/handlers.js +0 -193
- package/lib/strapi/remote/routes.d.ts +0 -21
- package/lib/strapi/remote/routes.js +0 -22
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handlerControllerFactory = exports.handleWSUpgrade = exports.isDataTransferMessage = exports.assertValidHeader = exports.transformUpgradeHeader = void 0;
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
5
|
+
const ws_1 = require("ws");
|
|
6
|
+
const providers_1 = require("../../../errors/providers");
|
|
7
|
+
const constants_1 = require("./constants");
|
|
8
|
+
const transformUpgradeHeader = (header = '') => {
|
|
9
|
+
return header.split(',').map((s) => s.trim().toLowerCase());
|
|
10
|
+
};
|
|
11
|
+
exports.transformUpgradeHeader = transformUpgradeHeader;
|
|
12
|
+
/**
|
|
13
|
+
* Make sure that the upgrade header is a valid websocket one
|
|
14
|
+
*/
|
|
15
|
+
const assertValidHeader = (ctx) => {
|
|
16
|
+
const upgradeHeader = (0, exports.transformUpgradeHeader)(ctx.headers.upgrade);
|
|
17
|
+
if (!upgradeHeader.includes('websocket')) {
|
|
18
|
+
throw new Error('Invalid Header');
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
exports.assertValidHeader = assertValidHeader;
|
|
22
|
+
const isDataTransferMessage = (message) => {
|
|
23
|
+
if (!message || typeof message !== 'object') {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
const { uuid, type } = message;
|
|
27
|
+
if (typeof uuid !== 'string' || typeof type !== 'string') {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
if (!['command', 'transfer'].includes(type)) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
return true;
|
|
34
|
+
};
|
|
35
|
+
exports.isDataTransferMessage = isDataTransferMessage;
|
|
36
|
+
/**
|
|
37
|
+
* Handle the upgrade to ws connection
|
|
38
|
+
*/
|
|
39
|
+
const handleWSUpgrade = (wss, ctx, callback) => {
|
|
40
|
+
(0, exports.assertValidHeader)(ctx);
|
|
41
|
+
wss.handleUpgrade(ctx.req, ctx.request.socket, Buffer.alloc(0), (client, request) => {
|
|
42
|
+
// Create a connection between the client & the server
|
|
43
|
+
wss.emit('connection', client, ctx.req);
|
|
44
|
+
// Invoke the ws callback
|
|
45
|
+
callback(client, request);
|
|
46
|
+
});
|
|
47
|
+
ctx.respond = false;
|
|
48
|
+
};
|
|
49
|
+
exports.handleWSUpgrade = handleWSUpgrade;
|
|
50
|
+
// Protocol related functions
|
|
51
|
+
const handlerControllerFactory = (implementation) => (options) => {
|
|
52
|
+
const { verify, server: serverOptions } = options ?? {};
|
|
53
|
+
const wss = new ws_1.WebSocket.Server({ ...serverOptions, noServer: true });
|
|
54
|
+
return async (ctx) => {
|
|
55
|
+
(0, exports.handleWSUpgrade)(wss, ctx, (ws) => {
|
|
56
|
+
const state = { id: undefined };
|
|
57
|
+
const prototype = {
|
|
58
|
+
// Transfer ID
|
|
59
|
+
get transferID() {
|
|
60
|
+
return state.id;
|
|
61
|
+
},
|
|
62
|
+
set transferID(id) {
|
|
63
|
+
state.id = id;
|
|
64
|
+
},
|
|
65
|
+
// Started at
|
|
66
|
+
get startedAt() {
|
|
67
|
+
return state.startedAt;
|
|
68
|
+
},
|
|
69
|
+
set startedAt(timestamp) {
|
|
70
|
+
state.startedAt = timestamp;
|
|
71
|
+
},
|
|
72
|
+
isTransferStarted() {
|
|
73
|
+
return this.transferID !== undefined && this.startedAt !== undefined;
|
|
74
|
+
},
|
|
75
|
+
assertValidTransfer() {
|
|
76
|
+
const isStarted = this.isTransferStarted();
|
|
77
|
+
if (!isStarted) {
|
|
78
|
+
throw new Error('Invalid Transfer Process');
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
assertValidTransferCommand(command) {
|
|
82
|
+
const isDefined = typeof this[command] === 'function';
|
|
83
|
+
const isValidTransferCommand = constants_1.VALID_TRANSFER_COMMANDS.includes(command);
|
|
84
|
+
if (!isDefined || !isValidTransferCommand) {
|
|
85
|
+
throw new Error('Invalid transfer command');
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
respond(uuid, e, data) {
|
|
89
|
+
return new Promise((resolve, reject) => {
|
|
90
|
+
if (!uuid && !e) {
|
|
91
|
+
reject(new Error('Missing uuid for this message'));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const payload = JSON.stringify({
|
|
95
|
+
uuid,
|
|
96
|
+
data: data ?? null,
|
|
97
|
+
error: e
|
|
98
|
+
? {
|
|
99
|
+
code: e?.name ?? 'ERR',
|
|
100
|
+
message: e?.message,
|
|
101
|
+
}
|
|
102
|
+
: null,
|
|
103
|
+
});
|
|
104
|
+
this.send(payload, (error) => (error ? reject(error) : resolve()));
|
|
105
|
+
});
|
|
106
|
+
},
|
|
107
|
+
send(message, cb) {
|
|
108
|
+
ws.send(message, cb);
|
|
109
|
+
},
|
|
110
|
+
confirm(message) {
|
|
111
|
+
return new Promise((resolve, reject) => {
|
|
112
|
+
const uuid = (0, crypto_1.randomUUID)();
|
|
113
|
+
const payload = JSON.stringify({ uuid, data: message });
|
|
114
|
+
this.send(payload, (error) => {
|
|
115
|
+
if (error) {
|
|
116
|
+
reject(error);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
const onResponse = (raw) => {
|
|
120
|
+
const response = JSON.parse(raw.toString());
|
|
121
|
+
if (response.uuid === uuid) {
|
|
122
|
+
if (response.error) {
|
|
123
|
+
return reject(new Error(response.error.message));
|
|
124
|
+
}
|
|
125
|
+
resolve(response.data ?? null);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
ws.once('message', onResponse);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
ws.once('message', onResponse);
|
|
132
|
+
});
|
|
133
|
+
},
|
|
134
|
+
async executeAndRespond(uuid, fn) {
|
|
135
|
+
try {
|
|
136
|
+
const response = await fn();
|
|
137
|
+
this.respond(uuid, null, response);
|
|
138
|
+
}
|
|
139
|
+
catch (e) {
|
|
140
|
+
if (e instanceof Error) {
|
|
141
|
+
this.respond(uuid, e);
|
|
142
|
+
}
|
|
143
|
+
else if (typeof e === 'string') {
|
|
144
|
+
this.respond(uuid, new providers_1.ProviderTransferError(e));
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
this.respond(uuid, new providers_1.ProviderTransferError('Unexpected error', {
|
|
148
|
+
error: e,
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
cleanup() {
|
|
154
|
+
this.transferID = undefined;
|
|
155
|
+
this.startedAt = undefined;
|
|
156
|
+
},
|
|
157
|
+
teardown() {
|
|
158
|
+
this.cleanup();
|
|
159
|
+
},
|
|
160
|
+
verifyAuth(scope) {
|
|
161
|
+
return verify(ctx, scope);
|
|
162
|
+
},
|
|
163
|
+
// Transfer commands
|
|
164
|
+
init() { },
|
|
165
|
+
end() { },
|
|
166
|
+
status() { },
|
|
167
|
+
// Default prototype implementation for events
|
|
168
|
+
onMessage() { },
|
|
169
|
+
onError() { },
|
|
170
|
+
onClose() { },
|
|
171
|
+
};
|
|
172
|
+
const handler = Object.assign(Object.create(prototype), implementation(prototype));
|
|
173
|
+
// Bind ws events to handler methods
|
|
174
|
+
ws.on('close', (...args) => handler.onClose(...args));
|
|
175
|
+
ws.on('error', (...args) => handler.onError(...args));
|
|
176
|
+
ws.on('message', (...args) => handler.onMessage(...args));
|
|
177
|
+
});
|
|
178
|
+
};
|
|
179
|
+
};
|
|
180
|
+
exports.handlerControllerFactory = handlerControllerFactory;
|
|
181
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -23,8 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.
|
|
27
|
-
exports.controllers = __importStar(require("./controllers"));
|
|
28
|
-
exports.routes = __importStar(require("./routes"));
|
|
26
|
+
exports.handlers = exports.constants = void 0;
|
|
29
27
|
exports.constants = __importStar(require("./constants"));
|
|
28
|
+
exports.handlers = __importStar(require("./handlers"));
|
|
30
29
|
//# sourceMappingURL=index.js.map
|
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.9.0-
|
|
3
|
+
"version": "4.9.0-beta.2",
|
|
4
4
|
"description": "Data transfer capabilities for Strapi",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"strapi",
|
|
@@ -39,16 +39,15 @@
|
|
|
39
39
|
"lib": "./lib"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@strapi/logger": "4.9.0-
|
|
43
|
-
"@strapi/strapi": "4.9.0-
|
|
42
|
+
"@strapi/logger": "4.9.0-beta.2",
|
|
43
|
+
"@strapi/strapi": "4.9.0-beta.2",
|
|
44
44
|
"chalk": "4.1.2",
|
|
45
45
|
"fs-extra": "10.0.0",
|
|
46
46
|
"lodash": "4.17.21",
|
|
47
|
-
"prettier": "2.7.1",
|
|
48
47
|
"semver": "7.3.8",
|
|
49
48
|
"stream-chain": "2.2.5",
|
|
50
49
|
"stream-json": "1.7.4",
|
|
51
|
-
"tar": "6.1.
|
|
50
|
+
"tar": "6.1.13",
|
|
52
51
|
"tar-stream": "2.2.0",
|
|
53
52
|
"uuid": "9.0.0",
|
|
54
53
|
"ws": "8.11.0"
|
|
@@ -58,12 +57,14 @@
|
|
|
58
57
|
"@types/fs-extra": "9.0.13",
|
|
59
58
|
"@types/jest": "29.2.0",
|
|
60
59
|
"@types/koa": "2.13.4",
|
|
60
|
+
"@types/lodash": "^4.14.191",
|
|
61
61
|
"@types/semver": "7.3.13",
|
|
62
62
|
"@types/stream-chain": "2.0.1",
|
|
63
63
|
"@types/stream-json": "1.7.3",
|
|
64
|
-
"@types/tar": "6.1.
|
|
64
|
+
"@types/tar": "6.1.4",
|
|
65
65
|
"@types/tar-stream": "2.2.2",
|
|
66
66
|
"@types/uuid": "9.0.0",
|
|
67
|
+
"@types/ws": "^8.5.4",
|
|
67
68
|
"knex": "2.4.0",
|
|
68
69
|
"koa": "2.13.4",
|
|
69
70
|
"rimraf": "3.0.2",
|
|
@@ -73,5 +74,5 @@
|
|
|
73
74
|
"node": ">=14.19.1 <=18.x.x",
|
|
74
75
|
"npm": ">=6.0.0"
|
|
75
76
|
},
|
|
76
|
-
"gitHead": "
|
|
77
|
+
"gitHead": "d893ead642592a15a95fefb71c544aaabe4db20b"
|
|
77
78
|
}
|
package/lib/strapi/register.d.ts
DELETED
package/lib/strapi/register.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const remote_1 = require("./remote");
|
|
4
|
-
/**
|
|
5
|
-
* This is intended to be called on Strapi register phase.
|
|
6
|
-
*
|
|
7
|
-
* It registers a transfer route in the Strapi admin router.
|
|
8
|
-
*/
|
|
9
|
-
const register = (strapi) => {
|
|
10
|
-
remote_1.routes.registerAdminTransferRoute(strapi);
|
|
11
|
-
};
|
|
12
|
-
exports.default = register;
|
|
13
|
-
//# sourceMappingURL=register.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './push';
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./push"), exports);
|
|
18
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
import { Writable } from 'stream-chain';
|
|
3
|
-
import type { IMetadata } from '../../../../types';
|
|
4
|
-
import type { TransferPushMessage, TransferPushStep } from '../../../../types/remote/protocol/client';
|
|
5
|
-
import { ILocalStrapiDestinationProviderOptions } from '../../providers';
|
|
6
|
-
export interface IPushController {
|
|
7
|
-
streams: {
|
|
8
|
-
[stage in TransferPushStep]?: Writable;
|
|
9
|
-
};
|
|
10
|
-
actions: {
|
|
11
|
-
getMetadata(): Promise<IMetadata>;
|
|
12
|
-
getSchemas(): Strapi.Schemas;
|
|
13
|
-
bootstrap(): Promise<void>;
|
|
14
|
-
close(): Promise<void>;
|
|
15
|
-
beforeTransfer(): Promise<void>;
|
|
16
|
-
};
|
|
17
|
-
transfer: {
|
|
18
|
-
[key in TransferPushStep]: <T extends TransferPushMessage>(value: T extends {
|
|
19
|
-
step: key;
|
|
20
|
-
data: infer U;
|
|
21
|
-
} ? U : never) => Promise<void>;
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
declare const createPushController: (options: ILocalStrapiDestinationProviderOptions) => IPushController;
|
|
25
|
-
export default createPushController;
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const stream_chain_1 = require("stream-chain");
|
|
4
|
-
const providers_1 = require("../../providers");
|
|
5
|
-
const createPushController = (options) => {
|
|
6
|
-
const provider = (0, providers_1.createLocalStrapiDestinationProvider)(options);
|
|
7
|
-
const streams = {};
|
|
8
|
-
const assets = {};
|
|
9
|
-
const writeAsync = (stream, data) => {
|
|
10
|
-
return new Promise((resolve, reject) => {
|
|
11
|
-
stream.write(data, (error) => {
|
|
12
|
-
if (error) {
|
|
13
|
-
reject(error);
|
|
14
|
-
}
|
|
15
|
-
resolve();
|
|
16
|
-
});
|
|
17
|
-
});
|
|
18
|
-
};
|
|
19
|
-
return {
|
|
20
|
-
streams,
|
|
21
|
-
actions: {
|
|
22
|
-
async getSchemas() {
|
|
23
|
-
return provider.getSchemas();
|
|
24
|
-
},
|
|
25
|
-
async getMetadata() {
|
|
26
|
-
return provider.getMetadata();
|
|
27
|
-
},
|
|
28
|
-
async bootstrap() {
|
|
29
|
-
return provider.bootstrap();
|
|
30
|
-
},
|
|
31
|
-
async close() {
|
|
32
|
-
return provider.close();
|
|
33
|
-
},
|
|
34
|
-
async beforeTransfer() {
|
|
35
|
-
return provider.beforeTransfer();
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
transfer: {
|
|
39
|
-
async entities(entity) {
|
|
40
|
-
if (!streams.entities) {
|
|
41
|
-
streams.entities = provider.createEntitiesWriteStream();
|
|
42
|
-
}
|
|
43
|
-
await writeAsync(streams.entities, entity);
|
|
44
|
-
},
|
|
45
|
-
async links(link) {
|
|
46
|
-
if (!streams.links) {
|
|
47
|
-
streams.links = await provider.createLinksWriteStream();
|
|
48
|
-
}
|
|
49
|
-
await writeAsync(streams.links, link);
|
|
50
|
-
},
|
|
51
|
-
async configuration(config) {
|
|
52
|
-
if (!streams.configuration) {
|
|
53
|
-
streams.configuration = await provider.createConfigurationWriteStream();
|
|
54
|
-
}
|
|
55
|
-
await writeAsync(streams.configuration, config);
|
|
56
|
-
},
|
|
57
|
-
async assets(payload) {
|
|
58
|
-
// TODO: close the stream upong receiving an 'end' event instead
|
|
59
|
-
if (payload === null) {
|
|
60
|
-
streams.assets?.end();
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
const { action, assetID } = payload;
|
|
64
|
-
if (!streams.assets) {
|
|
65
|
-
streams.assets = await provider.createAssetsWriteStream();
|
|
66
|
-
}
|
|
67
|
-
if (action === 'start') {
|
|
68
|
-
assets[assetID] = { ...payload.data, stream: new stream_chain_1.PassThrough() };
|
|
69
|
-
writeAsync(streams.assets, assets[assetID]);
|
|
70
|
-
}
|
|
71
|
-
if (action === 'stream') {
|
|
72
|
-
// The buffer has gone through JSON operations and is now of shape { type: "Buffer"; data: UInt8Array }
|
|
73
|
-
// We need to transform it back into a Buffer instance
|
|
74
|
-
const rawBuffer = payload.data;
|
|
75
|
-
const chunk = Buffer.from(rawBuffer.data);
|
|
76
|
-
await writeAsync(assets[assetID].stream, chunk);
|
|
77
|
-
}
|
|
78
|
-
if (action === 'end') {
|
|
79
|
-
await new Promise((resolve, reject) => {
|
|
80
|
-
const { stream } = assets[assetID];
|
|
81
|
-
stream
|
|
82
|
-
.on('close', () => {
|
|
83
|
-
delete assets[assetID];
|
|
84
|
-
resolve();
|
|
85
|
-
})
|
|
86
|
-
.on('error', reject)
|
|
87
|
-
.end();
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
};
|
|
93
|
-
};
|
|
94
|
-
exports.default = createPushController;
|
|
95
|
-
//# sourceMappingURL=push.js.map
|
|
@@ -1,193 +0,0 @@
|
|
|
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.createTransferHandler = void 0;
|
|
7
|
-
const crypto_1 = require("crypto");
|
|
8
|
-
const ws_1 = require("ws");
|
|
9
|
-
const push_1 = __importDefault(require("./controllers/push"));
|
|
10
|
-
const providers_1 = require("../../errors/providers");
|
|
11
|
-
const constants_1 = require("./constants");
|
|
12
|
-
const createTransferHandler = (options = {}) => async (ctx) => {
|
|
13
|
-
const upgradeHeader = (ctx.request.headers.upgrade || '')
|
|
14
|
-
.split(',')
|
|
15
|
-
.map((s) => s.trim().toLowerCase());
|
|
16
|
-
// Create the websocket server
|
|
17
|
-
const wss = new ws_1.WebSocket.Server({ ...options, noServer: true });
|
|
18
|
-
if (upgradeHeader.includes('websocket')) {
|
|
19
|
-
wss.handleUpgrade(ctx.req, ctx.request.socket, Buffer.alloc(0), (ws) => {
|
|
20
|
-
// Create a connection between the client & the server
|
|
21
|
-
wss.emit('connection', ws, ctx.req);
|
|
22
|
-
const state = {};
|
|
23
|
-
let uuid;
|
|
24
|
-
/**
|
|
25
|
-
* Format error & message to follow the remote transfer protocol
|
|
26
|
-
*/
|
|
27
|
-
const callback = (e = null, data) => {
|
|
28
|
-
return new Promise((resolve, reject) => {
|
|
29
|
-
if (!uuid) {
|
|
30
|
-
reject(new Error('Missing uuid for this message'));
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
const payload = JSON.stringify({
|
|
34
|
-
uuid,
|
|
35
|
-
data: data ?? null,
|
|
36
|
-
error: e
|
|
37
|
-
? {
|
|
38
|
-
code: 'ERR',
|
|
39
|
-
message: e?.message,
|
|
40
|
-
}
|
|
41
|
-
: null,
|
|
42
|
-
});
|
|
43
|
-
ws.send(payload, (error) => (error ? reject(error) : resolve()));
|
|
44
|
-
});
|
|
45
|
-
};
|
|
46
|
-
/**
|
|
47
|
-
* Wrap a function call to catch errors and answer the request with the correct format
|
|
48
|
-
*/
|
|
49
|
-
const answer = async (fn) => {
|
|
50
|
-
try {
|
|
51
|
-
const response = await fn();
|
|
52
|
-
callback(null, response);
|
|
53
|
-
}
|
|
54
|
-
catch (e) {
|
|
55
|
-
if (e instanceof Error) {
|
|
56
|
-
callback(e);
|
|
57
|
-
}
|
|
58
|
-
else if (typeof e === 'string') {
|
|
59
|
-
callback(new providers_1.ProviderTransferError(e));
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
callback(new providers_1.ProviderTransferError('Unexpected error', {
|
|
63
|
-
error: e,
|
|
64
|
-
}));
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
const teardown = () => {
|
|
69
|
-
delete state.controller;
|
|
70
|
-
delete state.transfer;
|
|
71
|
-
return { ok: true };
|
|
72
|
-
};
|
|
73
|
-
const init = (msg) => {
|
|
74
|
-
// TODO: this only checks for this instance of node: we should consider a database lock
|
|
75
|
-
if (state.controller) {
|
|
76
|
-
throw new providers_1.ProviderInitializationError('Transfer already in progres');
|
|
77
|
-
}
|
|
78
|
-
const { transfer } = msg.params;
|
|
79
|
-
// Push transfer
|
|
80
|
-
if (transfer === 'push') {
|
|
81
|
-
const { options: controllerOptions } = msg.params;
|
|
82
|
-
state.controller = (0, push_1.default)({
|
|
83
|
-
...controllerOptions,
|
|
84
|
-
autoDestroy: false,
|
|
85
|
-
getStrapi: () => strapi,
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
// Pull or any other string
|
|
89
|
-
else {
|
|
90
|
-
throw new providers_1.ProviderTransferError(`Transfer type not implemented: "${transfer}"`, {
|
|
91
|
-
transfer,
|
|
92
|
-
validTransfers: constants_1.TRANSFER_METHODS,
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
state.transfer = { id: (0, crypto_1.randomUUID)(), kind: transfer };
|
|
96
|
-
return { transferID: state.transfer.id };
|
|
97
|
-
};
|
|
98
|
-
/**
|
|
99
|
-
* On command message (init, end, status, ...)
|
|
100
|
-
*/
|
|
101
|
-
const onCommand = async (msg) => {
|
|
102
|
-
const { command } = msg;
|
|
103
|
-
if (command === 'init') {
|
|
104
|
-
await answer(() => init(msg));
|
|
105
|
-
}
|
|
106
|
-
if (command === 'end') {
|
|
107
|
-
await answer(teardown);
|
|
108
|
-
}
|
|
109
|
-
if (command === 'status') {
|
|
110
|
-
await callback(new providers_1.ProviderTransferError('Command not implemented: "status"', {
|
|
111
|
-
command,
|
|
112
|
-
validCommands: ['init', 'end', 'status'],
|
|
113
|
-
}));
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
const onTransferCommand = async (msg) => {
|
|
117
|
-
const { transferID, kind } = msg;
|
|
118
|
-
const { controller } = state;
|
|
119
|
-
// TODO: (re)move this check
|
|
120
|
-
// It shouldn't be possible to start a pull transfer for now, so reaching
|
|
121
|
-
// this code should be impossible too, but this has been added by security
|
|
122
|
-
if (state.transfer?.kind === 'pull') {
|
|
123
|
-
return callback(new providers_1.ProviderTransferError('Pull transfer not implemented'));
|
|
124
|
-
}
|
|
125
|
-
if (!controller) {
|
|
126
|
-
return callback(new providers_1.ProviderTransferError("The transfer hasn't been initialized"));
|
|
127
|
-
}
|
|
128
|
-
if (!transferID) {
|
|
129
|
-
return callback(new providers_1.ProviderTransferError('Missing transfer ID'));
|
|
130
|
-
}
|
|
131
|
-
// Action
|
|
132
|
-
if (kind === 'action') {
|
|
133
|
-
const { action } = msg;
|
|
134
|
-
if (!(action in controller.actions)) {
|
|
135
|
-
return callback(new providers_1.ProviderTransferError(`Invalid action provided: "${action}"`, {
|
|
136
|
-
action,
|
|
137
|
-
validActions: Object.keys(controller.actions),
|
|
138
|
-
}));
|
|
139
|
-
}
|
|
140
|
-
await answer(() => controller.actions[action]());
|
|
141
|
-
}
|
|
142
|
-
// Transfer
|
|
143
|
-
else if (kind === 'step') {
|
|
144
|
-
// We can only have push transfer message for the moment
|
|
145
|
-
const message = msg;
|
|
146
|
-
// TODO: lock transfer process
|
|
147
|
-
if (message.action === 'start') {
|
|
148
|
-
// console.log('Starting transfer for ', message.step);
|
|
149
|
-
}
|
|
150
|
-
// Stream step
|
|
151
|
-
else if (message.action === 'stream') {
|
|
152
|
-
await answer(() => controller.transfer[message.step]?.(message.data));
|
|
153
|
-
}
|
|
154
|
-
// TODO: unlock transfer process
|
|
155
|
-
else if (message.action === 'end') {
|
|
156
|
-
// console.log('Ending transfer for ', message.step);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
ws.on('close', () => {
|
|
161
|
-
teardown();
|
|
162
|
-
});
|
|
163
|
-
ws.on('error', (e) => {
|
|
164
|
-
teardown();
|
|
165
|
-
// TODO: is logging a console error to the running instance of Strapi ok to do? Should we check for an existing strapi.logger to use?
|
|
166
|
-
console.error(e);
|
|
167
|
-
});
|
|
168
|
-
ws.on('message', async (raw) => {
|
|
169
|
-
const msg = JSON.parse(raw.toString());
|
|
170
|
-
if (!msg.uuid) {
|
|
171
|
-
await callback(new providers_1.ProviderTransferError('Missing uuid in message'));
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
uuid = msg.uuid;
|
|
175
|
-
// Regular command message (init, end, status)
|
|
176
|
-
if (msg.type === 'command') {
|
|
177
|
-
await onCommand(msg);
|
|
178
|
-
}
|
|
179
|
-
// Transfer message (the transfer must be initialized first)
|
|
180
|
-
else if (msg.type === 'transfer') {
|
|
181
|
-
await onTransferCommand(msg);
|
|
182
|
-
}
|
|
183
|
-
// Invalid messages
|
|
184
|
-
else {
|
|
185
|
-
await callback(new providers_1.ProviderTransferError('Bad request'));
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
});
|
|
189
|
-
ctx.respond = false;
|
|
190
|
-
}
|
|
191
|
-
};
|
|
192
|
-
exports.createTransferHandler = createTransferHandler;
|
|
193
|
-
//# sourceMappingURL=handlers.js.map
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { Context } from 'koa';
|
|
2
|
-
declare module '@strapi/strapi' {
|
|
3
|
-
interface Strapi {
|
|
4
|
-
admin: {
|
|
5
|
-
routes: {
|
|
6
|
-
method: string;
|
|
7
|
-
path: string;
|
|
8
|
-
handler: (ctx: Context) => Promise<void>;
|
|
9
|
-
config: unknown;
|
|
10
|
-
}[];
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Register a transfer route in the Strapi admin router.
|
|
16
|
-
*
|
|
17
|
-
* It exposes a WS server that can be used to run and manage transfer processes.
|
|
18
|
-
*
|
|
19
|
-
* @param strapi - A Strapi instance
|
|
20
|
-
*/
|
|
21
|
-
export declare const registerAdminTransferRoute: (strapi: Strapi.Strapi) => void;
|