2022-06-09 03:44:18 +00:00
|
|
|
const fsPromises = require('fs/promises');
|
|
|
|
|
const path = require('path');
|
|
|
|
|
const os = require('os');
|
|
|
|
|
const readlineSync = require('readline-sync');
|
|
|
|
|
|
|
|
|
|
const shell = require('shelljs');
|
|
|
|
|
|
|
|
|
|
const utils = require('./utils');
|
|
|
|
|
const Constants = require('./constants');
|
|
|
|
|
|
2022-07-07 05:49:25 +00:00
|
|
|
async function getBackupFileName() {
|
|
|
|
|
|
|
|
|
|
const backupFiles = await utils.listLocalBackupFiles();
|
|
|
|
|
console.log("\n" + backupFiles.length + " Appsmith backup file(s) found: [Sorted in ascending/chronological order]");
|
|
|
|
|
if (backupFiles.length == 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
console.log('----------------------------------------------------------------');
|
|
|
|
|
console.log('Index\t|\tAppsmith Backup Archive File');
|
|
|
|
|
console.log('----------------------------------------------------------------');
|
|
|
|
|
for (var i = 0; i < backupFiles.length; i++) {
|
|
|
|
|
if (i === backupFiles.length - 1)
|
|
|
|
|
console.log(i + '\t|\t' + backupFiles[i] + ' <--Most recent backup');
|
|
|
|
|
else
|
|
|
|
|
console.log(i + '\t|\t' + backupFiles[i]);
|
|
|
|
|
}
|
|
|
|
|
console.log('----------------------------------------------------------------');
|
|
|
|
|
|
|
|
|
|
var backupFileIndex = parseInt(readlineSync.question('Please enter the backup file index: '), 10);
|
|
|
|
|
if (!isNaN(backupFileIndex) && Number.isInteger(backupFileIndex) && (backupFileIndex >= 0) && (backupFileIndex < backupFiles.length)) {
|
|
|
|
|
return backupFiles[parseInt(backupFileIndex, 10)];
|
2022-06-09 03:44:18 +00:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
console.log('Invalid input, please try the command again with a valid option');
|
2022-07-07 05:49:25 +00:00
|
|
|
return;
|
2022-06-09 03:44:18 +00:00
|
|
|
}
|
2022-07-07 05:49:25 +00:00
|
|
|
|
2022-06-09 03:44:18 +00:00
|
|
|
}
|
|
|
|
|
|
2022-07-07 05:49:25 +00:00
|
|
|
async function extractArchive(backupFilePath, restoreRootPath) {
|
2022-06-09 03:44:18 +00:00
|
|
|
console.log('Extracting the Appsmith backup archive at ' + backupFilePath);
|
|
|
|
|
await utils.execCommand(['tar', '-C', restoreRootPath, '-xf', backupFilePath]);
|
|
|
|
|
console.log('Extracting the backup archive completed');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function restoreDatabase(restoreContentsPath) {
|
|
|
|
|
console.log('Restoring database ....');
|
|
|
|
|
await utils.execCommand(['mongorestore', `--uri=${process.env.APPSMITH_MONGODB_URI}`, '--drop', `--archive=${restoreContentsPath}/mongodb-data.gz`, '--gzip']);
|
|
|
|
|
console.log('Restoring database completed');
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-07 05:49:25 +00:00
|
|
|
async function restoreDockerEnvFile(restoreContentsPath, backupName) {
|
2022-06-09 03:44:18 +00:00
|
|
|
console.log('Restoring docker environment file');
|
|
|
|
|
const dockerEnvFile = '/appsmith-stacks/configuration/docker.env';
|
2022-07-07 05:49:25 +00:00
|
|
|
var encryptionPwd = process.env.APPSMITH_ENCRYPTION_PASSWORD;
|
2022-06-09 03:44:18 +00:00
|
|
|
var encryptionSalt = process.env.APPSMITH_ENCRYPTION_SALT;
|
|
|
|
|
await utils.execCommand(['mv', dockerEnvFile, dockerEnvFile + '.' + backupName]);
|
|
|
|
|
await utils.execCommand(['cp', restoreContentsPath + '/docker.env', dockerEnvFile]);
|
|
|
|
|
|
2022-07-07 05:49:25 +00:00
|
|
|
if (encryptionPwd && encryptionSalt) {
|
|
|
|
|
const input = readlineSync.question('Existing encryption env values of the previous instance were found.\n\
|
2022-06-09 03:44:18 +00:00
|
|
|
Press Enter to continue with existing encryption values\n\
|
2022-07-07 05:49:25 +00:00
|
|
|
Or Type "n"/"No" to provide encryption key & password for the new restore instance.\n');
|
2022-06-09 03:44:18 +00:00
|
|
|
const answer = input && input.toLocaleUpperCase();
|
|
|
|
|
if (answer === 'N' || answer === 'NO') {
|
|
|
|
|
encryptionPwd = readlineSync.question('Enter the APPSMITH_ENCRYPTION_PASSWORD: ', {
|
2022-07-07 05:49:25 +00:00
|
|
|
hideEchoBack: true
|
2022-06-09 03:44:18 +00:00
|
|
|
});
|
|
|
|
|
encryptionSalt = readlineSync.question('Enter the APPSMITH_ENCRYPTION_SALT: ', {
|
2022-07-07 05:49:25 +00:00
|
|
|
hideEchoBack: true
|
2022-06-09 03:44:18 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
console.log('Restoring docker environment file with existing encryption password & salt');
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-07-07 05:49:25 +00:00
|
|
|
else {
|
2022-06-09 03:44:18 +00:00
|
|
|
encryptionPwd = readlineSync.question('Enter the APPSMITH_ENCRYPTION_PASSWORD: ', {
|
2022-07-07 05:49:25 +00:00
|
|
|
hideEchoBack: true
|
2022-06-09 03:44:18 +00:00
|
|
|
});
|
|
|
|
|
encryptionSalt = readlineSync.question('Enter the APPSMITH_ENCRYPTION_SALT: ', {
|
2022-07-07 05:49:25 +00:00
|
|
|
hideEchoBack: true
|
2022-06-09 03:44:18 +00:00
|
|
|
});
|
|
|
|
|
}
|
2022-07-07 05:49:25 +00:00
|
|
|
|
2022-06-09 03:44:18 +00:00
|
|
|
await fsPromises.appendFile(dockerEnvFile, '\nAPPSMITH_ENCRYPTION_PASSWORD=' + encryptionPwd +
|
2022-07-07 05:49:25 +00:00
|
|
|
'\nAPPSMITH_ENCRYPTION_SALT=' + encryptionSalt);
|
2022-06-09 03:44:18 +00:00
|
|
|
|
|
|
|
|
console.log('Restoring docker environment file completed');
|
|
|
|
|
}
|
2022-07-07 05:49:25 +00:00
|
|
|
|
|
|
|
|
async function restoreGitStorageArchive(restoreContentsPath, backupName) {
|
2022-06-09 03:44:18 +00:00
|
|
|
console.log('Restoring git-storage archive');
|
|
|
|
|
// TODO: Consider APPSMITH_GIT_ROOT env for later iterations
|
|
|
|
|
const gitRoot = '/appsmith-stacks/git-storage';
|
2022-07-07 05:49:25 +00:00
|
|
|
await utils.execCommand(['mv', gitRoot, gitRoot + '-' + backupName]);
|
2022-06-09 03:44:18 +00:00
|
|
|
await utils.execCommand(['mv', restoreContentsPath + '/git-storage', '/appsmith-stacks']);
|
|
|
|
|
console.log('Restoring git-storage archive completed');
|
|
|
|
|
|
|
|
|
|
}
|
2022-07-07 05:49:25 +00:00
|
|
|
|
|
|
|
|
async function checkRestoreVersionCompatability(restoreContentsPath) {
|
|
|
|
|
const content = await fsPromises.readFile('/opt/appsmith/rts/version.js', { encoding: 'utf8' });
|
2022-06-09 03:44:18 +00:00
|
|
|
const currentVersion = content.match(/\bexports\.VERSION\s*=\s*["']([^"]+)["']/)[1];
|
2022-07-07 05:49:25 +00:00
|
|
|
const manifest_data = await fsPromises.readFile(restoreContentsPath + '/manifest.json', { encoding: 'utf8' });
|
|
|
|
|
const manifest_json = JSON.parse(manifest_data);
|
|
|
|
|
const restoreVersion = manifest_json["appsmithVersion"];
|
2022-06-09 03:44:18 +00:00
|
|
|
console.log('Current Appsmith Version: ' + currentVersion);
|
|
|
|
|
console.log('Restore Appsmith Version: ' + restoreVersion);
|
|
|
|
|
|
2022-07-07 05:49:25 +00:00
|
|
|
if (currentVersion === restoreVersion) {
|
2022-06-09 03:44:18 +00:00
|
|
|
console.log('The restore instance is compatible with the current appsmith version');
|
|
|
|
|
} else {
|
|
|
|
|
console.log('**************************** WARNING ****************************');
|
|
|
|
|
console.log('The Appsmith instance to be restored is not compatible with the current version.');
|
|
|
|
|
console.log('Please update your appsmith image to \"index.docker.io/appsmith/appsmith-ce:' + restoreVersion +
|
2022-07-07 05:49:25 +00:00
|
|
|
'\" in the \"docker-compose.yml\" file\nand run the cmd: \"docker-compose restart\" ' +
|
|
|
|
|
'after the restore process is completed, to ensure the restored instance runs successfully.');
|
2022-06-09 03:44:18 +00:00
|
|
|
const confirm = readlineSync.question('Press Enter to continue \nOr Type "c" to cancel the restore process.\n');
|
|
|
|
|
if (confirm.toLowerCase() === 'c') {
|
|
|
|
|
process.exit(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function run() {
|
|
|
|
|
let errorCode = 0;
|
|
|
|
|
try {
|
2022-07-07 05:49:25 +00:00
|
|
|
check_supervisord_status_cmd = '/usr/bin/supervisorctl >/dev/null 2>&1';
|
|
|
|
|
shell.exec(check_supervisord_status_cmd, function (code) {
|
|
|
|
|
if (code > 0) {
|
|
|
|
|
shell.echo('application is not running, starting supervisord');
|
|
|
|
|
shell.exec('/usr/bin/supervisord');
|
2022-06-09 03:44:18 +00:00
|
|
|
}
|
2022-07-07 05:49:25 +00:00
|
|
|
});
|
2022-06-09 03:44:18 +00:00
|
|
|
|
2022-07-07 05:49:25 +00:00
|
|
|
const backupFileName = await getBackupFileName();
|
|
|
|
|
if (backupFileName == null) {
|
2022-06-09 03:44:18 +00:00
|
|
|
process.exit(errorCode);
|
2022-07-07 05:49:25 +00:00
|
|
|
} else {
|
|
|
|
|
const backupFilePath = path.join(Constants.BACKUP_PATH, backupFileName);
|
|
|
|
|
const backupName = backupFileName.replace(/\.tar\.gz$/, "");
|
|
|
|
|
const restoreRootPath = await fsPromises.mkdtemp(os.tmpdir());
|
|
|
|
|
const restoreContentsPath = path.join(restoreRootPath, backupName);
|
|
|
|
|
|
|
|
|
|
await extractArchive(backupFilePath, restoreRootPath);
|
|
|
|
|
await checkRestoreVersionCompatability(restoreContentsPath);
|
|
|
|
|
|
|
|
|
|
console.log('****************************************************************');
|
|
|
|
|
console.log('Restoring Appsmith instance from the backup at ' + backupFilePath);
|
|
|
|
|
utils.stop(['backend', 'rts']);
|
|
|
|
|
await restoreDatabase(restoreContentsPath);
|
|
|
|
|
await restoreDockerEnvFile(restoreContentsPath, backupName);
|
|
|
|
|
await restoreGitStorageArchive(restoreContentsPath, backupName);
|
|
|
|
|
console.log('Appsmith instance successfully restored.');
|
|
|
|
|
await fsPromises.rm(restoreRootPath, { recursive: true, force: true });
|
2022-06-09 03:44:18 +00:00
|
|
|
}
|
2022-07-07 05:49:25 +00:00
|
|
|
} catch (err) {
|
|
|
|
|
console.log(err);
|
|
|
|
|
errorCode = 1;
|
|
|
|
|
|
|
|
|
|
} finally {
|
|
|
|
|
utils.start(['backend', 'rts']);
|
|
|
|
|
process.exit(errorCode);
|
|
|
|
|
|
|
|
|
|
}
|
2022-06-09 03:44:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
|
run,
|
|
|
|
|
};
|