@strapi/data-transfer 4.6.0-beta.1 → 4.6.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/{dist → lib}/engine/index.d.ts +7 -1
- package/{dist → lib}/engine/index.js +74 -18
- package/lib/engine/validation/index.d.ts +1 -0
- package/{dist/providers/shared → lib/engine/validation}/index.js +2 -2
- package/{dist/strategies → lib/engine/validation/schemas}/index.d.ts +2 -2
- package/{dist/strategies → lib/engine/validation/schemas}/index.js +3 -2
- package/lib/file/index.d.ts +1 -0
- package/lib/file/index.js +28 -0
- package/{dist/providers/local-file-destination-provider → lib/file/providers/destination}/index.d.ts +6 -6
- package/{dist/providers/local-file-destination-provider → lib/file/providers/destination}/index.js +7 -7
- package/{dist/providers/local-file-destination-provider → lib/file/providers/destination}/utils.d.ts +0 -0
- package/{dist/providers/local-file-destination-provider → lib/file/providers/destination}/utils.js +0 -0
- package/lib/file/providers/index.d.ts +2 -0
- package/{dist → lib/file}/providers/index.js +2 -6
- package/{dist/providers/local-file-source-provider → lib/file/providers/source}/index.d.ts +9 -8
- package/{dist/providers/local-file-source-provider → lib/file/providers/source}/index.js +33 -45
- package/lib/index.d.ts +4 -0
- package/lib/index.js +31 -0
- package/lib/strapi/index.d.ts +4 -0
- package/lib/strapi/index.js +35 -0
- package/lib/strapi/providers/index.d.ts +3 -0
- package/lib/strapi/providers/index.js +22 -0
- package/{dist/providers/local-strapi-destination-provider → lib/strapi/providers/local-destination}/index.d.ts +8 -7
- package/{dist/providers/local-strapi-destination-provider → lib/strapi/providers/local-destination}/index.js +14 -6
- package/{dist/providers/local-strapi-destination-provider → lib/strapi/providers/local-destination}/strategies/index.d.ts +0 -0
- package/{dist/providers/local-strapi-destination-provider → lib/strapi/providers/local-destination}/strategies/index.js +0 -0
- package/{dist/providers/local-strapi-destination-provider → lib/strapi/providers/local-destination}/strategies/restore/configuration.d.ts +1 -1
- package/{dist/providers/local-strapi-destination-provider → lib/strapi/providers/local-destination}/strategies/restore/configuration.js +0 -0
- package/{dist/providers/local-strapi-destination-provider → lib/strapi/providers/local-destination}/strategies/restore/entities.d.ts +0 -0
- package/{dist/providers/local-strapi-destination-provider → lib/strapi/providers/local-destination}/strategies/restore/entities.js +25 -13
- package/{dist/providers/local-strapi-destination-provider → lib/strapi/providers/local-destination}/strategies/restore/index.d.ts +0 -0
- package/{dist/providers/local-strapi-destination-provider → lib/strapi/providers/local-destination}/strategies/restore/index.js +2 -2
- package/{dist/providers/local-strapi-destination-provider → lib/strapi/providers/local-destination}/strategies/restore/links.d.ts +0 -0
- package/{dist/providers/local-strapi-destination-provider → lib/strapi/providers/local-destination}/strategies/restore/links.js +1 -1
- package/{dist/providers/local-strapi-source-provider → lib/strapi/providers/local-source}/assets.d.ts +0 -0
- package/{dist/providers/local-strapi-source-provider → lib/strapi/providers/local-source}/assets.js +0 -0
- package/{dist/providers/local-strapi-source-provider → lib/strapi/providers/local-source}/configuration.d.ts +0 -0
- package/{dist/providers/local-strapi-source-provider → lib/strapi/providers/local-source}/configuration.js +12 -13
- package/{dist/providers/local-strapi-source-provider → lib/strapi/providers/local-source}/entities.d.ts +0 -0
- package/{dist/providers/local-strapi-source-provider → lib/strapi/providers/local-source}/entities.js +11 -4
- package/{dist/providers/local-strapi-source-provider → lib/strapi/providers/local-source}/index.d.ts +6 -6
- package/{dist/providers/local-strapi-source-provider → lib/strapi/providers/local-source}/index.js +6 -6
- package/{dist/providers/local-strapi-source-provider → lib/strapi/providers/local-source}/links.d.ts +0 -0
- package/{dist/providers/local-strapi-source-provider → lib/strapi/providers/local-source}/links.js +1 -1
- package/lib/strapi/providers/remote-destination/index.d.ts +40 -0
- package/lib/strapi/providers/remote-destination/index.js +171 -0
- package/lib/strapi/providers/remote-destination/utils.d.ts +31 -0
- package/lib/strapi/providers/remote-destination/utils.js +72 -0
- package/{dist/providers/shared/strapi → lib/strapi/queries}/entity.d.ts +0 -0
- package/{dist/providers/shared/strapi → lib/strapi/queries}/entity.js +0 -0
- package/{dist/providers/shared/strapi → lib/strapi/queries}/index.d.ts +0 -0
- package/{dist/providers/shared/strapi → lib/strapi/queries}/index.js +0 -0
- package/{dist/providers/shared/strapi → lib/strapi/queries}/link.d.ts +1 -1
- package/{dist/providers/shared/strapi → lib/strapi/queries}/link.js +0 -0
- package/lib/strapi/register.d.ts +7 -0
- package/lib/strapi/register.js +13 -0
- package/lib/strapi/remote/constants.d.ts +1 -0
- package/lib/strapi/remote/constants.js +5 -0
- package/lib/strapi/remote/controllers/index.d.ts +1 -0
- package/{dist → lib/strapi/remote/controllers}/index.js +1 -2
- package/lib/strapi/remote/controllers/push.d.ts +25 -0
- package/lib/strapi/remote/controllers/push.js +95 -0
- package/lib/strapi/remote/handlers.d.ts +3 -0
- package/lib/strapi/remote/handlers.js +177 -0
- package/lib/strapi/remote/index.d.ts +3 -0
- package/lib/strapi/remote/index.js +30 -0
- package/lib/strapi/remote/routes.d.ts +21 -0
- package/lib/strapi/remote/routes.js +22 -0
- package/lib/utils/encryption/decrypt.d.ts +11 -0
- package/{dist → lib/utils}/encryption/decrypt.js +8 -0
- package/lib/utils/encryption/encrypt.d.ts +11 -0
- package/{dist → lib/utils}/encryption/encrypt.js +8 -0
- package/{dist → lib/utils}/encryption/index.d.ts +0 -0
- package/{dist → lib/utils}/encryption/index.js +0 -0
- package/{dist → lib}/utils/index.d.ts +1 -0
- package/{dist → lib}/utils/index.js +2 -1
- package/{dist → lib}/utils/json.d.ts +7 -0
- package/{dist → lib}/utils/json.js +8 -1
- package/{dist → lib}/utils/schema.d.ts +4 -0
- package/{dist → lib}/utils/schema.js +9 -2
- package/lib/utils/stream.d.ts +27 -0
- package/lib/utils/stream.js +59 -0
- package/package.json +19 -12
- package/dist/encryption/decrypt.d.ts +0 -3
- package/dist/encryption/encrypt.d.ts +0 -3
- package/dist/index.d.ts +0 -2
- package/dist/providers/index.d.ts +0 -4
- package/dist/providers/shared/index.d.ts +0 -1
- package/dist/providers/test-utils/index.d.ts +0 -111
- package/dist/providers/test-utils/index.js +0 -64
- package/dist/utils/stream.d.ts +0 -10
- package/dist/utils/stream.js +0 -39
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
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
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
6
|
+
};
|
|
7
|
+
var _RemoteStrapiDestinationProvider_instances, _RemoteStrapiDestinationProvider_streamStep;
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.createRemoteStrapiDestinationProvider = void 0;
|
|
10
|
+
const ws_1 = require("ws");
|
|
11
|
+
const uuid_1 = require("uuid");
|
|
12
|
+
const stream_1 = require("stream");
|
|
13
|
+
const utils_1 = require("./utils");
|
|
14
|
+
const constants_1 = require("../../remote/constants");
|
|
15
|
+
class RemoteStrapiDestinationProvider {
|
|
16
|
+
constructor(options) {
|
|
17
|
+
_RemoteStrapiDestinationProvider_instances.add(this);
|
|
18
|
+
this.name = 'destination::remote-strapi';
|
|
19
|
+
this.type = 'destination';
|
|
20
|
+
this.options = options;
|
|
21
|
+
this.ws = null;
|
|
22
|
+
this.dispatcher = null;
|
|
23
|
+
}
|
|
24
|
+
async initTransfer() {
|
|
25
|
+
const { strategy, restore } = this.options;
|
|
26
|
+
// Wait for the connection to be made to the server, then init the transfer
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
this.ws
|
|
29
|
+
?.once('open', async () => {
|
|
30
|
+
const query = this.dispatcher?.dispatchCommand({
|
|
31
|
+
command: 'init',
|
|
32
|
+
params: { options: { strategy, restore }, transfer: 'push' },
|
|
33
|
+
});
|
|
34
|
+
const res = (await query);
|
|
35
|
+
if (!res?.transferID) {
|
|
36
|
+
return reject(new Error('Init failed, invalid response from the server'));
|
|
37
|
+
}
|
|
38
|
+
resolve(res.transferID);
|
|
39
|
+
})
|
|
40
|
+
.once('error', reject);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
async bootstrap() {
|
|
44
|
+
const { url, auth } = this.options;
|
|
45
|
+
let ws;
|
|
46
|
+
if (!['https:', 'http:'].includes(url.protocol)) {
|
|
47
|
+
throw new Error(`Invalid protocol "${url.protocol}"`);
|
|
48
|
+
}
|
|
49
|
+
const wsProtocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
50
|
+
const wsUrl = `${wsProtocol}//${url.host}${url.pathname}${constants_1.TRANSFER_PATH}`;
|
|
51
|
+
// No auth defined, trying public access for transfer
|
|
52
|
+
if (!auth) {
|
|
53
|
+
ws = new ws_1.WebSocket(wsUrl);
|
|
54
|
+
}
|
|
55
|
+
// Common token auth, this should be the main auth method
|
|
56
|
+
else if (auth.type === 'token') {
|
|
57
|
+
const headers = { Authorization: `Bearer ${auth.token}` };
|
|
58
|
+
ws = new ws_1.WebSocket(wsUrl, { headers });
|
|
59
|
+
}
|
|
60
|
+
// Invalid auth method provided
|
|
61
|
+
else {
|
|
62
|
+
throw new Error('Auth method not implemented');
|
|
63
|
+
}
|
|
64
|
+
this.ws = ws;
|
|
65
|
+
this.dispatcher = (0, utils_1.createDispatcher)(this.ws);
|
|
66
|
+
const transferID = await this.initTransfer();
|
|
67
|
+
this.dispatcher.setTransferProperties({ id: transferID, kind: 'push' });
|
|
68
|
+
await this.dispatcher.dispatchTransferAction('bootstrap');
|
|
69
|
+
}
|
|
70
|
+
async close() {
|
|
71
|
+
await this.dispatcher?.dispatchTransferAction('close');
|
|
72
|
+
await new Promise((resolve) => {
|
|
73
|
+
const { ws } = this;
|
|
74
|
+
if (!ws || ws.CLOSED) {
|
|
75
|
+
resolve();
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
ws.on('close', () => resolve()).close();
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
getMetadata() {
|
|
82
|
+
return this.dispatcher?.dispatchTransferAction('getMetadata') ?? null;
|
|
83
|
+
}
|
|
84
|
+
async beforeTransfer() {
|
|
85
|
+
await this.dispatcher?.dispatchTransferAction('beforeTransfer');
|
|
86
|
+
}
|
|
87
|
+
getSchemas() {
|
|
88
|
+
if (!this.dispatcher) {
|
|
89
|
+
return Promise.resolve(null);
|
|
90
|
+
}
|
|
91
|
+
return this.dispatcher.dispatchTransferAction('getSchemas');
|
|
92
|
+
}
|
|
93
|
+
createEntitiesWriteStream() {
|
|
94
|
+
return new stream_1.Writable({
|
|
95
|
+
objectMode: true,
|
|
96
|
+
write: async (entity, _encoding, callback) => {
|
|
97
|
+
const e = await __classPrivateFieldGet(this, _RemoteStrapiDestinationProvider_instances, "m", _RemoteStrapiDestinationProvider_streamStep).call(this, 'entities', entity);
|
|
98
|
+
callback(e);
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
createLinksWriteStream() {
|
|
103
|
+
return new stream_1.Writable({
|
|
104
|
+
objectMode: true,
|
|
105
|
+
write: async (link, _encoding, callback) => {
|
|
106
|
+
const e = await __classPrivateFieldGet(this, _RemoteStrapiDestinationProvider_instances, "m", _RemoteStrapiDestinationProvider_streamStep).call(this, 'links', link);
|
|
107
|
+
callback(e);
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
createConfigurationWriteStream() {
|
|
112
|
+
return new stream_1.Writable({
|
|
113
|
+
objectMode: true,
|
|
114
|
+
write: async (configuration, _encoding, callback) => {
|
|
115
|
+
const e = await __classPrivateFieldGet(this, _RemoteStrapiDestinationProvider_instances, "m", _RemoteStrapiDestinationProvider_streamStep).call(this, 'configuration', configuration);
|
|
116
|
+
callback(e);
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
createAssetsWriteStream() {
|
|
121
|
+
return new stream_1.Writable({
|
|
122
|
+
objectMode: true,
|
|
123
|
+
final: async (callback) => {
|
|
124
|
+
// TODO: replace this stream call by an end call
|
|
125
|
+
const e = await __classPrivateFieldGet(this, _RemoteStrapiDestinationProvider_instances, "m", _RemoteStrapiDestinationProvider_streamStep).call(this, 'assets', null);
|
|
126
|
+
callback(e);
|
|
127
|
+
},
|
|
128
|
+
write: async (asset, _encoding, callback) => {
|
|
129
|
+
const { filename, filepath, stats, stream } = asset;
|
|
130
|
+
const assetID = (0, uuid_1.v4)();
|
|
131
|
+
await __classPrivateFieldGet(this, _RemoteStrapiDestinationProvider_instances, "m", _RemoteStrapiDestinationProvider_streamStep).call(this, 'assets', {
|
|
132
|
+
action: 'start',
|
|
133
|
+
assetID,
|
|
134
|
+
data: { filename, filepath, stats },
|
|
135
|
+
});
|
|
136
|
+
for await (const chunk of stream) {
|
|
137
|
+
await __classPrivateFieldGet(this, _RemoteStrapiDestinationProvider_instances, "m", _RemoteStrapiDestinationProvider_streamStep).call(this, 'assets', {
|
|
138
|
+
action: 'stream',
|
|
139
|
+
assetID,
|
|
140
|
+
data: chunk,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
await __classPrivateFieldGet(this, _RemoteStrapiDestinationProvider_instances, "m", _RemoteStrapiDestinationProvider_streamStep).call(this, 'assets', {
|
|
144
|
+
action: 'end',
|
|
145
|
+
assetID,
|
|
146
|
+
});
|
|
147
|
+
callback();
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
_RemoteStrapiDestinationProvider_instances = new WeakSet(), _RemoteStrapiDestinationProvider_streamStep = async function _RemoteStrapiDestinationProvider_streamStep(step, data) {
|
|
153
|
+
try {
|
|
154
|
+
await this.dispatcher?.dispatchTransferStep({ action: 'stream', step, data });
|
|
155
|
+
}
|
|
156
|
+
catch (e) {
|
|
157
|
+
if (e instanceof Error) {
|
|
158
|
+
return e;
|
|
159
|
+
}
|
|
160
|
+
if (typeof e === 'string') {
|
|
161
|
+
return new Error(e);
|
|
162
|
+
}
|
|
163
|
+
return new Error('Unexpected error');
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
};
|
|
167
|
+
const createRemoteStrapiDestinationProvider = (options) => {
|
|
168
|
+
return new RemoteStrapiDestinationProvider(options);
|
|
169
|
+
};
|
|
170
|
+
exports.createRemoteStrapiDestinationProvider = createRemoteStrapiDestinationProvider;
|
|
171
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { WebSocket } from 'ws';
|
|
2
|
+
import type { client } from '../../../../types/remote/protocol';
|
|
3
|
+
interface IDispatcherState {
|
|
4
|
+
transfer?: {
|
|
5
|
+
kind: client.TransferKind;
|
|
6
|
+
id: string;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
interface IDispatchOptions {
|
|
10
|
+
attachTransfer?: boolean;
|
|
11
|
+
}
|
|
12
|
+
declare type Dispatch<T> = Omit<T, 'transferID' | 'uuid'>;
|
|
13
|
+
declare const createDispatcher: (ws: WebSocket) => {
|
|
14
|
+
readonly transferID: string | undefined;
|
|
15
|
+
readonly transferKind: "push" | "pull" | undefined;
|
|
16
|
+
setTransferProperties: (properties: Exclude<IDispatcherState['transfer'], undefined>) => void;
|
|
17
|
+
dispatch: <U = null>(message: Dispatch<client.Message>, options?: IDispatchOptions) => Promise<U | null>;
|
|
18
|
+
dispatchCommand: <U_1 extends "end" | "init" | "status">(payload: {
|
|
19
|
+
command: U_1;
|
|
20
|
+
} & ([client.GetCommandParams<U_1>] extends [never] ? unknown : {
|
|
21
|
+
params: client.GetCommandParams<U_1>;
|
|
22
|
+
})) => Promise<null>;
|
|
23
|
+
dispatchTransferAction: <T>(action: client.Action['action']) => Promise<T | null>;
|
|
24
|
+
dispatchTransferStep: <T_1, A extends "stream" | "end" | "start" = "stream" | "end" | "start", S extends "entities" | "links" | "configuration" | "assets" = "entities" | "links" | "configuration" | "assets">(payload: {
|
|
25
|
+
step: S;
|
|
26
|
+
action: A;
|
|
27
|
+
} & (A extends "stream" ? {
|
|
28
|
+
data: client.GetTransferPushStreamData<S>;
|
|
29
|
+
} : unknown)) => Promise<T_1 | null>;
|
|
30
|
+
};
|
|
31
|
+
export { createDispatcher };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createDispatcher = void 0;
|
|
4
|
+
const uuid_1 = require("uuid");
|
|
5
|
+
const createDispatcher = (ws) => {
|
|
6
|
+
const state = {};
|
|
7
|
+
const dispatch = async (message, options = {}) => {
|
|
8
|
+
if (!ws) {
|
|
9
|
+
throw new Error('No websocket connection found');
|
|
10
|
+
}
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
const uuid = (0, uuid_1.v4)();
|
|
13
|
+
const payload = { ...message, uuid };
|
|
14
|
+
if (options.attachTransfer) {
|
|
15
|
+
Object.assign(payload, { transferID: state.transfer?.id });
|
|
16
|
+
}
|
|
17
|
+
const stringifiedPayload = JSON.stringify(payload);
|
|
18
|
+
ws.send(stringifiedPayload, (error) => {
|
|
19
|
+
if (error) {
|
|
20
|
+
reject(error);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
const onResponse = (raw) => {
|
|
24
|
+
const response = JSON.parse(raw.toString());
|
|
25
|
+
if (response.uuid === uuid) {
|
|
26
|
+
if (response.error) {
|
|
27
|
+
return reject(new Error(response.error.message));
|
|
28
|
+
}
|
|
29
|
+
resolve(response.data ?? null);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
ws.once('message', onResponse);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
// TODO: What happens if the server sends another message (not a response to this message)
|
|
36
|
+
ws.once('message', onResponse);
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
const dispatchCommand = (payload) => {
|
|
40
|
+
return dispatch({ type: 'command', ...payload });
|
|
41
|
+
};
|
|
42
|
+
const dispatchTransferAction = async (action) => {
|
|
43
|
+
const payload = { type: 'transfer', kind: 'action', action };
|
|
44
|
+
return dispatch(payload, { attachTransfer: true }) ?? Promise.resolve(null);
|
|
45
|
+
};
|
|
46
|
+
const dispatchTransferStep = async (payload) => {
|
|
47
|
+
const message = {
|
|
48
|
+
type: 'transfer',
|
|
49
|
+
kind: 'step',
|
|
50
|
+
...payload,
|
|
51
|
+
};
|
|
52
|
+
return dispatch(message, { attachTransfer: true }) ?? Promise.resolve(null);
|
|
53
|
+
};
|
|
54
|
+
const setTransferProperties = (properties) => {
|
|
55
|
+
state.transfer = { ...properties };
|
|
56
|
+
};
|
|
57
|
+
return {
|
|
58
|
+
get transferID() {
|
|
59
|
+
return state.transfer?.id;
|
|
60
|
+
},
|
|
61
|
+
get transferKind() {
|
|
62
|
+
return state.transfer?.kind;
|
|
63
|
+
},
|
|
64
|
+
setTransferProperties,
|
|
65
|
+
dispatch,
|
|
66
|
+
dispatchCommand,
|
|
67
|
+
dispatchTransferAction,
|
|
68
|
+
dispatchTransferStep,
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
exports.createDispatcher = createDispatcher;
|
|
72
|
+
//# sourceMappingURL=utils.js.map
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ILink } from '
|
|
1
|
+
import { ILink } from '../../../types';
|
|
2
2
|
export declare const createLinkQuery: (strapi: Strapi.Strapi) => () => {
|
|
3
3
|
generateAll: (uid: string) => AsyncGenerator<ILink>;
|
|
4
4
|
generateAllForAttribute: (uid: string, fieldName: string) => AsyncGenerator<ILink>;
|
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const TRANSFER_PATH = "/transfer";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './push';
|
|
@@ -14,6 +14,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./
|
|
18
|
-
__exportStar(require("./providers"), exports);
|
|
17
|
+
__exportStar(require("./push"), exports);
|
|
19
18
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
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;
|
|
@@ -0,0 +1,95 @@
|
|
|
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
|
|
@@ -0,0 +1,177 @@
|
|
|
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 createTransferHandler = (options = {}) => async (ctx) => {
|
|
11
|
+
const upgradeHeader = (ctx.request.headers.upgrade || '')
|
|
12
|
+
.split(',')
|
|
13
|
+
.map((s) => s.trim().toLowerCase());
|
|
14
|
+
// Create the websocket server
|
|
15
|
+
const wss = new ws_1.WebSocket.Server({ ...options, noServer: true });
|
|
16
|
+
if (upgradeHeader.includes('websocket')) {
|
|
17
|
+
wss.handleUpgrade(ctx.req, ctx.request.socket, Buffer.alloc(0), (ws) => {
|
|
18
|
+
// Create a connection between the client & the server
|
|
19
|
+
wss.emit('connection', ws, ctx.req);
|
|
20
|
+
const state = {};
|
|
21
|
+
let uuid;
|
|
22
|
+
/**
|
|
23
|
+
* Format error & message to follow the remote transfer protocol
|
|
24
|
+
*/
|
|
25
|
+
const callback = (e = null, data) => {
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
if (!uuid) {
|
|
28
|
+
reject(new Error('Missing uuid for this message'));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const payload = JSON.stringify({
|
|
32
|
+
uuid,
|
|
33
|
+
data: data ?? null,
|
|
34
|
+
error: e
|
|
35
|
+
? {
|
|
36
|
+
code: 'ERR',
|
|
37
|
+
message: e?.message,
|
|
38
|
+
}
|
|
39
|
+
: null,
|
|
40
|
+
});
|
|
41
|
+
ws.send(payload, (error) => (error ? reject(error) : resolve()));
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Wrap a function call to catch errors and answer the request with the correct format
|
|
46
|
+
*/
|
|
47
|
+
const answer = async (fn) => {
|
|
48
|
+
try {
|
|
49
|
+
const response = await fn();
|
|
50
|
+
callback(null, response);
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
if (e instanceof Error) {
|
|
54
|
+
callback(e);
|
|
55
|
+
}
|
|
56
|
+
else if (typeof e === 'string') {
|
|
57
|
+
callback(new Error(e));
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
callback(new Error('Unexpected error'));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const teardown = () => {
|
|
65
|
+
delete state.controller;
|
|
66
|
+
delete state.transfer;
|
|
67
|
+
return { ok: true };
|
|
68
|
+
};
|
|
69
|
+
const init = (msg) => {
|
|
70
|
+
if (state.controller) {
|
|
71
|
+
throw new Error('Transfer already in progres');
|
|
72
|
+
}
|
|
73
|
+
const { transfer } = msg.params;
|
|
74
|
+
// Push transfer
|
|
75
|
+
if (transfer === 'push') {
|
|
76
|
+
const { options: controllerOptions } = msg.params;
|
|
77
|
+
state.controller = (0, push_1.default)({
|
|
78
|
+
...controllerOptions,
|
|
79
|
+
autoDestroy: false,
|
|
80
|
+
getStrapi: () => strapi,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
// Pull or any other string
|
|
84
|
+
else {
|
|
85
|
+
throw new Error(`Transfer not implemented: "${transfer}"`);
|
|
86
|
+
}
|
|
87
|
+
state.transfer = { id: (0, crypto_1.randomUUID)(), kind: transfer };
|
|
88
|
+
return { transferID: state.transfer.id };
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* On command message (init, end, status, ...)
|
|
92
|
+
*/
|
|
93
|
+
const onCommand = async (msg) => {
|
|
94
|
+
const { command } = msg;
|
|
95
|
+
if (command === 'init') {
|
|
96
|
+
await answer(() => init(msg));
|
|
97
|
+
}
|
|
98
|
+
if (command === 'end') {
|
|
99
|
+
await answer(teardown);
|
|
100
|
+
}
|
|
101
|
+
if (command === 'status') {
|
|
102
|
+
await callback(new Error('Command not implemented: "status"'));
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
const onTransferCommand = async (msg) => {
|
|
106
|
+
const { transferID, kind } = msg;
|
|
107
|
+
const { controller } = state;
|
|
108
|
+
// TODO: (re)move this check
|
|
109
|
+
// It shouldn't be possible to strart a pull transfer for now, so reaching
|
|
110
|
+
// this code should be impossible too, but this has been added by security
|
|
111
|
+
if (state.transfer?.kind === 'pull') {
|
|
112
|
+
return callback(new Error('Pull transfer not implemented'));
|
|
113
|
+
}
|
|
114
|
+
if (!controller) {
|
|
115
|
+
return callback(new Error("The transfer hasn't been initialized"));
|
|
116
|
+
}
|
|
117
|
+
if (!transferID) {
|
|
118
|
+
return callback(new Error('Missing transfer ID'));
|
|
119
|
+
}
|
|
120
|
+
// Action
|
|
121
|
+
if (kind === 'action') {
|
|
122
|
+
const { action } = msg;
|
|
123
|
+
if (!(action in controller.actions)) {
|
|
124
|
+
return callback(new Error(`Invalid action provided: "${action}"`));
|
|
125
|
+
}
|
|
126
|
+
await answer(() => controller.actions[action]());
|
|
127
|
+
}
|
|
128
|
+
// Transfer
|
|
129
|
+
else if (kind === 'step') {
|
|
130
|
+
// We can only have push transfer message for the moment
|
|
131
|
+
const message = msg;
|
|
132
|
+
// TODO: lock transfer process
|
|
133
|
+
if (message.action === 'start') {
|
|
134
|
+
// console.log('Starting transfer for ', message.step);
|
|
135
|
+
}
|
|
136
|
+
// Stream step
|
|
137
|
+
else if (message.action === 'stream') {
|
|
138
|
+
await answer(() => controller.transfer[message.step]?.(message.data));
|
|
139
|
+
}
|
|
140
|
+
// TODO: unlock transfer process
|
|
141
|
+
else if (message.action === 'end') {
|
|
142
|
+
// console.log('Ending transfer for ', message.step);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
ws.on('close', () => {
|
|
147
|
+
teardown();
|
|
148
|
+
});
|
|
149
|
+
ws.on('error', (e) => {
|
|
150
|
+
teardown();
|
|
151
|
+
console.error(e);
|
|
152
|
+
});
|
|
153
|
+
ws.on('message', async (raw) => {
|
|
154
|
+
const msg = JSON.parse(raw.toString());
|
|
155
|
+
if (!msg.uuid) {
|
|
156
|
+
throw new Error('Missing uuid in message');
|
|
157
|
+
}
|
|
158
|
+
uuid = msg.uuid;
|
|
159
|
+
// Regular command message (init, end, status)
|
|
160
|
+
if (msg.type === 'command') {
|
|
161
|
+
await onCommand(msg);
|
|
162
|
+
}
|
|
163
|
+
// Transfer message (the transfer must be initialized first)
|
|
164
|
+
else if (msg.type === 'transfer') {
|
|
165
|
+
await onTransferCommand(msg);
|
|
166
|
+
}
|
|
167
|
+
// Invalid messages
|
|
168
|
+
else {
|
|
169
|
+
await callback(new Error('Bad request'));
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
ctx.respond = false;
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
exports.createTransferHandler = createTransferHandler;
|
|
177
|
+
//# sourceMappingURL=handlers.js.map
|