#include "servers.hpp" #include "server_env_manager.hpp" #include "service_runner.hpp" #include "utils/tableprint.hpp" #include "utils/envmanager.hpp" #include "utils/directories.hpp" #include "services.hpp" #include "config.hpp" #include "templates.hpp" #include "contrib/transwarp.hpp" #include #include #include #include namespace dropshell { std::vector get_configured_servers() { std::vector servers; std::vector lsdp = gConfig().get_local_server_definition_paths(); if (lsdp.empty()) return servers; for (auto servers_dir : lsdp) { if (!servers_dir.empty() && std::filesystem::exists(servers_dir)) { for (const auto& entry : std::filesystem::directory_iterator(servers_dir)) { if (std::filesystem::is_directory(entry)) { std::string server_name = entry.path().filename().string(); if (server_name.empty() || server_name[0]=='.' || server_name[0]=='_') continue; server_env_manager env(server_name); if (!env.is_valid()) { std::cerr << "Error: Invalid server environment file: " << entry.path().string() << std::endl; continue; } servers.push_back({ server_name, env.get_SSH_HOST(), env.get_SSH_USER(), env.get_SSH_PORT() }); } } } } return servers; } ServerInfo get_server_info(const std::string &server_name) { std::vector lsdp = gConfig().get_local_server_definition_paths(); if (lsdp.empty()) return ServerInfo(); for (auto &config_dir : lsdp) { std::string server_dir = config_dir + "/" + server_name; if (std::filesystem::exists(server_dir)) { server_env_manager env(server_name); if (!env.is_valid()) { std::cerr << "Error: Invalid server environment file: " << server_dir << std::endl; continue; } return ServerInfo({server_name, env.get_SSH_HOST(), env.get_SSH_USER(), env.get_SSH_PORT()}); } } return ServerInfo(); } // https://github.com/bloomen/transwarp?tab=readme-ov-file#range-functions void list_servers() { auto servers = get_configured_servers(); tableprint tp("All DropShell Servers"); tp.add_row({"Name", "User", "Address", "Health", "Ports"}); transwarp::parallel exec{servers.size()}; auto task = transwarp::for_each(exec, servers.begin(), servers.end(), [&](const ServerInfo& server) { std::map status = service_runner::get_all_services_status(server.name); std::set ports_used; std::string serviceticks = ""; for (const auto& [service_name, service_status] : status) { ports_used.insert(service_status.ports.begin(), service_status.ports.end()); serviceticks += service_runner::HealthStatus2String(service_status.health) + " "; } std::string ports_used_str = ""; for (const auto& port : ports_used) ports_used_str += std::to_string(port) + " "; tp.add_row({server.name, server.ssh_user, server.ssh_host, serviceticks, ports_used_str}); }); task->wait(); tp.print(); } void show_server_details(const std::string& server_name) { server_env_manager env(server_name); if (!env.is_valid()) { std::cerr << "Error: Invalid server environment file: " << server_name << std::endl; return; } //--------------------- // Check if server is reachable via SSH std::string ssh_address = env.get_SSH_HOST(); std::string ssh_user = env.get_SSH_USER(); std::string ssh_port = env.get_SSH_PORT(); if (!ssh_address.empty()) { std::cout << std::endl << "Server Status:" << std::endl; std::cout << std::string(40, '-') << std::endl; // Try to connect to the server std::string cmd = "ssh -o ConnectTimeout=5 " + ssh_user + "@" + ssh_address + " -p " + ssh_port + " 'echo connected' 2>/dev/null"; int result = system(cmd.c_str()); if (result == 0) { std::cout << "Status: Online" << std::endl; // // Get uptime if possible // cmd = "ssh " + ssh_address + " 'uptime' 2>/dev/null"; // int rval = system(cmd.c_str()); // if (rval != 0) { // std::cout << "Error: Failed to get uptime" << std::endl; // } } else { std::cout << "Status: Offline" << std::endl; } } std::cout << std::endl; //--------------------- { std::cout << std::endl; tableprint tp("Server Configuration: " + server_name, true); tp.add_row({"Key", "Value"}); for (const auto& [key, value] : env.get_variables()) { tp.add_row({key, value}); } tp.print(); } //--------------------- // list services, and run healthcheck on each { tableprint tp("Services: " + server_name, false); tp.add_row({"Status", "Service", "Ports"}); std::map status = service_runner::get_all_services_status(server_name); std::set ports_used; std::string serviceticks = ""; for (const auto& [service_name, service_status] : status) { std::string healthy = service_runner::HealthStatus2String(service_status.health); std::string ports_str = ""; for (const auto& port : service_status.ports) ports_str += std::to_string(port) + " "; tp.add_row({healthy, service_name, ports_str}); } // end of for (const auto& service : services) tp.print(); } // end of list services } // end of show_server_details void create_server(const std::string &server_name) { // 1. check if server name already exists std::string server_existing_dir = localpath::server(server_name); if (!server_existing_dir.empty()) { std::cerr << "Error: Server name already exists: " << server_name << std::endl; std::cerr << "Current server path: " << server_existing_dir << std::endl; return; } // 2. create a new directory in the user config directory auto lsdp = gConfig().get_local_server_definition_paths(); if (lsdp.empty() || lsdp[0].empty()) { std::cerr << "Error: Local server definition path not found - is DropShell initialised?" << std::endl; return; } std::string server_dir = lsdp[0] + "/" + server_name; std::filesystem::create_directory(server_dir); // 3. create a template server.env file in the server directory std::string user = getenv("USER"); std::string server_env_path = server_dir + "/server.env"; std::ofstream server_env_file(server_env_path); server_env_file << "SSH_HOST=" << server_name << std::endl; server_env_file << "SSH_USER=" << user << std::endl; server_env_file << "SSH_PORT=" << 22 << std::endl; server_env_file << std::endl; server_env_file << "DROPSHELL_DIR=/home/"+user+"/.dropshell" << std::endl; server_env_file.close(); // 4. add dropshell-agent service to server create_service(server_name, "dropshell-agent", "dropshell-agent", true); // silently create service. std::cout << "Server created successfully: " << server_name << std::endl; std::cout << "Please complete the installation:" < &commands) { std::vector servers = get_configured_servers(); for (const auto& server : servers) { auto services = dropshell::get_server_services_info(server.name); for (const auto& service : services) commands.merge(dropshell::get_used_commands(server.name, service.service_name)); } } } // namespace dropshell