310 lines
9.2 KiB
C++
310 lines
9.2 KiB
C++
#include "utils/directories.hpp"
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <set>
|
|
#include "config.hpp"
|
|
#include "utils/utils.hpp"
|
|
#include <filesystem>
|
|
#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<std::string> allowed_fields = {
|
|
"server_definition_paths",
|
|
"template_local_paths",
|
|
"template_registries",
|
|
"backups_path",
|
|
"log_level"
|
|
};
|
|
|
|
std::set<std::string> 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<std::string> 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<std::string> & a, const std::vector<std::string> & 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<std::string> 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<tRegistryEntry> config::get_template_registry_urls() {
|
|
nlohmann::json template_registries = mConfig["template_registries"];
|
|
std::vector<tRegistryEntry> registries;
|
|
for (auto ®istry : template_registries) {
|
|
if (registry.is_object() && !registry.empty())
|
|
registries.push_back(tRegistryEntry(registry));
|
|
}
|
|
return registries;
|
|
}
|
|
|
|
std::vector<std::string> config::get_local_template_paths()
|
|
{
|
|
nlohmann::json template_local_paths = mConfig["template_local_paths"];
|
|
std::vector<std::string> paths;
|
|
for (auto &path : template_local_paths) {
|
|
if (path.is_string() && !path.empty())
|
|
paths.push_back(path);
|
|
}
|
|
return paths;
|
|
}
|
|
|
|
std::vector<std::string> config::get_local_server_definition_paths() {
|
|
nlohmann::json server_definition_paths = mConfig["server_definition_paths"];
|
|
std::vector<std::string> 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<std::string> paths = get_local_server_definition_paths();
|
|
if (paths.empty())
|
|
return "";
|
|
return paths[0];
|
|
}
|
|
|
|
std::string config::get_template_create_path()
|
|
{
|
|
std::vector<std::string> 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
|