wip-backup-manager
Version:
This utility enables backup & restore of Kentico Kontent projects
223 lines (177 loc) • 6.61 kB
text/typescript
import * as fs from 'fs';
import yargs = require('yargs');
import { CleanService } from '../clean';
import { ICliFileConfig, fileHelper, getFilenameWithoutExtension, CliAction } from '../core';
import { ExportService } from '../export';
import { IImportSource, ImportService } from '../import';
import { ZipService } from '../zip';
import { ProjectContracts, SharedModels } from '@kentico/kontent-management';
const argv = yargs.argv;
const backupAsync = async (config: ICliFileConfig) => {
const exportService = new ExportService({
apiKey: config.apiKey,
projectId: config.projectId,
onExport: item => {
if (config.enableLog) {
console.log(`Exported: ${item.title} | ${item.type}`);
}
}
});
const zipService = new ZipService({
filename: config.zipFilename,
enableLog: config.enableLog
});
const report = await exportService.exportProjectValidationAsync();
const response = await exportService.exportAllAsync();
await zipService.createZipAsync(response);
if (exportContainsInconsistencies(report)) {
const logFilename: string = getLogFilename(config.zipFilename);
await fileHelper.createFileInCurrentFolderAsync(logFilename, JSON.stringify(report));
console.log(`Project contains inconsistencies which may cause future import to not work.`);
console.log(`See '${logFilename}' for more details.`);
}
console.log('Completed');
};
const getLogFilename = (filename: string) => {
return`${getFilenameWithoutExtension(filename)}_log.json`;
}
const cleanAsync = async (config: ICliFileConfig) => {
const cleanService = new CleanService({
onDelete: item => {
if (config.enableLog) {
console.log(`Deleted: ${item.title} | ${item.type}`);
}
},
projectId: config.projectId,
apiKey: config.apiKey
});
await cleanService.cleanAllAsync();
console.log('Completed');
};
const restoreAsync = async (config: ICliFileConfig) => {
const zipService = new ZipService({
filename: config.zipFilename,
enableLog: config.enableLog
});
const importService = new ImportService({
onImport: item => {
if (config.enableLog) {
console.log(`Imported: ${item.title} | ${item.type}`);
}
},
fixLanguages: true,
projectId: config.projectId,
apiKey: config.apiKey,
enableLog: config.enableLog,
workflowIdForImportedItems: '00000000-0000-0000-0000-000000000000',
process: {
contentItem: item => {
return true;
}
}
});
const data = await zipService.extractZipAsync();
if (canImport(data, config)) {
await importService.importFromSourceAsync(data);
console.log('Completed');
} else {
const logFilename: string = getLogFilename(config.zipFilename);
await fileHelper.createFileInCurrentFolderAsync(logFilename, JSON.stringify(data.validation));
console.log(`Project could not be imported due to data inconsistencies.`);
console.log(`A log file '${logFilename}' with issues was created in current folder.`);
console.log(`To import data regardless of issues, set 'force' config parameter to true`);
}
};
const validateConfig = (config?: ICliFileConfig) => {
if (!config) {
throw Error(`Invalid config file`);
}
const projectId = config.projectId;
const apiKey = config.apiKey;
const action = config.action;
if (!projectId) {
throw Error('Invalid project id');
}
if (!apiKey) {
throw Error('Invalid api key');
}
if (!action) {
throw Error('Invalid action');
}
};
const process = async () => {
const config = await getConfig();
validateConfig(config);
if (config.action === 'backup') {
await backupAsync(config);
} else if (config.action === 'clean') {
await cleanAsync(config);
} else if (config.action === 'restore') {
await restoreAsync(config);
} else {
throw Error(`Invalid action`);
}
};
const exportContainsInconsistencies = (projectReport: ProjectContracts.IProjectReportResponseContract) => {
if (projectReport.variant_issues.length > 0 || projectReport.type_issues.length > 0) {
return true;
}
return false;
};
const canImport = (importData: IImportSource, config: ICliFileConfig) => {
if (!importData.metadata.isInconsistentExport) {
return true;
}
if (config.force === true) {
return true;
}
return false;
};
const getConfig = async() => {
const configFilename: string = argv.config as string;
if (configFilename) {
// get config from file
const configFile = await fs.promises.readFile(`./${configFilename}`);
return JSON.parse(configFile.toString()) as ICliFileConfig;
}
const action: CliAction | undefined = argv.action as CliAction | undefined;
const apiKey: string | undefined = argv.apiKey as string | undefined;
const enableLog: boolean | undefined = (argv.enableLog as boolean | undefined) ?? true;
const force: boolean | undefined = (argv.force as boolean | undefined) ?? true;
const projectId: string | undefined = argv.projectId as string | undefined;
const zipFilename: string | undefined = (argv.zipFilename as string | undefined) ?? getDefaultBackupFilename()
if (!action) {
throw Error(`No action was provided`);
}
if (!apiKey) {
throw Error(`Api key was not provided`);
}
if (!projectId) {
throw Error(`Project id was not provided`);
}
// get config from command line
const config: ICliFileConfig = {
action,
apiKey,
enableLog,
force,
projectId,
zipFilename
};
return config;
}
const getDefaultBackupFilename = () => {
const date = new Date();
return `kontent-backup-${date.getDate()}-${date.getMonth() + 1}-${date.getFullYear()}-${date.getHours()}-${date.getMinutes()}`;
}
process().then(m => {}).catch(err => {
if (err instanceof SharedModels.ContentManagementBaseKontentError) {
console.log(`Management API error occured:`, err.message);
for (const validationError of err.validationErrors) {
console.log(validationError.message);
}
} else {
console.log(`There was an error processing your request: `, err);
}
});