From ebc59c40b209ade616ce23446ac5664a6b386f63 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 24 Aug 2025 21:35:45 +1200 Subject: [PATCH] Update source/src/templates.cpp --- source/src/templates.cpp | 260 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 251 insertions(+), 9 deletions(-) diff --git a/source/src/templates.cpp b/source/src/templates.cpp index 4073f70..bac4ba8 100644 --- a/source/src/templates.cpp +++ b/source/src/templates.cpp @@ -6,11 +6,13 @@ #include #include #include + #include #include #include "utils/envmanager.hpp" #include "utils/directories.hpp" #include "utils/utils.hpp" + #include "utils/output.hpp" #include "templates.hpp" #include "config.hpp" #include "utils/hash.hpp" @@ -69,32 +71,272 @@ std::set template_source_registry::get_template_list() { - #pragma message("TODO: Implement template_source_registry::get_template_list") - return std::set(); + // Query the registry for available templates + // The registry should return a JSON list of available templates + std::string list_url = mRegistry.url + "/dir"; + + nlohmann::json json_response; + + // For HTTPS URLs, use curl to fetch the JSON + if (list_url.substr(0, 8) == "https://") { + std::string temp_file = "/tmp/dropshell_registry_list_" + std::to_string(std::chrono::system_clock::now().time_since_epoch().count()); + std::string cmd = "curl -fsSL " + quote(list_url) + " -o " + quote(temp_file) + " 2>/dev/null"; + int result = system(cmd.c_str()); + + if (result == 0) { + try { + std::ifstream file(temp_file); + if (file.is_open()) { + file >> json_response; + file.close(); + } + } catch (...) { + // Failed to parse JSON + } + std::filesystem::remove(temp_file); + } + } else { + json_response = get_json_from_url(list_url); + } + + std::set templates; + if (json_response.is_null() || !json_response.contains("entries")) { + warning << "Failed to get template list from registry: " << mRegistry.name << std::endl; + return templates; + } + + // Parse the entries array to extract unique template names + for (const auto& entry : json_response["entries"]) { + if (entry.contains("labeltags")) { + for (const auto& label : entry["labeltags"]) { + // Extract template name from label (format: "template:version") + std::string label_str = label.get(); + size_t colon_pos = label_str.find(':'); + if (colon_pos != std::string::npos) { + std::string template_name = label_str.substr(0, colon_pos); + templates.insert(template_name); + } + } + } + } + + return templates; } bool template_source_registry::has_template(const std::string& template_name) { - #pragma message("TODO: Implement template_source_registry::has_template") + // Check if template exists in registry + std::string check_url = mRegistry.url + "/exists/" + template_name + ":latest"; + + // For HTTPS URLs, use curl to fetch the JSON + nlohmann::json json_response; + if (check_url.substr(0, 8) == "https://") { + // Create a temporary file for the response + std::string temp_file = "/tmp/dropshell_registry_check_" + std::to_string(std::chrono::system_clock::now().time_since_epoch().count()); + std::string cmd = "curl -fsSL " + quote(check_url) + " -o " + quote(temp_file) + " 2>/dev/null"; + int result = system(cmd.c_str()); + + if (result == 0) { + try { + std::ifstream file(temp_file); + if (file.is_open()) { + file >> json_response; + file.close(); + } + } catch (...) { + // Failed to parse JSON + } + std::filesystem::remove(temp_file); + } + } else { + json_response = get_json_from_url(check_url); + } + + if (!json_response.is_null() && json_response.contains("exists")) { + return json_response["exists"].get(); + } + return false; } template_info template_source_registry::get_template_info(const std::string& template_name) { - #pragma message("TODO: Implement template_source_registry::get_template_info") - return template_info(); + // Check if template exists in registry + if (!has_template(template_name)) { + return template_info(); + } + + // Get cache directory + std::filesystem::path cache_dir = get_cache_dir(); + std::filesystem::path template_cache_dir = cache_dir / template_name; + std::filesystem::path template_json_file = cache_dir / (template_name + ".json"); + + // Create cache directory if it doesn't exist + if (!std::filesystem::exists(cache_dir)) { + std::filesystem::create_directories(cache_dir); + } + + // Get metadata from registry to check version + std::string meta_url = mRegistry.url + "/meta/" + template_name + ":latest"; + + nlohmann::json registry_metadata; + + // For HTTPS URLs, use curl to fetch the JSON + if (meta_url.substr(0, 8) == "https://") { + std::string temp_file = "/tmp/dropshell_registry_meta_" + std::to_string(std::chrono::system_clock::now().time_since_epoch().count()); + std::string cmd = "curl -fsSL " + quote(meta_url) + " -o " + quote(temp_file) + " 2>/dev/null"; + int result = system(cmd.c_str()); + + if (result == 0) { + try { + std::ifstream file(temp_file); + if (file.is_open()) { + file >> registry_metadata; + file.close(); + } + } catch (...) { + // Failed to parse JSON + } + std::filesystem::remove(temp_file); + } + } else { + registry_metadata = get_json_from_url(meta_url); + } + + if (registry_metadata.is_null()) { + warning << "Failed to get metadata for template: " << template_name << std::endl; + return template_info(); + } + + // Check if we need to download/update the template + bool need_download = true; + std::string registry_version = "unknown"; + std::string registry_hash = ""; + + // Extract version and hash from registry metadata + if (registry_metadata.contains("metadata")) { + auto& metadata = registry_metadata["metadata"]; + if (metadata.contains("version")) { + registry_version = metadata["version"].get(); + } + } + if (registry_metadata.contains("hash")) { + registry_hash = registry_metadata["hash"].get(); + } + + // Check if we have a cached version + if (std::filesystem::exists(template_json_file)) { + try { + std::ifstream cache_file(template_json_file); + nlohmann::json cache_json; + cache_file >> cache_json; + cache_file.close(); + + // Compare versions or hashes + if (cache_json.contains("hash") && !registry_hash.empty()) { + if (cache_json["hash"].get() == registry_hash) { + need_download = false; + } + } else if (cache_json.contains("version")) { + std::string cached_version = cache_json["version"].get(); + if (cached_version == registry_version) { + need_download = false; + } + } + } catch (...) { + // If reading cache fails, re-download + need_download = true; + } + } + + // Download and extract if needed + if (need_download) { + info << "Downloading template '" << template_name << "' from registry..." << std::endl; + + // Download the .tgz file + std::string download_url = mRegistry.url + "/" + template_name + ":latest"; + std::filesystem::path temp_tgz = cache_dir / (template_name + ".tgz"); + + if (!download_file(download_url, temp_tgz.string())) { + error << "Failed to download template: " << template_name << std::endl; + return template_info(); + } + + // Remove old template directory if it exists + if (std::filesystem::exists(template_cache_dir)) { + std::filesystem::remove_all(template_cache_dir); + } + + // Extract the .tgz file + std::string extract_cmd = "tar -xzf " + quote(temp_tgz.string()) + " -C " + quote(cache_dir.string()); + int result = system(extract_cmd.c_str()); + if (result != 0) { + error << "Failed to extract template: " << template_name << std::endl; + std::filesystem::remove(temp_tgz); + return template_info(); + } + + // Clean up the .tgz file + std::filesystem::remove(temp_tgz); + + // Generate .template_info.env if it doesn't exist + std::filesystem::path template_info_env_path = template_cache_dir / "config" / filenames::template_info_env; + if (!std::filesystem::exists(template_info_env_path)) { + // Create config directory if needed + std::filesystem::create_directories(template_cache_dir / "config"); + + // Write .template_info.env file + std::ofstream info_file(template_info_env_path); + info_file << "# Template information" << std::endl; + info_file << "TEMPLATE=" << template_name << std::endl; + info_file << "TEMPLATE_SOURCE=registry" << std::endl; + info_file << "TEMPLATE_REGISTRY=" << mRegistry.name << std::endl; + info_file << "TEMPLATE_VERSION=" << registry_version << std::endl; + if (!registry_hash.empty()) { + info_file << "TEMPLATE_HASH=" << registry_hash << std::endl; + } + info_file.close(); + } + + // Update cache JSON file + nlohmann::json cache_json; + cache_json["template"] = template_name; + cache_json["version"] = registry_version; + cache_json["hash"] = registry_hash; + cache_json["registry"] = mRegistry.name; + cache_json["last_updated"] = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + + std::ofstream cache_file(template_json_file); + cache_file << cache_json.dump(4); + cache_file.close(); + + info << "Template '" << template_name << "' downloaded and cached successfully" << std::endl; + } + + // Return template info pointing to the cached template + return template_info( + template_name, + "Registry: " + mRegistry.name, + template_cache_dir + ); } bool template_source_registry::template_command_exists(const std::string& template_name, const std::string& command) { - #pragma message("TODO: Implement template_source_registry::template_command_exists") - return false; + // Get template info to ensure it's downloaded and cached + auto tinfo = get_template_info(template_name); + if (!tinfo.is_set()) { + return false; + } + + // Check if the command script exists in the cached template + std::filesystem::path script_path = tinfo.local_template_path() / (command + ".sh"); + return std::filesystem::exists(script_path); } std::filesystem::path template_source_registry::get_cache_dir() { - #pragma message("TODO: Implement template_source_registry::get_cache_dir") - return std::filesystem::path(); + return localpath::template_cache(); }