diff --git a/source/src/commands/restoredata.cpp b/source/src/commands/restoredata.cpp new file mode 100644 index 0000000..fbbe939 --- /dev/null +++ b/source/src/commands/restoredata.cpp @@ -0,0 +1,137 @@ +#include +#include +#include +#include +#include + +#include "utils/output.hpp" +#include "utils/assert.hpp" +#include "utils/utils.hpp" +#include "command_registry.hpp" +#include "config.hpp" +#include "services.hpp" +#include "servers.hpp" +#include "server_env_manager.hpp" +#include "templates.hpp" +#include "utils/directories.hpp" +#include "shared_commands.hpp" + + +namespace dropshell { + +int restoredata_handler(const CommandContext& ctx); + +void restoredata_autocomplete(const CommandContext& ctx); + + +static std::vector restoredata_name_list={"restoredata","rd","restore","rest"}; + +// Static registration +struct RestoreDataCommandRegister { + RestoreDataCommandRegister() { + CommandRegistry::instance().register_command({ + restoredata_name_list, + restoredata_handler, + restoredata_autocomplete, + false, // hidden + true, // requires_config + true, // requires_install + 3, // min_args (after command) + 3, // max_args (after command) + "restoredata SERVER SERVICE BACKUP_FILE", + "Restore data for a service on a server, overwriting the existing data.", + // heredoc + R"( + restoredata SERVER SERVICE BACKUP_FILE Restore data to a service on a server. Destructive. + + Note: This command will not create any service configuration, you need + to have a valid service installed first. + The backup file must be in the local backups directory. + + WARNING: This will permanently overwrite the service's data on the remote server! + )" + }); + } +} restoredata_command_register; + + + +int restoredata_handler(const CommandContext& ctx) +{ + ASSERT(ctx.args.size() == 2, "Invalid number of arguments"); + std::string server = ctx.args[0]; + std::string service = ctx.args[1]; + std::string backup_file = ctx.args[2]; + + server_env_manager server_env(server); + if (!server_env.is_valid()) { + error << "Server " << server << " is not valid" << std::endl; + return 1; + } + + shared_commands::cBackupFileName backup_details(backup_file); + if (!backup_details.is_valid()) { + error << "Invalid backup file: " << backup_file << std::endl; + return 1; + } + + + + + return 0; +} + +void restoredata_autocomplete(const CommandContext& ctx) +{ + shared_commands::std_autocomplete(ctx); + if (ctx.args.size() == 2) // next arg is the backup file + { + std::string server = ctx.args[0]; + std::string service = ctx.args[1]; + + std::vector backups; + + LocalServiceInfo service_info = get_service_info(server, service); + if (!SIvalid(service_info)) { + error << "Service " << service << " is not valid" << std::endl; + return; + } + std::string template_name = service_info.template_name; + + std::string local_backups_dir = gConfig().get_local_backup_path(); + if (local_backups_dir.empty() || !std::filesystem::exists(local_backups_dir)) { + error << "Error: Local backups directory not found: " << local_backups_dir << std::endl; + return; + } + for (const auto& entry : std::filesystem::directory_iterator(local_backups_dir)) { + if (!entry.is_regular_file()) continue; + std::string filename = entry.path().filename().string(); + shared_commands::cBackupFileName backup_details(filename); + if (backup_details.is_valid()) { + if (backup_details.get_template_name() == template_name) { + backups.push_back(backup_details); + } + } + } + + // sort backups by datetime + std::sort(backups.begin(), backups.end(), [](const shared_commands::cBackupFileName& a, const shared_commands::cBackupFileName& b) { + return a.get_datetime() > b.get_datetime(); + }); + + // print most recent backup for each {host,service} pair + std::map unique_backups; + for (const auto& backup : backups) { + std::string key = backup.get_server() + "-" + backup.get_service(); + if (unique_backups.find(key) == unique_backups.end()) { + unique_backups[key] = backup.get_filename(); + } + } + for (const auto& [key, value] : unique_backups) { + rawout << value << std::endl; + } + } +} + + +} // namespace dropshell \ No newline at end of file