@strapi/data-transfer 4.7.0 → 4.7.1
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/encryption/decrypt.d.ts +3 -0
- package/dist/encryption/decrypt.js +39 -0
- package/dist/encryption/encrypt.d.ts +3 -0
- package/dist/encryption/encrypt.js +39 -0
- package/dist/encryption/index.d.ts +2 -0
- package/dist/encryption/index.js +19 -0
- package/dist/engine/index.d.ts +29 -0
- package/dist/engine/index.js +324 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +19 -0
- package/dist/providers/index.d.ts +4 -0
- package/dist/providers/index.js +23 -0
- package/dist/providers/local-file-destination-provider.d.ts +41 -0
- package/dist/providers/local-file-destination-provider.js +195 -0
- package/dist/providers/local-file-source-provider.d.ts +43 -0
- package/dist/providers/local-file-source-provider.js +162 -0
- package/dist/providers/local-strapi-destination-provider.d.ts +22 -0
- package/dist/providers/local-strapi-destination-provider.js +78 -0
- package/dist/providers/local-strapi-source-provider/configuration.d.ts +5 -0
- package/dist/providers/local-strapi-source-provider/configuration.js +37 -0
- package/dist/providers/local-strapi-source-provider/entities.d.ts +10 -0
- package/dist/providers/local-strapi-source-provider/entities.js +58 -0
- package/dist/providers/local-strapi-source-provider/index.d.ts +26 -0
- package/dist/providers/local-strapi-source-provider/index.js +83 -0
- package/dist/providers/local-strapi-source-provider/links/index.d.ts +5 -0
- package/dist/providers/local-strapi-source-provider/links/index.js +37 -0
- package/dist/providers/local-strapi-source-provider/links/utils.d.ts +27 -0
- package/dist/providers/local-strapi-source-provider/links/utils.js +155 -0
- package/dist/strategies/index.d.ts +7 -0
- package/dist/strategies/index.js +29 -0
- package/dist/utils.d.ts +10 -0
- package/dist/utils.js +90 -0
- package/package.json +4 -4
|
@@ -0,0 +1,195 @@
|
|
|
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 __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
9
|
+
};
|
|
10
|
+
var _LocalFileDestinationProvider_instances, _LocalFileDestinationProvider_providersMetadata, _LocalFileDestinationProvider_archive, _LocalFileDestinationProvider_archivePath_get, _LocalFileDestinationProvider_getDataTransformers, _LocalFileDestinationProvider_writeMetadata, _LocalFileDestinationProvider_getMetadataStream;
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.createLocalFileDestinationProvider = void 0;
|
|
13
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
14
|
+
const path_1 = __importDefault(require("path"));
|
|
15
|
+
const tar_stream_1 = __importDefault(require("tar-stream"));
|
|
16
|
+
const zlib_1 = __importDefault(require("zlib"));
|
|
17
|
+
const stream_1 = require("stream");
|
|
18
|
+
const Stringer_1 = require("stream-json/jsonl/Stringer");
|
|
19
|
+
const stream_chain_1 = require("stream-chain");
|
|
20
|
+
const encrypt_1 = require("../encryption/encrypt");
|
|
21
|
+
const createLocalFileDestinationProvider = (options) => {
|
|
22
|
+
return new LocalFileDestinationProvider(options);
|
|
23
|
+
};
|
|
24
|
+
exports.createLocalFileDestinationProvider = createLocalFileDestinationProvider;
|
|
25
|
+
class LocalFileDestinationProvider {
|
|
26
|
+
constructor(options) {
|
|
27
|
+
_LocalFileDestinationProvider_instances.add(this);
|
|
28
|
+
this.name = 'destination::local-file';
|
|
29
|
+
this.type = 'destination';
|
|
30
|
+
this.results = {};
|
|
31
|
+
_LocalFileDestinationProvider_providersMetadata.set(this, {});
|
|
32
|
+
_LocalFileDestinationProvider_archive.set(this, {});
|
|
33
|
+
this.options = options;
|
|
34
|
+
}
|
|
35
|
+
setMetadata(target, metadata) {
|
|
36
|
+
__classPrivateFieldGet(this, _LocalFileDestinationProvider_providersMetadata, "f")[target] = metadata;
|
|
37
|
+
return this;
|
|
38
|
+
}
|
|
39
|
+
bootstrap() {
|
|
40
|
+
const { compression, encryption } = this.options;
|
|
41
|
+
if (encryption.enabled && !encryption.key) {
|
|
42
|
+
throw new Error("Can't encrypt without a key");
|
|
43
|
+
}
|
|
44
|
+
__classPrivateFieldGet(this, _LocalFileDestinationProvider_archive, "f").stream = tar_stream_1.default.pack();
|
|
45
|
+
const outStream = fs_extra_1.default.createWriteStream(__classPrivateFieldGet(this, _LocalFileDestinationProvider_instances, "a", _LocalFileDestinationProvider_archivePath_get));
|
|
46
|
+
const archiveTransforms = [];
|
|
47
|
+
if (compression.enabled) {
|
|
48
|
+
archiveTransforms.push(zlib_1.default.createGzip());
|
|
49
|
+
}
|
|
50
|
+
if (encryption.enabled && encryption.key) {
|
|
51
|
+
archiveTransforms.push((0, encrypt_1.createEncryptionCipher)(encryption.key));
|
|
52
|
+
}
|
|
53
|
+
__classPrivateFieldGet(this, _LocalFileDestinationProvider_archive, "f").pipeline = (0, stream_chain_1.chain)([__classPrivateFieldGet(this, _LocalFileDestinationProvider_archive, "f").stream, ...archiveTransforms, outStream]);
|
|
54
|
+
this.results.file = { path: __classPrivateFieldGet(this, _LocalFileDestinationProvider_instances, "a", _LocalFileDestinationProvider_archivePath_get) };
|
|
55
|
+
}
|
|
56
|
+
async close() {
|
|
57
|
+
const { stream, pipeline } = __classPrivateFieldGet(this, _LocalFileDestinationProvider_archive, "f");
|
|
58
|
+
if (!stream) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
await __classPrivateFieldGet(this, _LocalFileDestinationProvider_instances, "m", _LocalFileDestinationProvider_writeMetadata).call(this);
|
|
62
|
+
stream.finalize();
|
|
63
|
+
if (pipeline && !pipeline.closed) {
|
|
64
|
+
await new Promise((resolve, reject) => {
|
|
65
|
+
pipeline.on('close', resolve).on('error', reject);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async rollback() {
|
|
70
|
+
await this.close();
|
|
71
|
+
fs_extra_1.default.rmSync(__classPrivateFieldGet(this, _LocalFileDestinationProvider_instances, "a", _LocalFileDestinationProvider_archivePath_get), { force: true });
|
|
72
|
+
}
|
|
73
|
+
getMetadata() {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
getSchemasStream() {
|
|
77
|
+
if (!__classPrivateFieldGet(this, _LocalFileDestinationProvider_archive, "f").stream) {
|
|
78
|
+
throw new Error('Archive stream is unavailable');
|
|
79
|
+
}
|
|
80
|
+
const filePathFactory = createFilePathFactory('schemas');
|
|
81
|
+
const entryStream = createTarEntryStream(__classPrivateFieldGet(this, _LocalFileDestinationProvider_archive, "f").stream, filePathFactory, this.options.file.maxSize);
|
|
82
|
+
return (0, stream_chain_1.chain)([(0, Stringer_1.stringer)(), entryStream]);
|
|
83
|
+
}
|
|
84
|
+
getEntitiesStream() {
|
|
85
|
+
if (!__classPrivateFieldGet(this, _LocalFileDestinationProvider_archive, "f").stream) {
|
|
86
|
+
throw new Error('Archive stream is unavailable');
|
|
87
|
+
}
|
|
88
|
+
const filePathFactory = createFilePathFactory('entities');
|
|
89
|
+
const entryStream = createTarEntryStream(__classPrivateFieldGet(this, _LocalFileDestinationProvider_archive, "f").stream, filePathFactory, this.options.file.maxSize);
|
|
90
|
+
return (0, stream_chain_1.chain)([(0, Stringer_1.stringer)(), entryStream]);
|
|
91
|
+
}
|
|
92
|
+
getLinksStream() {
|
|
93
|
+
if (!__classPrivateFieldGet(this, _LocalFileDestinationProvider_archive, "f").stream) {
|
|
94
|
+
throw new Error('Archive stream is unavailable');
|
|
95
|
+
}
|
|
96
|
+
const filePathFactory = createFilePathFactory('links');
|
|
97
|
+
const entryStream = createTarEntryStream(__classPrivateFieldGet(this, _LocalFileDestinationProvider_archive, "f").stream, filePathFactory, this.options.file.maxSize);
|
|
98
|
+
return (0, stream_chain_1.chain)([(0, Stringer_1.stringer)(), entryStream]);
|
|
99
|
+
}
|
|
100
|
+
getConfigurationStream() {
|
|
101
|
+
if (!__classPrivateFieldGet(this, _LocalFileDestinationProvider_archive, "f").stream) {
|
|
102
|
+
throw new Error('Archive stream is unavailable');
|
|
103
|
+
}
|
|
104
|
+
const filePathFactory = createFilePathFactory('configuration');
|
|
105
|
+
const entryStream = createTarEntryStream(__classPrivateFieldGet(this, _LocalFileDestinationProvider_archive, "f").stream, filePathFactory, this.options.file.maxSize);
|
|
106
|
+
return (0, stream_chain_1.chain)([(0, Stringer_1.stringer)(), entryStream]);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
_LocalFileDestinationProvider_providersMetadata = new WeakMap(), _LocalFileDestinationProvider_archive = new WeakMap(), _LocalFileDestinationProvider_instances = new WeakSet(), _LocalFileDestinationProvider_archivePath_get = function _LocalFileDestinationProvider_archivePath_get() {
|
|
110
|
+
const { encryption, compression, file } = this.options;
|
|
111
|
+
let path = `${file.path}.tar`;
|
|
112
|
+
if (compression.enabled) {
|
|
113
|
+
path += '.gz';
|
|
114
|
+
}
|
|
115
|
+
if (encryption.enabled) {
|
|
116
|
+
path += '.enc';
|
|
117
|
+
}
|
|
118
|
+
return path;
|
|
119
|
+
}, _LocalFileDestinationProvider_getDataTransformers = function _LocalFileDestinationProvider_getDataTransformers(options = {}) {
|
|
120
|
+
const { jsonl = true } = options;
|
|
121
|
+
const transforms = [];
|
|
122
|
+
if (jsonl) {
|
|
123
|
+
// Convert to stringified JSON lines
|
|
124
|
+
transforms.push((0, Stringer_1.stringer)());
|
|
125
|
+
}
|
|
126
|
+
return transforms;
|
|
127
|
+
}, _LocalFileDestinationProvider_writeMetadata = async function _LocalFileDestinationProvider_writeMetadata() {
|
|
128
|
+
const metadata = __classPrivateFieldGet(this, _LocalFileDestinationProvider_providersMetadata, "f").source;
|
|
129
|
+
if (metadata) {
|
|
130
|
+
await new Promise((resolve) => {
|
|
131
|
+
const outStream = __classPrivateFieldGet(this, _LocalFileDestinationProvider_instances, "m", _LocalFileDestinationProvider_getMetadataStream).call(this);
|
|
132
|
+
const data = JSON.stringify(metadata, null, 2);
|
|
133
|
+
stream_1.Readable.from(data).pipe(outStream).on('close', resolve);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}, _LocalFileDestinationProvider_getMetadataStream = function _LocalFileDestinationProvider_getMetadataStream() {
|
|
137
|
+
const { stream } = __classPrivateFieldGet(this, _LocalFileDestinationProvider_archive, "f");
|
|
138
|
+
if (!stream) {
|
|
139
|
+
throw new Error('Archive stream is unavailable');
|
|
140
|
+
}
|
|
141
|
+
return createTarEntryStream(stream, () => 'metadata.json');
|
|
142
|
+
};
|
|
143
|
+
/**
|
|
144
|
+
* Create a file path factory for a given path & prefix.
|
|
145
|
+
* Upon being called, the factory will return a file path for a given index
|
|
146
|
+
*/
|
|
147
|
+
const createFilePathFactory = (type) => (fileIndex = 0) => {
|
|
148
|
+
return path_1.default.join(
|
|
149
|
+
// "{type}" directory
|
|
150
|
+
type,
|
|
151
|
+
// "${type}_XXXXX.jsonl" file
|
|
152
|
+
`${type}_${String(fileIndex).padStart(5, '0')}.jsonl`);
|
|
153
|
+
};
|
|
154
|
+
const createTarEntryStream = (archive, pathFactory, maxSize = 2.56e8) => {
|
|
155
|
+
let fileIndex = 0;
|
|
156
|
+
let buffer = '';
|
|
157
|
+
const flush = async () => {
|
|
158
|
+
if (!buffer) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const name = pathFactory(fileIndex++);
|
|
162
|
+
const size = buffer.length;
|
|
163
|
+
await new Promise((resolve, reject) => {
|
|
164
|
+
archive.entry({ name, size }, buffer, (err) => {
|
|
165
|
+
if (err) {
|
|
166
|
+
reject(err);
|
|
167
|
+
}
|
|
168
|
+
resolve();
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
buffer = '';
|
|
172
|
+
};
|
|
173
|
+
const push = (chunk) => {
|
|
174
|
+
buffer += chunk;
|
|
175
|
+
};
|
|
176
|
+
return new stream_1.Writable({
|
|
177
|
+
async destroy(err, callback) {
|
|
178
|
+
await flush();
|
|
179
|
+
callback(err);
|
|
180
|
+
},
|
|
181
|
+
async write(chunk, _encoding, callback) {
|
|
182
|
+
const size = chunk.length;
|
|
183
|
+
if (chunk.length > maxSize) {
|
|
184
|
+
callback(new Error(`payload too large: ${chunk.length}>${maxSize}`));
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (buffer.length + size > maxSize) {
|
|
188
|
+
await flush();
|
|
189
|
+
}
|
|
190
|
+
push(chunk);
|
|
191
|
+
callback(null);
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
};
|
|
195
|
+
//# sourceMappingURL=local-file-destination-provider.js.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/// <reference types="lodash" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
import type { IMetadata, ISourceProvider, ProviderType } from '../../types';
|
|
4
|
+
/**
|
|
5
|
+
* Provider options
|
|
6
|
+
*/
|
|
7
|
+
export interface ILocalFileSourceProviderOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Path to the backup archive
|
|
10
|
+
*/
|
|
11
|
+
backupFilePath: string;
|
|
12
|
+
/**
|
|
13
|
+
* Whether the backup data is encrypted or not
|
|
14
|
+
*/
|
|
15
|
+
encrypted?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Encryption key used to decrypt the encrypted data (if necessary)
|
|
18
|
+
*/
|
|
19
|
+
encryptionKey?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Whether the backup data is compressed or not
|
|
22
|
+
*/
|
|
23
|
+
compressed?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export declare const createLocalFileSourceProvider: (options: ILocalFileSourceProviderOptions) => LocalFileSourceProvider;
|
|
26
|
+
declare class LocalFileSourceProvider implements ISourceProvider {
|
|
27
|
+
#private;
|
|
28
|
+
type: ProviderType;
|
|
29
|
+
name: string;
|
|
30
|
+
options: ILocalFileSourceProviderOptions;
|
|
31
|
+
constructor(options: ILocalFileSourceProviderOptions);
|
|
32
|
+
/**
|
|
33
|
+
* Pre flight checks regarding the provided options (making sure that the provided path is correct, etc...)
|
|
34
|
+
*/
|
|
35
|
+
bootstrap(): void;
|
|
36
|
+
getMetadata(): Promise<IMetadata>;
|
|
37
|
+
getSchemas(): Promise<import("lodash").Dictionary<unknown>>;
|
|
38
|
+
streamEntities(): NodeJS.ReadableStream;
|
|
39
|
+
streamSchemas(): NodeJS.ReadableStream | Promise<NodeJS.ReadableStream>;
|
|
40
|
+
streamLinks(): NodeJS.ReadableStream;
|
|
41
|
+
streamConfiguration(): NodeJS.ReadableStream;
|
|
42
|
+
}
|
|
43
|
+
export {};
|
|
@@ -0,0 +1,162 @@
|
|
|
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 __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
9
|
+
};
|
|
10
|
+
var _LocalFileSourceProvider_instances, _LocalFileSourceProvider_getBackupStream, _LocalFileSourceProvider_streamJsonlDirectory, _LocalFileSourceProvider_parseJSONFile;
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.createLocalFileSourceProvider = void 0;
|
|
13
|
+
const fs_1 = __importDefault(require("fs"));
|
|
14
|
+
const zlib_1 = __importDefault(require("zlib"));
|
|
15
|
+
const tar_1 = __importDefault(require("tar"));
|
|
16
|
+
const fp_1 = require("lodash/fp");
|
|
17
|
+
const stream_chain_1 = require("stream-chain");
|
|
18
|
+
const stream_1 = require("stream");
|
|
19
|
+
const Parser_1 = require("stream-json/jsonl/Parser");
|
|
20
|
+
const encryption_1 = require("../encryption");
|
|
21
|
+
const utils_1 = require("../utils");
|
|
22
|
+
/**
|
|
23
|
+
* Constant for the metadata file path
|
|
24
|
+
*/
|
|
25
|
+
const METADATA_FILE_PATH = 'metadata.json';
|
|
26
|
+
const createLocalFileSourceProvider = (options) => {
|
|
27
|
+
return new LocalFileSourceProvider(options);
|
|
28
|
+
};
|
|
29
|
+
exports.createLocalFileSourceProvider = createLocalFileSourceProvider;
|
|
30
|
+
class LocalFileSourceProvider {
|
|
31
|
+
constructor(options) {
|
|
32
|
+
_LocalFileSourceProvider_instances.add(this);
|
|
33
|
+
this.type = 'source';
|
|
34
|
+
this.name = 'source::local-file';
|
|
35
|
+
this.options = options;
|
|
36
|
+
if (this.options.encrypted && this.options.encryptionKey === undefined) {
|
|
37
|
+
throw new Error('Missing encryption key');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Pre flight checks regarding the provided options (making sure that the provided path is correct, etc...)
|
|
42
|
+
*/
|
|
43
|
+
bootstrap() {
|
|
44
|
+
const path = this.options.backupFilePath;
|
|
45
|
+
const isValidBackupPath = fs_1.default.existsSync(path);
|
|
46
|
+
// Check if the provided path exists
|
|
47
|
+
if (!isValidBackupPath) {
|
|
48
|
+
throw new Error(`Invalid backup file path provided. "${path}" does not exist on the filesystem.`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
getMetadata() {
|
|
52
|
+
// TODO: need to read the file & extract the metadata json file
|
|
53
|
+
// => we might also need to read the schema.jsonl files & implements a custom stream-check
|
|
54
|
+
const backupStream = __classPrivateFieldGet(this, _LocalFileSourceProvider_instances, "m", _LocalFileSourceProvider_getBackupStream).call(this);
|
|
55
|
+
return __classPrivateFieldGet(this, _LocalFileSourceProvider_instances, "m", _LocalFileSourceProvider_parseJSONFile).call(this, backupStream, METADATA_FILE_PATH);
|
|
56
|
+
}
|
|
57
|
+
async getSchemas() {
|
|
58
|
+
const schemas = await (0, utils_1.collect)(this.streamSchemas());
|
|
59
|
+
return (0, fp_1.keyBy)('uid', schemas);
|
|
60
|
+
}
|
|
61
|
+
streamEntities() {
|
|
62
|
+
return __classPrivateFieldGet(this, _LocalFileSourceProvider_instances, "m", _LocalFileSourceProvider_streamJsonlDirectory).call(this, 'entities');
|
|
63
|
+
}
|
|
64
|
+
streamSchemas() {
|
|
65
|
+
return __classPrivateFieldGet(this, _LocalFileSourceProvider_instances, "m", _LocalFileSourceProvider_streamJsonlDirectory).call(this, 'schemas');
|
|
66
|
+
}
|
|
67
|
+
streamLinks() {
|
|
68
|
+
return __classPrivateFieldGet(this, _LocalFileSourceProvider_instances, "m", _LocalFileSourceProvider_streamJsonlDirectory).call(this, 'links');
|
|
69
|
+
}
|
|
70
|
+
streamConfiguration() {
|
|
71
|
+
// NOTE: TBD
|
|
72
|
+
return __classPrivateFieldGet(this, _LocalFileSourceProvider_instances, "m", _LocalFileSourceProvider_streamJsonlDirectory).call(this, 'configuration');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
_LocalFileSourceProvider_instances = new WeakSet(), _LocalFileSourceProvider_getBackupStream = function _LocalFileSourceProvider_getBackupStream(decompress = true) {
|
|
76
|
+
const path = this.options.backupFilePath;
|
|
77
|
+
const readStream = fs_1.default.createReadStream(path);
|
|
78
|
+
const streams = [readStream];
|
|
79
|
+
// Handle decompression
|
|
80
|
+
if (decompress) {
|
|
81
|
+
streams.push(zlib_1.default.createGunzip());
|
|
82
|
+
}
|
|
83
|
+
return (0, stream_chain_1.chain)(streams);
|
|
84
|
+
}, _LocalFileSourceProvider_streamJsonlDirectory = function _LocalFileSourceProvider_streamJsonlDirectory(directory) {
|
|
85
|
+
const options = this.options;
|
|
86
|
+
const inStream = __classPrivateFieldGet(this, _LocalFileSourceProvider_instances, "m", _LocalFileSourceProvider_getBackupStream).call(this);
|
|
87
|
+
const outStream = new stream_1.PassThrough({ objectMode: true });
|
|
88
|
+
(0, stream_1.pipeline)([
|
|
89
|
+
inStream,
|
|
90
|
+
new tar_1.default.Parse({
|
|
91
|
+
filter(path, entry) {
|
|
92
|
+
if (entry.type !== 'File') {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
const parts = path.split('/');
|
|
96
|
+
if (parts.length !== 2) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
return parts[0] === directory;
|
|
100
|
+
},
|
|
101
|
+
onentry(entry) {
|
|
102
|
+
const transforms = [];
|
|
103
|
+
if (options.encrypted) {
|
|
104
|
+
transforms.push((0, encryption_1.createDecryptionCipher)(options.encryptionKey));
|
|
105
|
+
}
|
|
106
|
+
if (options.compressed) {
|
|
107
|
+
transforms.push(zlib_1.default.createGunzip());
|
|
108
|
+
}
|
|
109
|
+
transforms.push(
|
|
110
|
+
// JSONL parser to read the data chunks one by one (line by line)
|
|
111
|
+
(0, Parser_1.parser)(),
|
|
112
|
+
// The JSONL parser returns each line as key/value
|
|
113
|
+
(line) => line.value);
|
|
114
|
+
entry
|
|
115
|
+
// Pipe transforms
|
|
116
|
+
.pipe((0, stream_chain_1.chain)(transforms))
|
|
117
|
+
// Pipe the out stream to the whole pipeline
|
|
118
|
+
// DO NOT send the 'end' event when this entry has finished
|
|
119
|
+
// emitting data, so that it doesn't close the out stream
|
|
120
|
+
.pipe(outStream, { end: false });
|
|
121
|
+
},
|
|
122
|
+
}),
|
|
123
|
+
], () => {
|
|
124
|
+
// Manually send the 'end' event to the out stream
|
|
125
|
+
// once every entry has finished streaming its content
|
|
126
|
+
outStream.end();
|
|
127
|
+
});
|
|
128
|
+
return outStream;
|
|
129
|
+
}, _LocalFileSourceProvider_parseJSONFile = async function _LocalFileSourceProvider_parseJSONFile(fileStream, filePath) {
|
|
130
|
+
return new Promise((resolve, reject) => {
|
|
131
|
+
(0, stream_1.pipeline)([
|
|
132
|
+
fileStream,
|
|
133
|
+
// Custom backup archive parsing
|
|
134
|
+
new tar_1.default.Parse({
|
|
135
|
+
/**
|
|
136
|
+
* Filter the parsed entries to only keep the one that matches the given filepath
|
|
137
|
+
*/
|
|
138
|
+
filter(path, entry) {
|
|
139
|
+
return path === filePath && entry.type === 'File';
|
|
140
|
+
},
|
|
141
|
+
/**
|
|
142
|
+
* Whenever an entry passes the filter method, process it
|
|
143
|
+
*/
|
|
144
|
+
async onentry(entry) {
|
|
145
|
+
// Collect all the content of the entry file
|
|
146
|
+
const content = await entry.collect();
|
|
147
|
+
// Parse from buffer to string to JSON
|
|
148
|
+
const parsedContent = JSON.parse(content.toString());
|
|
149
|
+
// Resolve the Promise with the parsed content
|
|
150
|
+
resolve(parsedContent);
|
|
151
|
+
// Cleanup (close the stream associated to the entry)
|
|
152
|
+
entry.destroy();
|
|
153
|
+
},
|
|
154
|
+
}),
|
|
155
|
+
], () => {
|
|
156
|
+
// If the promise hasn't been resolved and we've parsed all
|
|
157
|
+
// the archive entries, then the file doesn't exist
|
|
158
|
+
reject(`${filePath} not found in the archive stream`);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
};
|
|
162
|
+
//# sourceMappingURL=local-file-source-provider.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import type { IDestinationProvider, IMetadata, ProviderType } from '../../types';
|
|
3
|
+
import { Duplex } from 'stream';
|
|
4
|
+
interface ILocalStrapiDestinationProviderOptions {
|
|
5
|
+
getStrapi(): Promise<Strapi.Strapi>;
|
|
6
|
+
}
|
|
7
|
+
export declare const createLocalStrapiDestinationProvider: (options: ILocalStrapiDestinationProviderOptions) => LocalStrapiDestinationProvider;
|
|
8
|
+
declare class LocalStrapiDestinationProvider implements IDestinationProvider {
|
|
9
|
+
name: string;
|
|
10
|
+
type: ProviderType;
|
|
11
|
+
options: ILocalStrapiDestinationProviderOptions;
|
|
12
|
+
strapi?: Strapi.Strapi;
|
|
13
|
+
constructor(options: ILocalStrapiDestinationProviderOptions);
|
|
14
|
+
bootstrap(): Promise<void>;
|
|
15
|
+
close(): Promise<void>;
|
|
16
|
+
getMetadata(): IMetadata | Promise<IMetadata>;
|
|
17
|
+
getSchemas(): {
|
|
18
|
+
[x: string]: Partial<any>;
|
|
19
|
+
};
|
|
20
|
+
getEntitiesStream(): Duplex;
|
|
21
|
+
}
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,78 @@
|
|
|
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.createLocalStrapiDestinationProvider = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const stream_1 = require("stream");
|
|
9
|
+
const utils_1 = require("../utils");
|
|
10
|
+
// TODO: getting some type errors with @strapi/logger that need to be resolved first
|
|
11
|
+
// const log = createLogger();
|
|
12
|
+
const log = console;
|
|
13
|
+
const createLocalStrapiDestinationProvider = (options) => {
|
|
14
|
+
return new LocalStrapiDestinationProvider(options);
|
|
15
|
+
};
|
|
16
|
+
exports.createLocalStrapiDestinationProvider = createLocalStrapiDestinationProvider;
|
|
17
|
+
class LocalStrapiDestinationProvider {
|
|
18
|
+
constructor(options) {
|
|
19
|
+
this.name = 'destination::local-strapi';
|
|
20
|
+
this.type = 'destination';
|
|
21
|
+
this.options = options;
|
|
22
|
+
}
|
|
23
|
+
async bootstrap() {
|
|
24
|
+
this.strapi = await this.options.getStrapi();
|
|
25
|
+
}
|
|
26
|
+
async close() {
|
|
27
|
+
await this.strapi?.destroy?.();
|
|
28
|
+
}
|
|
29
|
+
// TODO
|
|
30
|
+
getMetadata() {
|
|
31
|
+
return {};
|
|
32
|
+
}
|
|
33
|
+
getSchemas() {
|
|
34
|
+
if (!this.strapi) {
|
|
35
|
+
throw new Error('Not able to get Schemas. Strapi instance not found');
|
|
36
|
+
}
|
|
37
|
+
const schemas = {
|
|
38
|
+
...this.strapi.contentTypes,
|
|
39
|
+
...this.strapi.components,
|
|
40
|
+
};
|
|
41
|
+
return (0, utils_1.mapSchemasValues)(schemas);
|
|
42
|
+
}
|
|
43
|
+
getEntitiesStream() {
|
|
44
|
+
const self = this;
|
|
45
|
+
return new stream_1.Duplex({
|
|
46
|
+
objectMode: true,
|
|
47
|
+
async write(entity, _encoding, callback) {
|
|
48
|
+
if (!self.strapi) {
|
|
49
|
+
callback(new Error('Strapi instance not found'));
|
|
50
|
+
}
|
|
51
|
+
const { type: uid, id, data } = entity;
|
|
52
|
+
try {
|
|
53
|
+
await strapi.entityService.create(uid, { data });
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
// TODO: remove "any" cast
|
|
57
|
+
log.warn(chalk_1.default.bold(`Failed to import ${chalk_1.default.yellowBright(uid)} (${chalk_1.default.greenBright(id)})`));
|
|
58
|
+
e.details.errors
|
|
59
|
+
.map((err, i) => {
|
|
60
|
+
// TODO: add correct error type
|
|
61
|
+
const info = {
|
|
62
|
+
uid: chalk_1.default.yellowBright(`[${uid}]`),
|
|
63
|
+
path: chalk_1.default.blueBright(`[${err.path.join('.')}]`),
|
|
64
|
+
id: chalk_1.default.greenBright(`[${id}]`),
|
|
65
|
+
message: err.message,
|
|
66
|
+
};
|
|
67
|
+
return `(${i}) ${info.uid}${info.id}${info.path}: ${info.message}`;
|
|
68
|
+
})
|
|
69
|
+
.forEach((message) => log.warn(message));
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
callback();
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=local-strapi-destination-provider.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createConfigurationStream = void 0;
|
|
4
|
+
const stream_chain_1 = require("stream-chain");
|
|
5
|
+
const stream_1 = require("stream");
|
|
6
|
+
const fp_1 = require("lodash/fp");
|
|
7
|
+
/**
|
|
8
|
+
* Create a readable stream that export the Strapi app configuration
|
|
9
|
+
*/
|
|
10
|
+
const createConfigurationStream = (strapi) => {
|
|
11
|
+
// Core Store
|
|
12
|
+
const coreStoreStream = (0, stream_chain_1.chain)([
|
|
13
|
+
strapi.db.queryBuilder('strapi::core-store').stream(),
|
|
14
|
+
(data) => (0, fp_1.set)('value', JSON.parse(data.value), data),
|
|
15
|
+
wrapConfigurationItem('core-store'),
|
|
16
|
+
]);
|
|
17
|
+
// Webhook
|
|
18
|
+
const webhooksStream = (0, stream_chain_1.chain)([
|
|
19
|
+
strapi.db.queryBuilder('webhook').stream(),
|
|
20
|
+
wrapConfigurationItem('webhook'),
|
|
21
|
+
]);
|
|
22
|
+
const streams = [coreStoreStream, webhooksStream];
|
|
23
|
+
// Readable configuration stream
|
|
24
|
+
return stream_1.Readable.from((async function* () {
|
|
25
|
+
for (const stream of streams) {
|
|
26
|
+
for await (const item of stream) {
|
|
27
|
+
yield item;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
})());
|
|
31
|
+
};
|
|
32
|
+
exports.createConfigurationStream = createConfigurationStream;
|
|
33
|
+
const wrapConfigurationItem = (type) => (value) => ({
|
|
34
|
+
type,
|
|
35
|
+
value,
|
|
36
|
+
});
|
|
37
|
+
//# sourceMappingURL=configuration.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Readable, PassThrough } from 'stream';
|
|
2
|
+
/**
|
|
3
|
+
* Generate and consume content-types streams in order to stream each entity individually
|
|
4
|
+
*/
|
|
5
|
+
export declare const createEntitiesStream: (strapi: Strapi.Strapi) => Readable;
|
|
6
|
+
/**
|
|
7
|
+
* Create an entity transform stream which convert the output of
|
|
8
|
+
* the multi-content-types stream to the transfer entity format
|
|
9
|
+
*/
|
|
10
|
+
export declare const createEntitiesTransformStream: () => PassThrough;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createEntitiesTransformStream = exports.createEntitiesStream = void 0;
|
|
4
|
+
const stream_1 = require("stream");
|
|
5
|
+
/**
|
|
6
|
+
* Generate and consume content-types streams in order to stream each entity individually
|
|
7
|
+
*/
|
|
8
|
+
const createEntitiesStream = (strapi) => {
|
|
9
|
+
const contentTypes = Object.values(strapi.contentTypes);
|
|
10
|
+
async function* contentTypeStreamGenerator() {
|
|
11
|
+
for (const contentType of contentTypes) {
|
|
12
|
+
const stream = strapi.db
|
|
13
|
+
// Create a query builder instance (default type is 'select')
|
|
14
|
+
.queryBuilder(contentType.uid)
|
|
15
|
+
// Apply the populate
|
|
16
|
+
.populate(getPopulateAttributes(contentType))
|
|
17
|
+
// Get a readable stream
|
|
18
|
+
.stream();
|
|
19
|
+
yield { contentType, stream };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return stream_1.Readable.from((async function* () {
|
|
23
|
+
for await (const { stream, contentType } of contentTypeStreamGenerator()) {
|
|
24
|
+
for await (const entity of stream) {
|
|
25
|
+
yield { entity, contentType };
|
|
26
|
+
}
|
|
27
|
+
stream.destroy();
|
|
28
|
+
}
|
|
29
|
+
})());
|
|
30
|
+
};
|
|
31
|
+
exports.createEntitiesStream = createEntitiesStream;
|
|
32
|
+
/**
|
|
33
|
+
* Create an entity transform stream which convert the output of
|
|
34
|
+
* the multi-content-types stream to the transfer entity format
|
|
35
|
+
*/
|
|
36
|
+
const createEntitiesTransformStream = () => {
|
|
37
|
+
return new stream_1.PassThrough({
|
|
38
|
+
objectMode: true,
|
|
39
|
+
transform(data, _encoding, callback) {
|
|
40
|
+
const { entity, contentType } = data;
|
|
41
|
+
const { id, ...attributes } = entity;
|
|
42
|
+
callback(null, {
|
|
43
|
+
type: contentType.uid,
|
|
44
|
+
id,
|
|
45
|
+
data: attributes,
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
exports.createEntitiesTransformStream = createEntitiesTransformStream;
|
|
51
|
+
/**
|
|
52
|
+
* Get the list of attributes that needs to be populated for the entities streaming
|
|
53
|
+
*/
|
|
54
|
+
const getPopulateAttributes = (contentType) => {
|
|
55
|
+
const { attributes } = contentType;
|
|
56
|
+
return Object.keys(attributes).filter((key) => ['component', 'dynamiczone'].includes(attributes[key].type));
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=entities.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import type { IMetadata, ISourceProvider, ProviderType } from '../../../types';
|
|
3
|
+
export interface ILocalStrapiSourceProviderOptions {
|
|
4
|
+
getStrapi(): Strapi.Strapi | Promise<Strapi.Strapi>;
|
|
5
|
+
autoDestroy?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare const createLocalStrapiSourceProvider: (options: ILocalStrapiSourceProviderOptions) => LocalStrapiSourceProvider;
|
|
8
|
+
declare class LocalStrapiSourceProvider implements ISourceProvider {
|
|
9
|
+
name: string;
|
|
10
|
+
type: ProviderType;
|
|
11
|
+
options: ILocalStrapiSourceProviderOptions;
|
|
12
|
+
strapi?: Strapi.Strapi;
|
|
13
|
+
constructor(options: ILocalStrapiSourceProviderOptions);
|
|
14
|
+
bootstrap(): Promise<void>;
|
|
15
|
+
close(): Promise<void>;
|
|
16
|
+
getMetadata(): IMetadata;
|
|
17
|
+
streamEntities(): Promise<NodeJS.ReadableStream>;
|
|
18
|
+
streamLinks(): NodeJS.ReadableStream;
|
|
19
|
+
streamConfiguration(): NodeJS.ReadableStream;
|
|
20
|
+
getSchemas(): {
|
|
21
|
+
[x: string]: Partial<any>;
|
|
22
|
+
};
|
|
23
|
+
streamSchemas(): NodeJS.ReadableStream;
|
|
24
|
+
}
|
|
25
|
+
export declare type ILocalStrapiSourceProvider = InstanceType<typeof LocalStrapiSourceProvider>;
|
|
26
|
+
export {};
|