#include #include #include #include #include #include #include #include #include #include "utils/envmanager.hpp" #include "utils/directories.hpp" #include "utils/utils.hpp" #include "templates.hpp" #include "config.hpp" #include "utils/hash.hpp" namespace dropshell { // ------------------------------------------------------------------------------------------------ // template_source_local // ------------------------------------------------------------------------------------------------ std::set template_source_local::get_template_list() { std::set templates; // Helper function to add templates from a directory auto add_templates_from_dir = [&templates](const std::string& dir_path) { if (!std::filesystem::exists(dir_path)) return; for (const auto& entry : std::filesystem::directory_iterator(dir_path)) if (entry.is_directory()) templates.insert(entry.path().filename().string()); }; add_templates_from_dir(mLocalPath); return templates; } bool template_source_local::has_template(const std::string& template_name) { std::filesystem::path path = mLocalPath / template_name; return (std::filesystem::exists(path)); } bool template_source_local::template_command_exists(const std::string& template_name, const std::string& command) { std::filesystem::path path = mLocalPath / template_name / (command+".sh"); return std::filesystem::exists(path); } template_info template_source_local::get_template_info(const std::string& template_name) { std::filesystem::path path = mLocalPath / template_name; if (!std::filesystem::exists(path)) return template_info(); return template_info( template_name, mLocalPath.string(), path ); } // ------------------------------------------------------------------------------------------------ // template_source_registry // ------------------------------------------------------------------------------------------------ std::set template_source_registry::get_template_list() { #pragma message("TODO: Implement template_source_registry::get_template_list") return std::set(); } bool template_source_registry::has_template(const std::string& template_name) { #pragma message("TODO: Implement template_source_registry::has_template") 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(); } 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; } std::filesystem::path template_source_registry::get_cache_dir() { #pragma message("TODO: Implement template_source_registry::get_cache_dir") return std::filesystem::path(); } // ------------------------------------------------------------------------------------------------ // template_manager // ------------------------------------------------------------------------------------------------ void template_manager::list_templates() const { ASSERT(mLoaded && mSources.size() > 0, "Template manager not loaded, or no template sources found."); auto templates = get_template_list(); if (templates.empty()) { std::cout << "No templates found." << std::endl; return; } std::cout << "Available templates:" << std::endl; // print templates. std::cout << std::string(60, '-') << std::endl; bool first = true; for (const auto& t : templates) { std::cout << (first ? "" : ", ") << t; first = false; } std::cout << std::endl; std::cout << std::string(60, '-') << std::endl; } std::set template_manager::get_template_list() const { ASSERT(mLoaded && mSources.size() > 0, "Template manager not loaded, or no template sources found."); std::set templates; for (const auto& source : mSources) { auto source_templates = source->get_template_list(); templates.insert(source_templates.begin(), source_templates.end()); } return templates; } bool template_manager::has_template(const std::string &template_name) const { ASSERT(mLoaded && mSources.size() > 0, "Template manager not loaded, or no template sources found."); template_source_interface* source = get_source(template_name); if (!source) return false; return true; } template_info template_manager::get_template_info(const std::string &template_name) const { ASSERT(mLoaded && mSources.size() > 0, "Template manager not loaded, or no template sources found."); template_source_interface* source = get_source(template_name); if (source) return source->get_template_info(template_name); // fail return template_info(); } bool template_manager::template_command_exists(const std::string &template_name, const std::string &command) const { ASSERT(mLoaded && mSources.size() > 0, "Template manager not loaded, or no template sources found."); template_source_interface* source = get_source(template_name); if (!source) { error << "Template '" << template_name << "' not found" << std::endl; return false; } return source->template_command_exists(template_name, command); } bool template_manager::create_template(const std::string &template_name) const { // 1. Create a new directory in the user templates directory std::vector local_server_definition_paths = gConfig().get_local_server_definition_paths(); if (local_server_definition_paths.empty()) { error << "No local server definition paths found" << std::endl; info << "Run 'dropshell edit' to configure DropShell" << std::endl; return false; } auto tinfo = get_template_info(template_name); if (tinfo.is_set()) { error << "Template '" << template_name << "' already exists at " << tinfo.locationID() << std::endl; return false; } auto local_template_paths = gConfig().get_local_template_paths(); if (local_template_paths.empty()) { error << "No local template paths found" << std::endl; info << "Run 'dropshell edit' to add one to the DropShell config" << std::endl; return false; } std::string new_template_path = local_template_paths[0] + "/" + template_name; // Create the new template directory std::filesystem::create_directories(new_template_path); // 2. Copy the example template from the system templates directory auto example_info = gTemplateManager().get_template_info("example-nginx"); if (!example_info.is_set()) { error << "Example template not found" << std::endl; return false; } std::string example_template_path = example_info.local_template_path(); // Copy all files from example template to new template for (const auto& entry : std::filesystem::recursive_directory_iterator(example_template_path)) { std::string relative_path = entry.path().string().substr(example_template_path.length()); std::string target_path = new_template_path + relative_path; if (entry.is_directory()) { std::filesystem::create_directory(target_path); } else { std::filesystem::copy_file(entry.path(), target_path); } } // modify the TEMPLATE=example line in the .template_info.env file to TEMPLATE= std::string search_string = "TEMPLATE="; std::string replacement_line = "TEMPLATE=" + template_name; std::string service_env_path = new_template_path + "/config/" + filenames::template_info_env; if (!replace_line_in_file(service_env_path, search_string, replacement_line)) { error << "Failed to replace TEMPLATE= line in the " << filenames::template_info_env <<" file" << std::endl; return false; } // 3. Print out the README.txt file and the path std::string readme_path = new_template_path + "/README.txt"; if (std::filesystem::exists(readme_path)) { std::cout << "\nREADME contents:" << std::endl; std::cout << std::string(60, '-') << std::endl; std::ifstream readme_file(readme_path); if (readme_file.is_open()) { std::string line; while (std::getline(readme_file, line)) { std::cout << line << std::endl; } readme_file.close(); } std::cout << std::string(60, '-') << std::endl; } std::cout << std::endl; std::cout << "Template '" << template_name << "' created at " << new_template_path << std::endl; return test_template(new_template_path); } void template_manager::load_sources() { ASSERT(mSources.empty(), "Template manager already loaded (sources are not empty)."); ASSERT(gConfig().is_config_set(), "Config not set."); ASSERT(!mLoaded, "Template manager already loaded."); auto local_template_paths = gConfig().get_local_template_paths(); if (local_template_paths.empty()) return; for (const auto& path : local_template_paths) mSources.push_back(std::make_unique(path)); auto registry_urls = gConfig().get_template_registry_urls(); for (const auto& url : registry_urls) mSources.push_back(std::make_unique(url)); mLoaded = true; } void template_manager::print_sources() const { std::cout << "Template sources: "; for (const auto& source : mSources) { std::cout << "[" << source->get_description() << "] "; } std::cout << std::endl; } bool template_manager::required_file(std::string path, std::string template_name) { if (!std::filesystem::exists(path)) { error << path << " file not found in template - REQUIRED." << template_name << std::endl; return false; } return true; } template_source_interface *template_manager::get_source(const std::string &template_name) const { ASSERT(mLoaded && mSources.size() > 0, "Template manager not loaded, or no template sources found."); for (const auto& source : mSources) { if (source->has_template(template_name)) { return source.get(); } } return nullptr; } bool template_manager::test_template(const std::string &template_path) { if (template_path.empty()) return false; if (!std::filesystem::exists(template_path)) return false; std::string template_name = std::filesystem::path(template_path).filename().string(); std::vector required_files = { "config/" + filenames::service_env, "config/" + filenames::template_info_env, "install.sh", "uninstall.sh" }; for (const auto& file : required_files) { if (!required_file(template_path + "/" + file, template_name)) return false; // check if file is executable, if it ends in .sh std::string suffix=".sh"; if (file.find(suffix) == file.size() - suffix.size()) { std::filesystem::path path = template_path + "/" + file; auto perms = std::filesystem::status(path).permissions(); if ((perms & std::filesystem::perms::owner_exec) == std::filesystem::perms::none) error << file << " is not executable" << std::endl; } } // ------------------------------------------------------------ // check TEMPLATE= line. std::map all_env_vars; std::vector env_files = { "config/" + filenames::service_env, "config/" + filenames::template_info_env }; for (const auto& file : env_files) { { // load service.env from the service on this machine. std::map env_vars; envmanager env_manager(template_path + "/" + file); env_manager.load(); env_manager.get_all_variables(env_vars); all_env_vars.merge(env_vars); } } // determine template name. auto it = all_env_vars.find("TEMPLATE"); if (it == all_env_vars.end()) { error << "TEMPLATE variable not found in " << template_path << std::endl; return false; } std::string env_template_name = it->second; if (env_template_name.empty()) { error << "TEMPLATE variable is empty in " << template_path << std::endl; return false; } if (env_template_name != template_name) { error << "TEMPLATE variable is wrong in " << template_path << std::endl; return false; } return true; } template_manager & gTemplateManager() { static template_manager instance; return instance; } template_info::template_info(const std::string &template_name, const std::string &location_id, const std::filesystem::path &local_template_path) : mTemplateName(template_name), mLocationID(location_id), mTemplateLocalPath(local_template_path), mTemplateValid(template_manager::test_template(local_template_path.string())), mIsSet(!template_name.empty() && !location_id.empty() && !local_template_path.empty()),\ mHash(0) { if (!std::filesystem::exists(local_template_path)) { error << "Template path does not exist: " << local_template_path << std::endl; return; } mHash = hash_directory_recursive(local_template_path); } } // namespace dropshell