From 263edd9b5020f6bd7982de69a7a091e39dd66ade Mon Sep 17 00:00:00 2001
From: Your Name <j@842.be>
Date: Sun, 18 May 2025 15:58:29 +1200
Subject: [PATCH] dropshell release 2025.0518.1558

---
 source/src/commands/restoredata.cpp | 137 ++++++++++++++++++++++++++++
 1 file changed, 137 insertions(+)
 create mode 100644 source/src/commands/restoredata.cpp

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 <unistd.h>
+#include <cstring>
+#include <iostream>
+#include <sstream>
+#include <filesystem>
+
+#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<std::string> 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<shared_commands::cBackupFileName> 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<std::string, std::string> 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