mirror of
https://github.com/strapi/strapi.git
synced 2025-11-03 11:25:17 +00:00
Merge branch 'features/deits' into deits/import-configs
This commit is contained in:
commit
db8dac601f
@ -18,18 +18,15 @@ import { chain, Writable } from 'stream-chain';
|
||||
import { createEncryptionCipher } from '../../encryption/encrypt';
|
||||
import { createFilePathFactory, createTarEntryStream } from './utils';
|
||||
export interface ILocalFileDestinationProviderOptions {
|
||||
// Encryption
|
||||
encryption: {
|
||||
enabled: boolean;
|
||||
key?: string;
|
||||
};
|
||||
|
||||
// Compression
|
||||
compression: {
|
||||
enabled: boolean;
|
||||
};
|
||||
|
||||
// File
|
||||
file: {
|
||||
path: string;
|
||||
maxSize?: number;
|
||||
|
||||
@ -24,25 +24,18 @@ const METADATA_FILE_PATH = 'metadata.json';
|
||||
* Provider options
|
||||
*/
|
||||
export interface ILocalFileSourceProviderOptions {
|
||||
/**
|
||||
* Path to the backup archive
|
||||
*/
|
||||
backupFilePath: string;
|
||||
file: {
|
||||
path: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether the backup data is encrypted or not
|
||||
*/
|
||||
encrypted?: boolean;
|
||||
encryption: {
|
||||
enabled: boolean;
|
||||
key?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encryption key used to decrypt the encrypted data (if necessary)
|
||||
*/
|
||||
encryptionKey?: string;
|
||||
|
||||
/**
|
||||
* Whether the backup data is compressed or not
|
||||
*/
|
||||
compressed?: boolean;
|
||||
compression: {
|
||||
enabled: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export const createLocalFileSourceProvider = (options: ILocalFileSourceProviderOptions) => {
|
||||
@ -58,7 +51,9 @@ class LocalFileSourceProvider implements ISourceProvider {
|
||||
constructor(options: ILocalFileSourceProviderOptions) {
|
||||
this.options = options;
|
||||
|
||||
if (this.options.encrypted && this.options.encryptionKey === undefined) {
|
||||
const { encryption } = this.options;
|
||||
|
||||
if (encryption.enabled && encryption.key === undefined) {
|
||||
throw new Error('Missing encryption key');
|
||||
}
|
||||
}
|
||||
@ -67,7 +62,7 @@ class LocalFileSourceProvider implements ISourceProvider {
|
||||
* Pre flight checks regarding the provided options (making sure that the provided path is correct, etc...)
|
||||
*/
|
||||
bootstrap() {
|
||||
const path = this.options.backupFilePath;
|
||||
const { path } = this.options.file;
|
||||
const isValidBackupPath = fs.existsSync(path);
|
||||
|
||||
// Check if the provided path exists
|
||||
@ -108,13 +103,17 @@ class LocalFileSourceProvider implements ISourceProvider {
|
||||
return this.#streamJsonlDirectory('configuration');
|
||||
}
|
||||
|
||||
#getBackupStream(decompress: boolean = true) {
|
||||
const path = this.options.backupFilePath;
|
||||
const readStream = fs.createReadStream(path);
|
||||
const streams: StreamItemArray = [readStream];
|
||||
#getBackupStream() {
|
||||
const { file, encryption, compression } = this.options;
|
||||
|
||||
// Handle decompression
|
||||
if (decompress) {
|
||||
const fileStream = fs.createReadStream(file.path);
|
||||
const streams: StreamItemArray = [fileStream];
|
||||
|
||||
if (encryption.enabled && encryption.key) {
|
||||
streams.push(createDecryptionCipher(encryption.key));
|
||||
}
|
||||
|
||||
if (compression.enabled) {
|
||||
streams.push(zip.createGunzip());
|
||||
}
|
||||
|
||||
@ -146,22 +145,12 @@ class LocalFileSourceProvider implements ISourceProvider {
|
||||
},
|
||||
|
||||
onentry(entry) {
|
||||
const transforms = [];
|
||||
|
||||
if (options.encrypted) {
|
||||
transforms.push(createDecryptionCipher(options.encryptionKey!));
|
||||
}
|
||||
|
||||
if (options.compressed) {
|
||||
transforms.push(zip.createGunzip());
|
||||
}
|
||||
|
||||
transforms.push(
|
||||
const transforms = [
|
||||
// JSONL parser to read the data chunks one by one (line by line)
|
||||
parser(),
|
||||
// The JSONL parser returns each line as key/value
|
||||
(line: { key: string; value: any }) => line.value
|
||||
);
|
||||
(line: { key: string; value: any }) => line.value,
|
||||
];
|
||||
|
||||
entry
|
||||
// Pipe transforms
|
||||
@ -220,7 +209,7 @@ class LocalFileSourceProvider implements ISourceProvider {
|
||||
() => {
|
||||
// If the promise hasn't been resolved and we've parsed all
|
||||
// the archive entries, then the file doesn't exist
|
||||
reject(`${filePath} not found in the archive stream`);
|
||||
reject(new Error(`File "${filePath}" not found`));
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@ -15,10 +15,6 @@ interface ILocalStrapiDestinationProviderOptions {
|
||||
strategy: 'restore' | 'merge';
|
||||
}
|
||||
|
||||
// TODO: getting some type errors with @strapi/logger that need to be resolved first
|
||||
// const log = createLogger();
|
||||
const log = console;
|
||||
|
||||
export const createLocalStrapiDestinationProvider = (
|
||||
options: ILocalStrapiDestinationProviderOptions
|
||||
) => {
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
// FIXME
|
||||
/* eslint-disable import/extensions */
|
||||
const _ = require('lodash');
|
||||
const path = require('path');
|
||||
const resolveCwd = require('resolve-cwd');
|
||||
const { yellow } = require('chalk');
|
||||
const { Command, Option } = require('commander');
|
||||
@ -316,14 +317,15 @@ program
|
||||
'path and filename to the Strapi export file you want to import'
|
||||
)
|
||||
.addOption(
|
||||
new Option('--key <string>', 'Provide encryption key in command instead of using a prompt')
|
||||
new Option('-k, --key <string>', 'Provide encryption key in command instead of using a prompt')
|
||||
)
|
||||
.allowExcessArguments(false)
|
||||
.hook('preAction', async (thisCommand) => {
|
||||
const opts = thisCommand.opts();
|
||||
const ext = path.extname(String(opts.file));
|
||||
|
||||
// check extension to guess if we should prompt for key
|
||||
if (String(opts.file).endsWith('.enc')) {
|
||||
if (ext === '.enc') {
|
||||
if (!opts.key) {
|
||||
const answers = await inquirer.prompt([
|
||||
{
|
||||
|
||||
@ -8,10 +8,15 @@ const {
|
||||
// eslint-disable-next-line import/no-unresolved, node/no-missing-require
|
||||
} = require('@strapi/data-transfer');
|
||||
const { isObject } = require('lodash/fp');
|
||||
const path = require('path');
|
||||
|
||||
const strapi = require('../../index');
|
||||
const { buildTransferTable } = require('./utils');
|
||||
|
||||
/**
|
||||
* @typedef {import('@strapi/data-transfer').ILocalFileSourceProviderOptions} ILocalFileSourceProviderOptions
|
||||
*/
|
||||
|
||||
const logger = console;
|
||||
|
||||
module.exports = async (opts) => {
|
||||
@ -20,14 +25,12 @@ module.exports = async (opts) => {
|
||||
logger.error('Could not parse arguments');
|
||||
process.exit(1);
|
||||
}
|
||||
const filename = opts.file;
|
||||
|
||||
/**
|
||||
* From strapi backup file
|
||||
*/
|
||||
const sourceOptions = {
|
||||
backupFilePath: filename,
|
||||
};
|
||||
const sourceOptions = getLocalFileSourceOptions(opts);
|
||||
|
||||
const source = createLocalFileSourceProvider(sourceOptions);
|
||||
|
||||
/**
|
||||
@ -69,16 +72,50 @@ module.exports = async (opts) => {
|
||||
const engine = createTransferEngine(source, destination, engineOptions);
|
||||
|
||||
try {
|
||||
logger.log('Starting import...');
|
||||
logger.info('Starting import...');
|
||||
|
||||
const results = await engine.transfer();
|
||||
const table = buildTransferTable(results.engine);
|
||||
logger.log(table.toString());
|
||||
logger.info(table.toString());
|
||||
|
||||
logger.log('Import process has been completed successfully!');
|
||||
logger.info('Import process has been completed successfully!');
|
||||
process.exit(0);
|
||||
} catch (e) {
|
||||
logger.log(`Import process failed unexpectedly: ${e.message}`);
|
||||
logger.error(`Import process failed unexpectedly: ${e.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Infer local file source provider options based on a given filename
|
||||
*
|
||||
* @param {{ file: string; key?: string }} opts
|
||||
*
|
||||
* @return {ILocalFileSourceProviderOptions}
|
||||
*/
|
||||
const getLocalFileSourceOptions = (opts) => {
|
||||
/**
|
||||
* @type {ILocalFileSourceProviderOptions}
|
||||
*/
|
||||
const options = {
|
||||
file: { path: opts.file },
|
||||
compression: { enabled: false },
|
||||
encryption: { enabled: false },
|
||||
};
|
||||
|
||||
const { extname, parse } = path;
|
||||
|
||||
let file = options.file.path;
|
||||
|
||||
if (extname(file) === '.enc') {
|
||||
file = parse(file).name;
|
||||
options.encryption = { enabled: true, key: opts.key };
|
||||
}
|
||||
|
||||
if (extname(file) === '.gz') {
|
||||
file = parse(file).name;
|
||||
options.compression = { enabled: true };
|
||||
}
|
||||
|
||||
return options;
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user