#include "utils/directories.hpp" #include #include #include #include "config.hpp" #include "utils/utils.hpp" #include #include "utils/execute.hpp" #include "utils/output.hpp" namespace dropshell { config & gConfig() { static config *globalConfig = new config(); return *globalConfig; } config::config() : mIsConfigSet(false) { } config::~config() { } bool config::load_config() { // load json config file. std::string config_path = localfile::dropshell_json(); if (config_path.empty() || !std::filesystem::exists(config_path)) return false; std::ifstream config_file(config_path); if (!config_file.is_open()) return false; try { mConfig = nlohmann::json::parse(config_file); } catch (nlohmann::json::parse_error& ex) { error << "Failed to parse config file: " << ex.what() << std::endl; return false; } // Validate the config format - check for required and supported fields std::set allowed_fields = { "server_definition_paths", "template_local_paths", "template_registries", "backups_path", "log_level" }; std::set deprecated_fields = { "template_registry_URLs", "template_upload_token" }; // Check for deprecated fields for (const auto& field : deprecated_fields) { if (mConfig.contains(field)) { error << "Config file contains deprecated field '" << field << "'" << std::endl; error << "Please update your config file to the new format." << std::endl; if (field == "template_registry_URLs") { error << "Replace 'template_registry_URLs' with 'template_registries' using the format:" << std::endl; error << " \"template_registries\": [" << std::endl; error << " {" << std::endl; error << " \"name\": \"main\"," << std::endl; error << " \"url\": \"https://templates.dropshell.app\"," << std::endl; error << " \"token\": \"\"" << std::endl; error << " }" << std::endl; error << " ]" << std::endl; } return false; } } // Check for unknown fields for (auto& [key, value] : mConfig.items()) { if (allowed_fields.find(key) == allowed_fields.end()) { error << "Config file contains unknown field '" << key << "'" << std::endl; error << "Allowed fields are: "; bool first = true; for (const auto& field : allowed_fields) { if (!first) error << ", "; error << field; first = false; } error << std::endl; return false; } } // Validate template_registries format if present if (mConfig.contains("template_registries")) { if (!mConfig["template_registries"].is_array()) { error << "'template_registries' must be an array" << std::endl; return false; } for (const auto& registry : mConfig["template_registries"]) { if (!registry.is_object()) { error << "Each registry in 'template_registries' must be an object" << std::endl; return false; } if (!registry.contains("name") || !registry.contains("url")) { error << "Each registry must have 'name' and 'url' fields" << std::endl; return false; } } } // Validate log_level if present if (mConfig.contains("log_level")) { if (!mConfig["log_level"].is_string()) { error << "'log_level' must be a string" << std::endl; return false; } std::string log_level = mConfig["log_level"]; std::set valid_levels = {"debug", "info", "warning", "error"}; if (valid_levels.find(log_level) == valid_levels.end()) { error << "Invalid log_level '" << log_level << "'" << std::endl; error << "Valid levels are: debug, info, warning, error" << std::endl; return false; } } mIsConfigSet = true; apply_log_level(); // Apply the log level from config return true; } void _append(std::vector & a, const std::vector & b) { if (b.empty()) return; if (a.empty()) a = b; else a.insert(std::end(a), std::begin(b), std::end(b)); } bool config::save_config(bool create_aux_directories) { std::string config_path = localfile::dropshell_json(); if (config_path.empty()) return false; std::filesystem::create_directories(get_parent(config_path)); std::ofstream config_file(config_path); if (!config_file.is_open()) return false; if (!mIsConfigSet) { std::string homedir = localpath::current_user_home(); std::string dropshell_base = homedir + "/.dropshell"; mConfig["server_definition_paths"] = { dropshell_base + "/servers" }; mConfig["template_local_paths"] = { dropshell_base + "/local_templates" }; mConfig["template_registries"] = nlohmann::json::array({ nlohmann::json::object({ {"name", "main"}, {"url", "https://templates.dropshell.app"}, {"token", ""} }) }); mConfig["backups_path"] = { dropshell_base + "/backups" }; mConfig["log_level"] = "info"; // Default log level } config_file << mConfig.dump(4); config_file.close(); if (create_aux_directories) { std::vector paths; _append(paths, get_local_template_paths()); _append(paths, get_local_server_definition_paths()); for (auto & p : paths) if (!std::filesystem::exists(p)) { std::cout << "Creating directory: " << p << std::endl; std::filesystem::create_directories(p); } } debug << "Config paths: " << std::endl; for (auto [key,value] : mConfig.items()) { debug << " " << key << ": " << value << std::endl; } return true; } bool config::is_config_set() const { return mIsConfigSet; } bool config::is_agent_installed() { return std::filesystem::exists(localfile::bb64()); } std::vector config::get_template_registry_urls() { nlohmann::json template_registries = mConfig["template_registries"]; std::vector registries; for (auto ®istry : template_registries) { if (registry.is_object() && !registry.empty()) registries.push_back(tRegistryEntry(registry)); } return registries; } std::vector config::get_local_template_paths() { nlohmann::json template_local_paths = mConfig["template_local_paths"]; std::vector paths; for (auto &path : template_local_paths) { if (path.is_string() && !path.empty()) paths.push_back(path); } return paths; } std::vector config::get_local_server_definition_paths() { nlohmann::json server_definition_paths = mConfig["server_definition_paths"]; std::vector paths; for (auto &path : server_definition_paths) { if (path.is_string() && !path.empty()) paths.push_back(path); } return paths; } std::string config::get_server_create_path() { std::vector paths = get_local_server_definition_paths(); if (paths.empty()) return ""; return paths[0]; } std::string config::get_template_create_path() { std::vector paths = get_local_template_paths(); if (paths.empty()) return ""; return paths[0]; } std::string config::get_backups_path() { nlohmann::json backups_path = mConfig["backups_path"]; if (backups_path.empty()) return ""; if (backups_path.is_string()) return backups_path; warning << "backups_path is not a string: " << backups_path << std::endl; return ""; } dropshell::tRegistryEntry::tRegistryEntry(nlohmann::json json) { valid = false; if (json.is_object() && !json.empty()) { for (auto &[key, value] : json.items()) { if (value.is_string() && !value.empty()) switch (switchhash(key.c_str())) { case switchhash("name"): name = value; break; case switchhash("url"): url = value; break; case switchhash("token"): token = value; break; default: break; } } valid = (!url.empty()&&!name.empty()); // token can be empty. } } tRegistryEntry::~tRegistryEntry() { } std::string config::get_log_level() const { if (!mIsConfigSet || !mConfig.contains("log_level")) return "info"; // Default log level return mConfig["log_level"]; } void config::apply_log_level() { std::string level = get_log_level(); set_log_level(level); } } // namespace dropshell