Fairly big refactor...
This commit is contained in:
parent
50e340f1c5
commit
9a3dd18525
@ -1,107 +0,0 @@
|
|||||||
#include "dropshell.hpp"
|
|
||||||
#include "init_user_directory.hpp"
|
|
||||||
#include "config.hpp"
|
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
#include <iostream>
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
namespace fs = boost::filesystem;
|
|
||||||
|
|
||||||
namespace dropshell {
|
|
||||||
|
|
||||||
std::vector<std::string> autocomplete_list_servers() {
|
|
||||||
std::vector<std::string> servers;
|
|
||||||
std::string user_dir;
|
|
||||||
|
|
||||||
if (!get_user_directory(user_dir)) {
|
|
||||||
std::cerr << "Error: User directory not set" << std::endl;
|
|
||||||
return servers;
|
|
||||||
}
|
|
||||||
|
|
||||||
fs::path servers_dir = fs::path(user_dir) / "servers";
|
|
||||||
if (!fs::exists(servers_dir)) {
|
|
||||||
std::cerr << "Error: Servers directory not found" << std::endl;
|
|
||||||
return servers;
|
|
||||||
}
|
|
||||||
|
|
||||||
// List all server directories
|
|
||||||
for (const auto& entry : fs::directory_iterator(servers_dir)) {
|
|
||||||
if (fs::is_directory(entry)) {
|
|
||||||
servers.push_back(entry.path().filename().string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return servers;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> autocomplete_list_services(const std::string& server_name) {
|
|
||||||
std::vector<std::string> services;
|
|
||||||
std::string user_dir;
|
|
||||||
|
|
||||||
if (!get_user_directory(user_dir)) {
|
|
||||||
std::cerr << "Error: User directory not set" << std::endl;
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
fs::path server_dir = fs::path(user_dir) / "servers" / server_name;
|
|
||||||
if (!fs::exists(server_dir)) {
|
|
||||||
std::cerr << "Error: Server directory not found" << std::endl;
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for .env files in the server directory
|
|
||||||
for (const auto& entry : fs::directory_iterator(server_dir)) {
|
|
||||||
if (entry.path().extension() == ".env" && entry.path().filename().string() != "_server.env") {
|
|
||||||
services.push_back(entry.path().stem().string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> autocomplete_list_commands() {
|
|
||||||
std::vector<std::string> commands;
|
|
||||||
std::set<std::string> unique_commands; // To ensure deduplication
|
|
||||||
|
|
||||||
|
|
||||||
NEED TO CHANGE THIS SO IT ITERATES THROUGH ACTUAL INSTALLED SERVICES, NOT TEMPLATES!
|
|
||||||
|
|
||||||
// Helper function to add commands from a directory
|
|
||||||
auto add_commands_from_dir = [&unique_commands](const std::string& dir_path) {
|
|
||||||
if (!fs::exists(dir_path)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate through all template directories
|
|
||||||
for (const auto& template_entry : fs::directory_iterator(dir_path)) {
|
|
||||||
if (!fs::is_directory(template_entry)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for shell files in each template directory
|
|
||||||
for (const auto& file_entry : fs::directory_iterator(template_entry.path())) {
|
|
||||||
if (fs::is_regular_file(file_entry) &&
|
|
||||||
file_entry.path().extension() == ".sh" &&
|
|
||||||
file_entry.path().filename().string()[0] != '_') {
|
|
||||||
unique_commands.insert(file_entry.path().stem().string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// System templates directory
|
|
||||||
const std::string system_templates_dir = "/opt/dropshell/templates";
|
|
||||||
add_commands_from_dir(system_templates_dir);
|
|
||||||
|
|
||||||
std::string user_templates_dir;
|
|
||||||
if (get_user_directory(user_templates_dir)) {
|
|
||||||
// User templates directory
|
|
||||||
user_templates_dir += "/usertemplates";
|
|
||||||
add_commands_from_dir(user_templates_dir);
|
|
||||||
}
|
|
||||||
// Convert set to vector for return
|
|
||||||
commands.assign(unique_commands.begin(), unique_commands.end());
|
|
||||||
return commands;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace dropshell
|
|
@ -1,14 +0,0 @@
|
|||||||
#ifndef __AUTOCOMPLETE_H
|
|
||||||
#define __AUTOCOMPLETE_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace dropshell {
|
|
||||||
|
|
||||||
std::vector<std::string> autocomplete_list_servers();
|
|
||||||
std::vector<std::string> autocomplete_list_services(const std::string& server_name);
|
|
||||||
|
|
||||||
} // namespace dropshell
|
|
||||||
|
|
||||||
#endif // __AUTOCOMPLETE_H
|
|
107
src/config.cpp
107
src/config.cpp
@ -1,68 +1,87 @@
|
|||||||
#include "dropshell.hpp"
|
#include "utils/directories.hpp"
|
||||||
#include "init_user_directory.hpp"
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/property_tree/ptree.hpp>
|
#include <boost/property_tree/ptree.hpp>
|
||||||
#include <boost/property_tree/ini_parser.hpp>
|
#include <boost/property_tree/ini_parser.hpp>
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
|
#include "envmanager.hpp"
|
||||||
|
|
||||||
namespace fs = boost::filesystem;
|
namespace fs = boost::filesystem;
|
||||||
namespace pt = boost::property_tree;
|
namespace pt = boost::property_tree;
|
||||||
|
|
||||||
namespace dropshell {
|
namespace dropshell {
|
||||||
|
|
||||||
// Default user directory
|
|
||||||
static bool config_loaded = false;
|
|
||||||
|
|
||||||
bool is_config_loaded() {
|
config *get_global_config() {
|
||||||
return config_loaded;
|
static config *gConfig = new config();
|
||||||
|
return gConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get_config_path(std::string &path)
|
config::config() {
|
||||||
{
|
}
|
||||||
// Try ~/.config/dropshell/dropshell.conf
|
config::~config() {
|
||||||
const char* home = std::getenv("HOME");
|
|
||||||
if (home) {
|
|
||||||
fs::path user_path = fs::path(home) / ".config" / "dropshell" / "dropshell.conf";
|
|
||||||
path = user_path.string();
|
|
||||||
return fs::exists(path);
|
|
||||||
}
|
|
||||||
std::cerr << "Warning: Couldn't determine user directory" << std::endl;
|
|
||||||
path = "";
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool load_config() {
|
bool config::load_config() {
|
||||||
std::string config_path;
|
std::string config_path = get_local_dropshell_config_path();
|
||||||
if (!get_config_path(config_path))
|
if (config_path.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
try {
|
envmanager config_env(config_path);
|
||||||
pt::ptree tree;
|
if (!config_env.load())
|
||||||
pt::read_ini(config_path, tree);
|
{
|
||||||
bool config_okay = false;
|
std::cerr << "Warning: Unable to read configuration file: " << config_path << std::endl;
|
||||||
|
return false;
|
||||||
// Try to read user directory from config
|
|
||||||
try {
|
|
||||||
std::string user_dir;
|
|
||||||
user_dir = tree.get<std::string>("user.directory");
|
|
||||||
// Update user directory through the new interface
|
|
||||||
set_user_directory(user_dir);
|
|
||||||
config_okay = true;
|
|
||||||
|
|
||||||
} catch (const pt::ptree_error&) {
|
|
||||||
std::cerr << "Warning: User directory not set in config" << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// config loaded okay.
|
local_config_directory = config_env.get_variable_substituted("local.config.directory");
|
||||||
config_loaded = config_okay;
|
if (local_config_directory.empty())
|
||||||
return true;
|
{
|
||||||
|
std::cerr << "Warning: User directory not set in config" << std::endl;
|
||||||
} catch (const std::exception& e) {
|
return false;
|
||||||
std::cerr << "Warning: Error reading config file: " << e.what() << std::endl;
|
}
|
||||||
return true; // Not a critical error
|
return true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void config::save_config()
|
||||||
|
{
|
||||||
|
std::string config_path = get_local_dropshell_config_path();
|
||||||
|
if (config_path.empty())
|
||||||
|
{
|
||||||
|
std::cerr << "Warning: Unable to save configuration file, as DropShell has not been initialised."<< std::endl;
|
||||||
|
std::cerr << "Please run 'dropshell init <path>' to initialise DropShell." << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
envmanager config_env(config_path);
|
||||||
|
config_env.set_variable("local.config.directory", local_config_directory);
|
||||||
|
config_env.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool config::is_config_set() const
|
||||||
|
{
|
||||||
|
return !local_config_directory.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool config::get_local_config_directory(std::string& path) const {
|
||||||
|
path = local_config_directory;
|
||||||
|
return !path.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void config::init_local_config_directory(const std::string& path) {
|
||||||
|
// Convert to canonical path
|
||||||
|
fs::path abs_path = fs::canonical(path);
|
||||||
|
|
||||||
|
// The directory must exist
|
||||||
|
if (!fs::exists(abs_path)) {
|
||||||
|
throw std::runtime_error("The specifieduser directory does not exist: " + abs_path.string());
|
||||||
|
}
|
||||||
|
|
||||||
|
local_config_directory = abs_path.string();
|
||||||
|
save_config();
|
||||||
|
std::cout << "Local config directory initialized to: " << abs_path.string() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace dropshell
|
} // namespace dropshell
|
@ -4,9 +4,23 @@
|
|||||||
|
|
||||||
namespace dropshell {
|
namespace dropshell {
|
||||||
|
|
||||||
// Configuration functions
|
class config {
|
||||||
bool get_config_path(std::string& path);
|
public:
|
||||||
bool load_config();
|
config();
|
||||||
bool is_config_loaded();
|
~config();
|
||||||
|
bool load_config();
|
||||||
|
void save_config();
|
||||||
|
|
||||||
|
bool is_config_set() const;
|
||||||
|
|
||||||
|
bool get_local_config_directory(std::string& path) const;
|
||||||
|
void init_local_config_directory(const std::string& path);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string local_config_directory;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
config *get_global_config();
|
||||||
|
|
||||||
} // namespace dropshell
|
} // namespace dropshell
|
@ -1,27 +0,0 @@
|
|||||||
#include "dropshell.hpp"
|
|
||||||
#include "templates.hpp"
|
|
||||||
#include <iostream>
|
|
||||||
#include <iomanip>
|
|
||||||
|
|
||||||
void dropshell::list_templates() {
|
|
||||||
template_manager tm;
|
|
||||||
std::vector<template_info> templates;
|
|
||||||
|
|
||||||
if (!tm.get_templates(templates)) {
|
|
||||||
std::cerr << "Error: Failed to get templates" << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (templates.empty()) {
|
|
||||||
std::cout << "No templates found." << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "Available templates:" << std::endl;
|
|
||||||
std::cout << std::left << std::setw(20) << "Name" << "Path" << std::endl;
|
|
||||||
std::cout << std::string(60, '-') << std::endl;
|
|
||||||
|
|
||||||
for (const auto& t : templates) {
|
|
||||||
std::cout << std::left << std::setw(20) << t.name << t.path << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,76 +0,0 @@
|
|||||||
#include "init_user_directory.hpp"
|
|
||||||
#include "config.hpp"
|
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
#include <boost/property_tree/ptree.hpp>
|
|
||||||
#include <boost/property_tree/ini_parser.hpp>
|
|
||||||
|
|
||||||
namespace fs = boost::filesystem;
|
|
||||||
namespace pt = boost::property_tree;
|
|
||||||
|
|
||||||
namespace dropshell {
|
|
||||||
|
|
||||||
static std::string user_directory;
|
|
||||||
bool get_user_directory(std::string& path) {
|
|
||||||
path = user_directory;
|
|
||||||
return !path.empty();
|
|
||||||
}
|
|
||||||
void set_user_directory(const std::string& path) {
|
|
||||||
user_directory = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
void init_user_directory(const std::string& path) {
|
|
||||||
// Convert to canonical path
|
|
||||||
fs::path abs_path = fs::canonical(path);
|
|
||||||
|
|
||||||
// The directory must exist
|
|
||||||
if (!fs::exists(abs_path)) {
|
|
||||||
throw std::runtime_error("The user directory does not exist: " + abs_path.string());
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the servers subdirectory if it doesn't exist
|
|
||||||
fs::path servers_dir = abs_path / "servers";
|
|
||||||
if (!fs::exists(servers_dir)) {
|
|
||||||
fs::create_directories(servers_dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update config file
|
|
||||||
std::string config_path;
|
|
||||||
if (!get_config_path(config_path)) {
|
|
||||||
// No config file exists, create one in user's home directory
|
|
||||||
const char* home = std::getenv("HOME");
|
|
||||||
if (!home) {
|
|
||||||
throw std::runtime_error("HOME environment variable not set");
|
|
||||||
}
|
|
||||||
|
|
||||||
fs::path config_dir = fs::path(home) / ".config" / "dropshell";
|
|
||||||
if (!fs::exists(config_dir)) {
|
|
||||||
fs::create_directories(config_dir);
|
|
||||||
}
|
|
||||||
config_path = (config_dir / "dropshell.conf").string();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
pt::ptree tree;
|
|
||||||
// Read existing config if it exists
|
|
||||||
if (fs::exists(config_path)) {
|
|
||||||
pt::read_ini(config_path, tree);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update user directory
|
|
||||||
tree.put("user.directory", abs_path.string());
|
|
||||||
|
|
||||||
// Write back to config file
|
|
||||||
pt::write_ini(config_path, tree);
|
|
||||||
|
|
||||||
// Update in-memory value
|
|
||||||
user_directory = abs_path.string();
|
|
||||||
|
|
||||||
std::cout << "User directory initialized to: " << abs_path.string() << std::endl;
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
throw std::runtime_error("Failed to update config: " + std::string(e.what()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace dropshell
|
|
@ -1,11 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace dropshell {
|
|
||||||
|
|
||||||
// User directory initialization function
|
|
||||||
void init_user_directory(const std::string& path);
|
|
||||||
bool get_user_directory(std::string& path);
|
|
||||||
void set_user_directory(const std::string& path);
|
|
||||||
} // namespace dropshell
|
|
97
src/main.cpp
97
src/main.cpp
@ -1,7 +1,9 @@
|
|||||||
#include "dropshell.hpp"
|
#include "main.hpp"
|
||||||
#include "server_service.hpp"
|
#include "config.hpp"
|
||||||
#include "autocomplete.hpp"
|
#include "service_runner.hpp"
|
||||||
#include "init_user_directory.hpp"
|
#include "services.hpp"
|
||||||
|
#include "servers.hpp"
|
||||||
|
#include "utils/directories.hpp"
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@ -28,28 +30,20 @@ void print_help() {
|
|||||||
std::cout << " backup SERVER [SERVICE] Backup service(s)." << std::endl;
|
std::cout << " backup SERVER [SERVICE] Backup service(s)." << std::endl;
|
||||||
std::cout << " COMMAND SERVER [SERVICE] Run a custom command on service(s)." << std::endl;
|
std::cout << " COMMAND SERVER [SERVICE] Run a custom command on service(s)." << std::endl;
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
std::cout << "Examples:" << std::endl;
|
|
||||||
std::cout << " dropshell servers" << std::endl;
|
|
||||||
std::cout << " dropshell servers myserver" << std::endl;
|
|
||||||
std::cout << " dropshell init /path/to/directory" << std::endl;
|
|
||||||
std::cout << " dropshell templates" << std::endl;
|
|
||||||
std::cout << " dropshell install myserver myservice" << std::endl;
|
|
||||||
std::cout << " dropshell run myserver myservice status" << std::endl;
|
|
||||||
std::cout << " dropshell backup myserver myservice" << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace dropshell
|
} // namespace dropshell
|
||||||
|
|
||||||
|
|
||||||
bool parseargs(std::string arg2, std::string arg3, std::string & server_name, std::vector<std::string>& servicelist)
|
bool parseargs(std::string arg2, std::string arg3, std::string & server_name, std::vector<dropshell::ServiceInfo>& servicelist)
|
||||||
{
|
{
|
||||||
if (arg2.empty()) return false;
|
if (arg2.empty()) return false;
|
||||||
server_name = arg2;
|
server_name = arg2;
|
||||||
|
|
||||||
if (arg3.empty()) {
|
if (arg3.empty()) {
|
||||||
servicelist = dropshell::get_server_services(server_name);
|
servicelist = dropshell::get_server_services_info(server_name);
|
||||||
} else {
|
} else {
|
||||||
servicelist.push_back(arg3);
|
servicelist.push_back(dropshell::get_service_info(server_name, arg3));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -63,6 +57,7 @@ std::string safearg(int argc, char *argv[], int index)
|
|||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
try {
|
try {
|
||||||
|
dropshell::config *cfg = dropshell::get_global_config();
|
||||||
|
|
||||||
// Handle commands
|
// Handle commands
|
||||||
std::string cmd;
|
std::string cmd;
|
||||||
@ -77,7 +72,7 @@ int main(int argc, char* argv[]) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
dropshell::init_user_directory(argv[2]);
|
cfg->init_local_config_directory(argv[2]);
|
||||||
return 0;
|
return 0;
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
std::cerr << "Error: " << e.what() << std::endl;
|
std::cerr << "Error: " << e.what() << std::endl;
|
||||||
@ -96,27 +91,27 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// silently attempt to load the config file.
|
// silently attempt to load the config file.
|
||||||
dropshell::load_config();
|
cfg->load_config();
|
||||||
|
|
||||||
|
// auto completion stuff.
|
||||||
|
std::set<std::string> commands;
|
||||||
|
std::vector<dropshell::ServerInfo> servers = dropshell::get_configured_servers();
|
||||||
|
for (const auto& server : servers)
|
||||||
|
{
|
||||||
|
std::vector<dropshell::ServiceInfo> services = dropshell::get_server_services_info(server.name);
|
||||||
|
for (const auto& service : services)
|
||||||
|
commands.merge(dropshell::get_used_commands(server.name, service.service_name));
|
||||||
|
}
|
||||||
|
|
||||||
// auto compeltion stuff.
|
|
||||||
auto commands = dropshell::autocomplete_list_commands();
|
|
||||||
if (cmd == "autocomplete_list_commands") {
|
if (cmd == "autocomplete_list_commands") {
|
||||||
// add in standard commands.
|
commands.merge(std::set<std::string>{
|
||||||
commands.insert(commands.end(), {
|
"help","version","init"
|
||||||
"help",
|
|
||||||
"version",
|
|
||||||
"init"
|
|
||||||
});
|
});
|
||||||
if (dropshell::is_config_loaded()) { // these only work if the config is loaded.
|
if (cfg->is_config_set())
|
||||||
commands.insert(commands.end(), {
|
commands.merge(std::set<std::string>{
|
||||||
"servers",
|
"servers","templates","install","backup"
|
||||||
"templates",
|
|
||||||
"install",
|
|
||||||
"backup"
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& command : commands) {
|
for (const auto& command : commands) {
|
||||||
std::cout << command << std::endl;
|
std::cout << command << std::endl;
|
||||||
}
|
}
|
||||||
@ -124,11 +119,11 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cmd == "autocomplete_list_servers") {
|
if (cmd == "autocomplete_list_servers") {
|
||||||
if (dropshell::is_config_loaded())
|
if (cfg->is_config_set())
|
||||||
{
|
{
|
||||||
auto servers = dropshell::autocomplete_list_servers();
|
auto servers = dropshell::get_configured_servers();
|
||||||
for (const auto& server : servers)
|
for (const auto& server : servers)
|
||||||
std::cout << server << std::endl;
|
std::cout << server.name << std::endl;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -138,17 +133,17 @@ int main(int argc, char* argv[]) {
|
|||||||
std::cerr << "Error: autocomplete_list_services requires a server name" << std::endl;
|
std::cerr << "Error: autocomplete_list_services requires a server name" << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (dropshell::is_config_loaded()) {
|
if (cfg->is_config_set()) {
|
||||||
auto services = dropshell::autocomplete_list_services(argv[2]);
|
auto services = dropshell::get_server_services_info(argv[2]);
|
||||||
for (const auto& service : services)
|
for (const auto& service : services)
|
||||||
std::cout << service << std::endl;
|
std::cout << service.service_name << std::endl;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
// from here we require the config file to be loaded.
|
// from here we require the config file to be loaded.
|
||||||
if (!dropshell::is_config_loaded()) {
|
if (!cfg->is_config_set()) {
|
||||||
std::cerr << "Error: Failed to load configuration." << std::endl << "Please run 'dropshell init <path>' to initialise the user directory and create a configuration file." << std::endl;
|
std::cerr << "Error: Failed to load configuration." << std::endl << "Please run 'dropshell init <path>' to initialise the user directory and create a configuration file." << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -177,14 +172,14 @@ int main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
if (cmd == "install") {
|
if (cmd == "install") {
|
||||||
std::string server_name;
|
std::string server_name;
|
||||||
std::vector<std::string> servicelist;
|
std::vector<dropshell::ServiceInfo> servicelist;
|
||||||
if (!parseargs(safearg(argc, argv, 2), safearg(argc, argv, 3), server_name, servicelist)) {
|
if (!parseargs(safearg(argc, argv, 2), safearg(argc, argv, 3), server_name, servicelist)) {
|
||||||
std::cerr << "Error: install command requires server name and optionally service name" << std::endl;
|
std::cerr << "Error: install command requires server name and optionally service name" << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
for (const auto& service_name : servicelist) {
|
for (const auto& service_info : servicelist) {
|
||||||
dropshell::server_service service;
|
dropshell::service_runner service;
|
||||||
if (!service.init(server_name, service_name)) {
|
if (!service.init(server_name, service_info.service_name)) {
|
||||||
std::cerr << "Error: Failed to initialize service" << std::endl;
|
std::cerr << "Error: Failed to initialize service" << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -199,15 +194,15 @@ int main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
if (cmd == "backup") {
|
if (cmd == "backup") {
|
||||||
std::string server_name;
|
std::string server_name;
|
||||||
std::vector<std::string> servicelist;
|
std::vector<dropshell::ServiceInfo> servicelist;
|
||||||
if (!parseargs(safearg(argc, argv, 2), safearg(argc, argv, 3), server_name, servicelist)) {
|
if (!parseargs(safearg(argc, argv, 2), safearg(argc, argv, 3), server_name, servicelist)) {
|
||||||
std::cerr << "Error: backup command requires server name and optionally service name" << std::endl;
|
std::cerr << "Error: backup command requires server name and optionally service name" << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& service_name : servicelist) {
|
for (const auto& service_info : servicelist) {
|
||||||
dropshell::server_service service;
|
dropshell::service_runner service;
|
||||||
if (!service.init(server_name, service_name)) {
|
if (!service.init(server_name, service_info.service_name)) {
|
||||||
std::cerr << "Error: Failed to initialize service" << std::endl;
|
std::cerr << "Error: Failed to initialize service" << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -224,15 +219,15 @@ int main(int argc, char* argv[]) {
|
|||||||
for (const auto& command : commands) {
|
for (const auto& command : commands) {
|
||||||
if (cmd == command) {
|
if (cmd == command) {
|
||||||
std::string server_name;
|
std::string server_name;
|
||||||
std::vector<std::string> servicelist;
|
std::vector<dropshell::ServiceInfo> servicelist;
|
||||||
if (!parseargs(safearg(argc, argv, 2), safearg(argc, argv, 3), server_name, servicelist)) {
|
if (!parseargs(safearg(argc, argv, 2), safearg(argc, argv, 3), server_name, servicelist)) {
|
||||||
std::cerr << "Error: " << command << " command requires server name and optionally service name" << std::endl;
|
std::cerr << "Error: " << command << " command requires server name and optionally service name" << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& service_name : servicelist) {
|
for (const auto& service_info : servicelist) {
|
||||||
dropshell::server_service service;
|
dropshell::service_runner service;
|
||||||
if (!service.init(server_name, service_name)) {
|
if (!service.init(server_name, service_info.service_name)) {
|
||||||
std::cerr << "Error: Failed to initialize service" << std::endl;
|
std::cerr << "Error: Failed to initialize service" << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -13,13 +13,6 @@ const std::string RELEASE_DATE = "2025-04-21";
|
|||||||
const std::string AUTHOR = "j842";
|
const std::string AUTHOR = "j842";
|
||||||
const std::string LICENSE = "MIT";
|
const std::string LICENSE = "MIT";
|
||||||
|
|
||||||
// Server information structure
|
|
||||||
struct ServerInfo {
|
|
||||||
std::string name;
|
|
||||||
std::string ssh_host;
|
|
||||||
std::string ssh_user;
|
|
||||||
std::string ssh_port;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Command handlers
|
// Command handlers
|
||||||
void print_help(const boost::program_options::options_description& desc);
|
void print_help(const boost::program_options::options_description& desc);
|
||||||
@ -30,10 +23,4 @@ void list_templates();
|
|||||||
void show_server_details(const std::string& server_name);
|
void show_server_details(const std::string& server_name);
|
||||||
void interactive_mode();
|
void interactive_mode();
|
||||||
|
|
||||||
// Utility functions
|
|
||||||
std::vector<ServerInfo> get_configured_servers();
|
|
||||||
std::vector<std::string> autocomplete_list_servers();
|
|
||||||
std::vector<std::string> autocomplete_list_services(const std::string& server_name);
|
|
||||||
std::vector<std::string> autocomplete_list_commands();
|
|
||||||
|
|
||||||
} // namespace dropshell
|
} // namespace dropshell
|
@ -1,121 +1,56 @@
|
|||||||
#include "server_env.hpp"
|
#include "server_env.hpp"
|
||||||
#include <boost/property_tree/ptree.hpp>
|
#include "utils/envmanager.hpp"
|
||||||
#include <boost/property_tree/ini_parser.hpp>
|
#include "utils/directories.hpp"
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <cstdlib>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace dropshell {
|
namespace dropshell {
|
||||||
|
|
||||||
// Helper function to trim whitespace from both ends of a string
|
bool server_env::is_valid() const {
|
||||||
static std::string trim(const std::string& str) {
|
|
||||||
const std::string whitespace = " \t";
|
|
||||||
const auto strBegin = str.find_first_not_of(whitespace);
|
|
||||||
if (strBegin == std::string::npos) {
|
|
||||||
return ""; // empty string
|
|
||||||
}
|
|
||||||
const auto strEnd = str.find_last_not_of(whitespace);
|
|
||||||
const auto strRange = strEnd - strBegin + 1;
|
|
||||||
return str.substr(strBegin, strRange);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to print the contents of a file to screen
|
|
||||||
static void print_file(const std::string& path) {
|
|
||||||
std::cout << "Contents of " << path << ":" << std::endl;
|
|
||||||
std::ifstream file(path);
|
|
||||||
std::string line;
|
|
||||||
while (std::getline(file, line)) {
|
|
||||||
std::cout << " " << line << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool server_env::is_valid() {
|
|
||||||
return mValid;
|
return mValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
server_env::server_env(const std::string& path) : mValid(false) {
|
server_env::server_env(const std::string& server_name) : mValid(false) {
|
||||||
// Construct the full path to _server.env
|
// Construct the full path to server.env
|
||||||
boost::filesystem::path env_path = boost::filesystem::path(path) / "_server.env";
|
std::string env_path = get_local_server_env_path(server_name);
|
||||||
|
|
||||||
// Check if file exists
|
// Check if file exists
|
||||||
if (!boost::filesystem::exists(env_path)) {
|
if (!boost::filesystem::exists(env_path)) {
|
||||||
throw std::runtime_error("Server environment file not found: " + env_path.string());
|
throw std::runtime_error("Server environment file not found: " + env_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Read the environment file
|
// Use envmanager to handle the environment file
|
||||||
std::ifstream file(env_path.string());
|
m_env_manager = std::unique_ptr<envmanager>(new envmanager(env_path));
|
||||||
std::string line;
|
m_env_manager->load();
|
||||||
|
|
||||||
while (std::getline(file, line)) {
|
// Get all variables
|
||||||
// Skip empty lines and comments
|
m_env_manager->get_all_variables_substituted(variables);
|
||||||
if (line.empty() || line[0] == '#') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the position of the equals sign
|
|
||||||
size_t pos = line.find('=');
|
|
||||||
if (pos != std::string::npos) {
|
|
||||||
std::string key = line.substr(0, pos);
|
|
||||||
std::string value = line.substr(pos + 1);
|
|
||||||
|
|
||||||
// Trim whitespace using the helper function
|
|
||||||
key = trim(key);
|
|
||||||
value = trim(value);
|
|
||||||
|
|
||||||
// Handle ${USER} replacement
|
|
||||||
size_t user_pos;
|
|
||||||
while ((user_pos = value.find("${USER}")) != std::string::npos) {
|
|
||||||
const char* user = std::getenv("USER");
|
|
||||||
if (user) {
|
|
||||||
value.replace(user_pos, 7, user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
variables[key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify required variables exist
|
// Verify required variables exist
|
||||||
for (const auto& var : {"SSH_HOST", "SSH_USER", "SSH_PORT", "DROPSHELL_DIR"}) {
|
for (const auto& var : {"SSH_HOST", "SSH_USER", "SSH_PORT", "DROPSHELL_DIR"}) {
|
||||||
if (variables.find(var) == variables.end()) {
|
if (variables.find(var) == variables.end()) {
|
||||||
// print the contents of the _server.env file to screen:
|
// Print the variables identified in the file
|
||||||
print_file(env_path.string());
|
|
||||||
// print variables identified in the file:
|
|
||||||
std::cout << "Variables identified in the file:" << std::endl;
|
std::cout << "Variables identified in the file:" << std::endl;
|
||||||
for (const auto& var : variables) {
|
for (const auto& v : variables) {
|
||||||
std::cout << " " << var.first << std::endl;
|
std::cout << " " << v.first << std::endl;
|
||||||
}
|
}
|
||||||
throw std::runtime_error("Missing required variable: " + std::string(var));
|
throw std::runtime_error("Missing required variable: " + std::string(var));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mValid = true;
|
mValid = true;
|
||||||
|
|
||||||
} catch (const boost::property_tree::ini_parser_error& e) {
|
} catch (const std::exception& e) {
|
||||||
throw std::runtime_error("Failed to parse server environment file: " + std::string(e.what()));
|
throw std::runtime_error("Failed to parse server environment file: " + std::string(e.what()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string server_env::get_variable(const std::string& name) {
|
std::string server_env::get_variable(const std::string& name) const {
|
||||||
auto it = variables.find(name);
|
if (!m_env_manager) {
|
||||||
if (it == variables.end()) {
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
return m_env_manager->get_variable_substituted(name);
|
||||||
std::string value = it->second;
|
|
||||||
|
|
||||||
// Replace ${USER} with actual username
|
|
||||||
const char* username = std::getenv("USER");
|
|
||||||
if (username) {
|
|
||||||
std::string user_var = "${USER}";
|
|
||||||
size_t pos = value.find(user_var);
|
|
||||||
if (pos != std::string::npos) {
|
|
||||||
value.replace(pos, user_var.length(), username);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::map<std::string, std::string> &server_env::get_variables() const
|
const std::map<std::string, std::string> &server_env::get_variables() const
|
||||||
@ -123,19 +58,19 @@ const std::map<std::string, std::string> &server_env::get_variables() const
|
|||||||
return variables;
|
return variables;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string server_env::get_SSH_HOST() {
|
std::string server_env::get_SSH_HOST() const {
|
||||||
return get_variable("SSH_HOST");
|
return get_variable("SSH_HOST");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string server_env::get_SSH_USER() {
|
std::string server_env::get_SSH_USER() const {
|
||||||
return get_variable("SSH_USER");
|
return get_variable("SSH_USER");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string server_env::get_SSH_PORT() {
|
std::string server_env::get_SSH_PORT() const {
|
||||||
return get_variable("SSH_PORT");
|
return get_variable("SSH_PORT");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string server_env::get_DROPSHELL_DIR() {
|
std::string server_env::get_DROPSHELL_DIR() const {
|
||||||
return get_variable("DROPSHELL_DIR");
|
return get_variable("DROPSHELL_DIR");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
// server_env.hpp
|
// server_env.hpp
|
||||||
//
|
//
|
||||||
// read the _server.env file and provide a class to access the variables
|
// read the server.env file and provide a class to access the variables
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef __SERVER_ENV_HPP
|
#ifndef __SERVER_ENV_HPP
|
||||||
#define __SERVER_ENV_HPP
|
#define __SERVER_ENV_HPP
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include "utils/envmanager.hpp"
|
||||||
namespace dropshell {
|
namespace dropshell {
|
||||||
|
|
||||||
// reads path / _server.env and provides a class to access the variables.
|
// reads path / server.env and provides a class to access the variables.
|
||||||
// each env file is required to have the following variables:
|
// each env file is required to have the following variables:
|
||||||
// SSH_HOST
|
// SSH_HOST
|
||||||
// SSH_USER
|
// SSH_USER
|
||||||
@ -20,21 +20,22 @@ namespace dropshell {
|
|||||||
// ${USER} -> the username of the user running dropshell
|
// ${USER} -> the username of the user running dropshell
|
||||||
class server_env {
|
class server_env {
|
||||||
public:
|
public:
|
||||||
server_env(const std::string& path);
|
server_env(const std::string& server_name);
|
||||||
std::string get_variable(const std::string& name);
|
std::string get_variable(const std::string& name) const;
|
||||||
const std::map<std::string, std::string>& get_variables() const;
|
const std::map<std::string, std::string>& get_variables() const;
|
||||||
|
|
||||||
std::string get_SSH_HOST();
|
std::string get_SSH_HOST() const;
|
||||||
std::string get_SSH_USER();
|
std::string get_SSH_USER() const;
|
||||||
std::string get_SSH_PORT();
|
std::string get_SSH_PORT() const;
|
||||||
|
|
||||||
std::string get_DROPSHELL_DIR();
|
std::string get_DROPSHELL_DIR() const;
|
||||||
|
|
||||||
bool is_valid();
|
bool is_valid() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::map<std::string, std::string> variables;
|
std::map<std::string, std::string> variables;
|
||||||
bool mValid;
|
bool mValid;
|
||||||
|
std::unique_ptr<envmanager> m_env_manager;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dropshell
|
} // namespace dropshell
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
#include "init_user_directory.hpp"
|
#include "servers.hpp"
|
||||||
#include "dropshell.hpp"
|
|
||||||
#include "server_env.hpp"
|
#include "server_env.hpp"
|
||||||
#include "server_service.hpp"
|
#include "service_runner.hpp"
|
||||||
#include "tableprint.hpp"
|
#include "tableprint.hpp"
|
||||||
#include "interactive/interactive.hpp"
|
#include "interactive/interactive.hpp"
|
||||||
|
#include "utils/envmanager.hpp"
|
||||||
|
#include "utils/directories.hpp"
|
||||||
|
#include "services.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
@ -16,32 +18,29 @@ namespace dropshell {
|
|||||||
|
|
||||||
std::vector<ServerInfo> get_configured_servers() {
|
std::vector<ServerInfo> get_configured_servers() {
|
||||||
std::vector<ServerInfo> servers;
|
std::vector<ServerInfo> servers;
|
||||||
std::string user_dir;
|
|
||||||
|
|
||||||
if (!is_config_loaded()) {
|
std::string servers_dir = get_local_config_servers_path();
|
||||||
std::cerr << "Error: Config not loaded" << std::endl;
|
if (servers_dir.empty()) {
|
||||||
return servers;
|
|
||||||
}
|
|
||||||
if (!get_user_directory(user_dir)) {
|
|
||||||
std::cerr << "Error: User directory not set" << std::endl;
|
|
||||||
return servers;
|
|
||||||
}
|
|
||||||
fs::path servers_dir = fs::path(user_dir) / "servers";
|
|
||||||
if (!fs::exists(servers_dir)) {
|
|
||||||
std::cerr << "Error: Servers directory not found" << std::endl;
|
std::cerr << "Error: Servers directory not found" << std::endl;
|
||||||
return servers;
|
return servers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!fs::exists(servers_dir)) {
|
||||||
|
std::cerr << "Error: Servers directory not found:" << servers_dir << std::endl;
|
||||||
|
return servers;
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto& entry : fs::directory_iterator(servers_dir)) {
|
for (const auto& entry : fs::directory_iterator(servers_dir)) {
|
||||||
if (fs::is_directory(entry)) {
|
if (fs::is_directory(entry)) {
|
||||||
fs::path env_file = entry.path();
|
std::string server_name = entry.path().filename().string();
|
||||||
server_env env(env_file.string());
|
|
||||||
|
server_env env(server_name);
|
||||||
if (!env.is_valid()) {
|
if (!env.is_valid()) {
|
||||||
std::cerr << "Error: Invalid server environment file: " << env_file.string() << std::endl;
|
std::cerr << "Error: Invalid server environment file: " << server_name << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
servers.push_back({
|
servers.push_back({
|
||||||
entry.path().filename().string(),
|
server_name,
|
||||||
env.get_SSH_HOST(),
|
env.get_SSH_HOST(),
|
||||||
env.get_SSH_USER(),
|
env.get_SSH_USER(),
|
||||||
env.get_SSH_PORT()
|
env.get_SSH_PORT()
|
||||||
@ -91,10 +90,10 @@ void list_servers() {
|
|||||||
for (const auto& server : servers) {
|
for (const auto& server : servers) {
|
||||||
std::vector<int> ports_used;
|
std::vector<int> ports_used;
|
||||||
std::string serviceticks = "";
|
std::string serviceticks = "";
|
||||||
std::vector<std::string> services = get_server_services(server.name);
|
std::vector<ServiceInfo> services = get_server_services_info(server.name);
|
||||||
for (const auto& service : services) {
|
for (const auto& service : services) {
|
||||||
server_service ss;
|
service_runner ss;
|
||||||
if (ss.init(server.name, service))
|
if (ss.init(server.name, service.service_name))
|
||||||
{
|
{
|
||||||
if (ss.is_healthy())
|
if (ss.is_healthy())
|
||||||
serviceticks += ":tick: ";
|
serviceticks += ":tick: ";
|
||||||
@ -120,21 +119,12 @@ void list_servers() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void show_server_details(const std::string& server_name) {
|
void show_server_details(const std::string& server_name) {
|
||||||
std::string user_dir;
|
server_env env(server_name);
|
||||||
if (!get_user_directory(user_dir)) {
|
if (!env.is_valid()) {
|
||||||
std::cerr << "Error: User directory not set" << std::endl;
|
std::cerr << "Error: Invalid server environment file: " << server_name << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::path server_dir = fs::path(user_dir) / "servers" / server_name;
|
|
||||||
if (!fs::exists(server_dir)) {
|
|
||||||
std::cerr << "Error: Server '" << server_name << "' not found" << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
server_env env(server_dir.string());
|
|
||||||
|
|
||||||
|
|
||||||
//---------------------
|
//---------------------
|
||||||
// Check if server is reachable via SSH
|
// Check if server is reachable via SSH
|
||||||
std::string ssh_address = env.get_SSH_HOST();
|
std::string ssh_address = env.get_SSH_HOST();
|
||||||
@ -176,12 +166,12 @@ void show_server_details(const std::string& server_name) {
|
|||||||
tableprint tp("Services: " + server_name, false);
|
tableprint tp("Services: " + server_name, false);
|
||||||
tp.add_row({"Status", "Service", "Ports"});
|
tp.add_row({"Status", "Service", "Ports"});
|
||||||
|
|
||||||
std::vector<std::string> services = get_server_services(server_name);
|
std::vector<ServiceInfo> services = get_server_services_info(server_name);
|
||||||
for (const auto& service : services) {
|
for (const auto& service : services) {
|
||||||
bool healthy = false;
|
bool healthy = false;
|
||||||
std::vector<int> ports;
|
std::vector<int> ports;
|
||||||
server_service ss;
|
service_runner ss;
|
||||||
if (ss.init(server_name, service))
|
if (ss.init(server_name, service.service_name))
|
||||||
{
|
{
|
||||||
if (ss.is_healthy())
|
if (ss.is_healthy())
|
||||||
healthy=true;
|
healthy=true;
|
||||||
@ -196,7 +186,7 @@ void show_server_details(const std::string& server_name) {
|
|||||||
ports_str += std::to_string(port);
|
ports_str += std::to_string(port);
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
tp.add_row({healthy ? ":tick:" : ":cross:", service, ports_str});
|
tp.add_row({healthy ? ":tick:" : ":cross:", service.service_name, ports_str});
|
||||||
} // end of for (const auto& service : services)
|
} // end of for (const auto& service : services)
|
||||||
tp.print();
|
tp.print();
|
||||||
} // end of list services
|
} // end of list services
|
||||||
|
22
src/servers.hpp
Normal file
22
src/servers.hpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#ifndef SERVERS_HPP
|
||||||
|
#define SERVERS_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace dropshell {
|
||||||
|
|
||||||
|
// Server information structure
|
||||||
|
struct ServerInfo {
|
||||||
|
std::string name;
|
||||||
|
std::string ssh_host;
|
||||||
|
std::string ssh_user;
|
||||||
|
std::string ssh_port;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<ServerInfo> get_configured_servers();
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace dropshell
|
||||||
|
|
||||||
|
#endif // SERVERS_HPP
|
@ -1,9 +1,10 @@
|
|||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
#include "init_user_directory.hpp"
|
#include "service_runner.hpp"
|
||||||
#include "server_service.hpp"
|
|
||||||
#include "server_env.hpp"
|
#include "server_env.hpp"
|
||||||
#include "templates.hpp"
|
#include "templates.hpp"
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
|
#include "services.hpp"
|
||||||
|
#include "utils/directories.hpp"
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@ -16,57 +17,13 @@ namespace fs = boost::filesystem;
|
|||||||
|
|
||||||
namespace dropshell {
|
namespace dropshell {
|
||||||
|
|
||||||
std::vector<std::string> get_server_services(const std::string& server_name) {
|
|
||||||
std::vector<std::string> services;
|
|
||||||
std::string user_dir;
|
|
||||||
|
|
||||||
if (!get_user_directory(user_dir)) {
|
|
||||||
std::cerr << "Error: User directory not set" << std::endl;
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
fs::path server_dir = fs::path(user_dir) / "servers" / server_name;
|
|
||||||
if (!fs::exists(server_dir)) {
|
|
||||||
std::cerr << "Error: Server directory not found" << std::endl;
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for .env files in the server directory
|
|
||||||
for (const auto& entry : fs::directory_iterator(server_dir)) {
|
|
||||||
if (entry.path().extension() == ".env" && entry.path().filename().string() != "_server.env") {
|
|
||||||
services.push_back(entry.path().stem().string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
server_service::server_service() : m_server_name(""), m_service_name(""), m_server_env(nullptr) {}
|
service_runner::service_runner() : m_server_name(""), m_server_env(nullptr) {}
|
||||||
|
|
||||||
bool server_service::init(const std::string& server_name, const std::string& service_name) {
|
bool service_runner::init(const std::string& server_name, const std::string& service_name) {
|
||||||
std::string user_dir;
|
|
||||||
if (!get_user_directory(user_dir)) {
|
|
||||||
std::cerr << "Error: User directory not set" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if server exists
|
|
||||||
fs::path server_dir = fs::path(user_dir) / "servers" / server_name;
|
|
||||||
if (!fs::exists(server_dir)) {
|
|
||||||
std::cerr << "Error: Server '" << server_name << "' not found" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if service env file exists
|
|
||||||
fs::path service_env = server_dir / (service_name + ".env");
|
|
||||||
if (!fs::exists(service_env)) {
|
|
||||||
std::cerr << "Error: Service environment file not found: " << service_env.string() << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize server environment
|
// Initialize server environment
|
||||||
try {
|
try {
|
||||||
m_server_env = std::make_unique<server_env>(server_dir.string());
|
m_server_env = std::make_unique<server_env>(server_name);
|
||||||
if (!m_server_env->is_valid()) {
|
if (!m_server_env->is_valid()) {
|
||||||
std::cerr << "Error: Invalid server environment" << std::endl;
|
std::cerr << "Error: Invalid server environment" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
@ -75,55 +32,49 @@ bool server_service::init(const std::string& server_name, const std::string& ser
|
|||||||
std::cerr << "Error: Failed to initialize server environment: " << e.what() << std::endl;
|
std::cerr << "Error: Failed to initialize server environment: " << e.what() << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mRemote_service_path = get_remote_service_path(m_server_name, m_service_info.service_name);
|
||||||
|
mRemote_service_config_path = get_remote_service_config_path(m_server_name, m_service_info.service_name);
|
||||||
|
mRemote_service_template_path = get_remote_service_template_path(m_server_name, m_service_info.service_name);
|
||||||
|
mRemote_service_env_file = get_remote_service_env_file(m_server_name, m_service_info.service_name);
|
||||||
|
|
||||||
|
|
||||||
m_server_name = server_name;
|
m_server_name = server_name;
|
||||||
m_service_name = service_name;
|
m_service_info = get_service_info(server_name, service_name);
|
||||||
return true;
|
return !m_service_info.path.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper method implementations
|
// Helper method implementations
|
||||||
std::string server_service::construct_ssh_cmd() const {
|
std::string service_runner::construct_ssh_cmd() const {
|
||||||
std::stringstream ssh_cmd;
|
std::stringstream ssh_cmd;
|
||||||
ssh_cmd << "ssh -p " << m_server_env->get_SSH_PORT() << " "
|
ssh_cmd << "ssh -p " << m_server_env->get_SSH_PORT() << " "
|
||||||
<< m_server_env->get_SSH_USER() << "@" << m_server_env->get_SSH_HOST() << " ";
|
<< m_server_env->get_SSH_USER() << "@" << m_server_env->get_SSH_HOST() << " ";
|
||||||
return ssh_cmd.str();
|
return ssh_cmd.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string server_service::get_service_dir() const {
|
bool service_runner::check_remote_dir_exists(const std::string& ssh_cmd, const std::string& dir_path) const {
|
||||||
return m_server_env->get_DROPSHELL_DIR() + "/" + m_service_name;
|
std::string check_dir_cmd = ssh_cmd + "'test -d " + dir_path + "'";
|
||||||
}
|
|
||||||
|
|
||||||
std::string server_service::get_env_path() const {
|
|
||||||
return get_service_dir() + "/" + m_service_name + ".env";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string server_service::get_script_dir() const {
|
|
||||||
return get_service_dir() + "/template";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool server_service::check_service_dir_exists(const std::string& ssh_cmd) const {
|
|
||||||
std::string check_dir_cmd = ssh_cmd + "'test -d " + get_service_dir() + "'";
|
|
||||||
if (system(check_dir_cmd.c_str()) != 0) {
|
if (system(check_dir_cmd.c_str()) != 0) {
|
||||||
std::cerr << "Error: Service directory not found on server - has it been installed?" << std::endl;
|
std::cerr << "Error: Directory not found on remote server:" << dir_path << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool server_service::check_remote_file_exists(const std::string& ssh_cmd, const std::string& file_path) const {
|
bool service_runner::check_remote_file_exists(const std::string& ssh_cmd, const std::string& file_path) const {
|
||||||
std::string check_cmd = ssh_cmd + "'test -f " + file_path + "'";
|
std::string check_cmd = ssh_cmd + "'test -f " + file_path + "'";
|
||||||
if (system(check_cmd.c_str()) != 0) {
|
if (system(check_cmd.c_str()) != 0) {
|
||||||
std::cerr << "Error: File not found: " << file_path << std::endl;
|
std::cerr << "Error: File not found on remote server: " << file_path << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool server_service::execute_ssh_command(const std::string& command, const std::string& error_msg) const {
|
bool service_runner::execute_ssh_command(const std::string& command, const std::string& error_msg) const {
|
||||||
std::string full_cmd = construct_ssh_cmd() + command;
|
std::string full_cmd = construct_ssh_cmd() + command;
|
||||||
return execute_local_command(full_cmd, error_msg);
|
return execute_local_command(full_cmd, error_msg);
|
||||||
}
|
}
|
||||||
bool server_service::execute_local_command(const std::string& command, const std::string& error_msg) const {
|
bool service_runner::execute_local_command(const std::string& command, const std::string& error_msg) const {
|
||||||
bool okay = (system(command.c_str()) == 0);
|
bool okay = (system(command.c_str()) == 0);
|
||||||
|
|
||||||
if (!okay && !error_msg.empty())
|
if (!okay && !error_msg.empty())
|
||||||
@ -131,14 +82,14 @@ bool server_service::execute_local_command(const std::string& command, const std
|
|||||||
return okay;
|
return okay;
|
||||||
}
|
}
|
||||||
|
|
||||||
void server_service::maketitle(const std::string& title) const {
|
void service_runner::maketitle(const std::string& title) const {
|
||||||
std::cout << std::string(title.length() + 4, '-') << std::endl;
|
std::cout << std::string(title.length() + 4, '-') << std::endl;
|
||||||
std::cout << "| " << title << " |" << std::endl;
|
std::cout << "| " << title << " |" << std::endl;
|
||||||
std::cout << std::string(title.length() + 4, '-') << std::endl;
|
std::cout << std::string(title.length() + 4, '-') << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool server_service::install() {
|
bool service_runner::install() {
|
||||||
maketitle("Installing " + m_service_name + " on " + m_server_name);
|
maketitle("Installing " + m_service_info.service_name + " (" + m_service_info.template_name + ") on " + m_server_name);
|
||||||
if (!m_server_env) {
|
if (!m_server_env) {
|
||||||
std::cerr << "Error: Server service not initialized" << std::endl;
|
std::cerr << "Error: Server service not initialized" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
@ -146,16 +97,14 @@ bool server_service::install() {
|
|||||||
|
|
||||||
// Check if template exists
|
// Check if template exists
|
||||||
template_manager tm;
|
template_manager tm;
|
||||||
template_info info;
|
template_info tinfo;
|
||||||
if (!tm.get_template_info(m_service_name, info)) {
|
if (!tm.get_template_info(m_service_info.template_name, tinfo)) {
|
||||||
std::cerr << "Error: Template '" << m_service_name << "' not found" << std::endl;
|
std::cerr << "Error: Template '" << m_service_info.template_name << "' not found" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string script_dir = get_script_dir();
|
|
||||||
|
|
||||||
// Create service directory
|
// Create service directory
|
||||||
std::string mkdir_cmd = "'mkdir -p " + script_dir + "'";
|
std::string mkdir_cmd = "'mkdir -p " + mRemote_service_path + "'";
|
||||||
if (!execute_ssh_command(mkdir_cmd, "Failed to create service directory")) {
|
if (!execute_ssh_command(mkdir_cmd, "Failed to create service directory")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -167,52 +116,55 @@ bool server_service::install() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Copy template files
|
// Copy template files
|
||||||
std::cout << "Copying template files from " << info.path << " to " << script_dir << "/" << std::endl;
|
{
|
||||||
std::string rsync_cmd = "rsync --delete -zrpc -e 'ssh -p " + m_server_env->get_SSH_PORT() + "' " +
|
std::cout << "Copying template files from " << tinfo.path << " to " << mRemote_service_template_path << "/" << std::endl;
|
||||||
info.path + "/ " +
|
std::string rsync_cmd = "rsync --delete -zrpc -e 'ssh -p " + m_server_env->get_SSH_PORT() + "' " +
|
||||||
m_server_env->get_SSH_USER() + "@" + m_server_env->get_SSH_HOST() + ":" +
|
tinfo.path + "/ " +
|
||||||
script_dir + "/";
|
m_server_env->get_SSH_USER() + "@" + m_server_env->get_SSH_HOST() + ":" +
|
||||||
execute_local_command(rsync_cmd,"Failed to copy template files");
|
mRemote_service_template_path + "/";
|
||||||
|
execute_local_command(rsync_cmd,"Failed to copy template files");
|
||||||
// Copy service env file
|
|
||||||
std::string user_dir;
|
|
||||||
if (!get_user_directory(user_dir)) {
|
|
||||||
std::cerr << "Error: User directory not set" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
fs::path service_env = fs::path(user_dir) / "servers" / m_server_name / (m_service_name + ".env");
|
|
||||||
std::string scp_cmd = "scp -P " + m_server_env->get_SSH_PORT() + " " +
|
|
||||||
service_env.string() + " " +
|
|
||||||
m_server_env->get_SSH_USER() + "@" + m_server_env->get_SSH_HOST() + ":" +
|
|
||||||
get_env_path();
|
|
||||||
if (!execute_ssh_command(scp_cmd, "Failed to copy service environment file")) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy service files (including service.env)
|
||||||
|
{
|
||||||
|
std::string local_service_path = get_local_service_path(m_server_name, m_service_info.service_name);
|
||||||
|
if (local_service_path.empty() || !fs::exists(local_service_path)) {
|
||||||
|
std::cerr << "Error: Service directory not found: " << local_service_path << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::cout << "Copying service files from " << local_service_path << " to " << mRemote_service_config_path << std::endl;
|
||||||
|
std::string rsync_cmd = "rsync --delete -zrpc -e 'ssh -p " + m_server_env->get_SSH_PORT() + "' " +
|
||||||
|
local_service_path + "/ " +
|
||||||
|
m_server_env->get_SSH_USER() + "@" + m_server_env->get_SSH_HOST() + ":" +
|
||||||
|
mRemote_service_config_path + "/";
|
||||||
|
execute_local_command(rsync_cmd,"Failed to copy service files");
|
||||||
|
}
|
||||||
|
|
||||||
// Run install script
|
// Run install script
|
||||||
std::string install_cmd = "'cd " + script_dir +
|
{
|
||||||
" && /bin/bash _install.sh " + get_env_path() + "'";
|
std::string install_cmd = "'cd " + mRemote_service_template_path +
|
||||||
bool ok= execute_ssh_command(install_cmd, "Failed to run install script");
|
" && /bin/bash _install.sh " + mRemote_service_env_file + "'";
|
||||||
if (!ok)
|
bool ok= execute_ssh_command(install_cmd, "Failed to run install script");
|
||||||
return false;
|
if (!ok)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// print health tick
|
// print health tick
|
||||||
std::cout << "Health: " << healthtick() << std::endl;
|
std::cout << "Health: " << healthtick() << std::endl;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool server_service::run_command(const std::string& command) {
|
bool service_runner::run_command(const std::string& command) {
|
||||||
if (!m_server_env) {
|
if (!m_server_env) {
|
||||||
std::cerr << "Error: Server service not initialized" << std::endl;
|
std::cerr << "Error: Server service not initialized" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ssh_cmd = construct_ssh_cmd();
|
std::string ssh_cmd = construct_ssh_cmd();
|
||||||
std::string script_dir = get_script_dir();
|
std::string script_path = mRemote_service_template_path + "/" + command + ".sh";
|
||||||
std::string script_path = script_dir + "/" + command + ".sh";
|
|
||||||
|
|
||||||
// Check if service directory exists
|
// Check if service directory exists
|
||||||
if (!check_service_dir_exists(ssh_cmd)) {
|
if (!check_remote_dir_exists(ssh_cmd, mRemote_service_path)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,18 +174,18 @@ bool server_service::run_command(const std::string& command) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if env file exists
|
// Check if env file exists
|
||||||
if (!check_remote_file_exists(ssh_cmd, get_env_path())) {
|
if (!check_remote_file_exists(ssh_cmd, mRemote_service_env_file)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the command
|
// Run the command
|
||||||
std::string run_cmd = "'cd " + script_dir +
|
std::string run_cmd = "'cd " + mRemote_service_template_path +
|
||||||
" && /bin/bash " + script_path + " " + get_env_path() + "'";
|
" && /bin/bash " + script_path + " " + mRemote_service_env_file + "'";
|
||||||
return execute_ssh_command(run_cmd, "Command returned error code: " + script_path);
|
return execute_ssh_command(run_cmd, "Command returned error code: " + script_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool server_service::backup() {
|
bool service_runner::backup() {
|
||||||
maketitle("Backing up " + m_service_name + " on " + m_server_name);
|
maketitle("Backing up " + m_service_info.service_name + " (" + m_service_info.template_name + ") on " + m_server_name);
|
||||||
|
|
||||||
if (!m_server_env) {
|
if (!m_server_env) {
|
||||||
std::cerr << "Error: Server service not initialized" << std::endl;
|
std::cerr << "Error: Server service not initialized" << std::endl;
|
||||||
@ -241,11 +193,10 @@ bool server_service::backup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string ssh_cmd = construct_ssh_cmd();
|
std::string ssh_cmd = construct_ssh_cmd();
|
||||||
std::string script_dir = get_script_dir();
|
std::string script_path = mRemote_service_template_path + "/_backup.sh";
|
||||||
std::string script_path = script_dir + "/_backup.sh";
|
|
||||||
|
|
||||||
// Check if basic installed stuff is in place.
|
// Check if basic installed stuff is in place.
|
||||||
if (!check_service_dir_exists(ssh_cmd) || !check_remote_file_exists(ssh_cmd, script_path) || !check_remote_file_exists(ssh_cmd, get_env_path()))
|
if (!check_remote_dir_exists(ssh_cmd, mRemote_service_path) || !check_remote_file_exists(ssh_cmd, script_path) || !check_remote_file_exists(ssh_cmd, mRemote_service_env_file))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Create backups directory on server if it doesn't exist
|
// Create backups directory on server if it doesn't exist
|
||||||
@ -256,12 +207,7 @@ bool server_service::backup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create backups directory locally if it doesn't exist
|
// Create backups directory locally if it doesn't exist
|
||||||
std::string user_dir;
|
std::string local_backups_dir = get_local_config_backups_path();
|
||||||
if (!get_user_directory(user_dir)) {
|
|
||||||
std::cerr << "Error: User directory not set" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
fs::path local_backups_dir = fs::path(user_dir) / "backups";
|
|
||||||
if (!fs::exists(local_backups_dir)) {
|
if (!fs::exists(local_backups_dir)) {
|
||||||
fs::create_directories(local_backups_dir);
|
fs::create_directories(local_backups_dir);
|
||||||
}
|
}
|
||||||
@ -273,13 +219,13 @@ bool server_service::backup() {
|
|||||||
datetime << std::put_time(std::localtime(&time), "%Y-%m-%d_%H-%M-%S");
|
datetime << std::put_time(std::localtime(&time), "%Y-%m-%d_%H-%M-%S");
|
||||||
|
|
||||||
// Construct backup filename
|
// Construct backup filename
|
||||||
std::string backup_filename = m_server_name + "-" + m_service_name + "-" + datetime.str() + ".tgz";
|
std::string backup_filename = m_server_name + "-" + m_service_info.service_name + "-" + datetime.str() + ".tgz";
|
||||||
std::string server_backup_path = server_backups_dir + "/" + backup_filename;
|
std::string server_backup_path = server_backups_dir + "/" + backup_filename;
|
||||||
std::string local_backup_path = (local_backups_dir / backup_filename).string();
|
std::string local_backup_path = (fs::path(local_backups_dir) / backup_filename).string();
|
||||||
|
|
||||||
// Run backup script
|
// Run backup script
|
||||||
std::string backup_cmd = "'cd " + script_dir +
|
std::string backup_cmd = "'cd " + mRemote_service_template_path +
|
||||||
" && /bin/bash \""+script_path+"\" " + get_env_path() + " " + server_backup_path + "'";
|
" && /bin/bash \""+script_path+"\" " + mRemote_service_env_file + " " + server_backup_path + "'";
|
||||||
if (!execute_ssh_command(backup_cmd, "Backup script failed")) {
|
if (!execute_ssh_command(backup_cmd, "Backup script failed")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -296,22 +242,18 @@ bool server_service::backup() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool server_service::is_healthy()
|
bool service_runner::is_healthy()
|
||||||
{
|
{
|
||||||
if (!m_server_env) {
|
if (!m_server_env) {
|
||||||
std::cerr << "Error: Server service not initialized" << std::endl;
|
std::cerr << "Error: Server service not initialized" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string service_dir = m_server_env->get_DROPSHELL_DIR() + "/" + m_service_name;
|
|
||||||
std::string script_dir = service_dir + "/template";
|
|
||||||
std::string env_path = service_dir + "/" + m_service_name + ".env";
|
|
||||||
|
|
||||||
// Run status script, does not display output.
|
// Run status script, does not display output.
|
||||||
return execute_ssh_command("'cd " + script_dir + " && /bin/bash _status.sh " + env_path + " > /dev/null 2>&1'","");
|
return execute_ssh_command("'cd " + mRemote_service_template_path + " && /bin/bash _status.sh " + mRemote_service_env_file + " > /dev/null 2>&1'","");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string server_service::healthtick()
|
std::string service_runner::healthtick()
|
||||||
{
|
{
|
||||||
std::string green_tick = "\033[32m✓\033[0m";
|
std::string green_tick = "\033[32m✓\033[0m";
|
||||||
std::string red_cross = "\033[31m✗\033[0m";
|
std::string red_cross = "\033[31m✗\033[0m";
|
||||||
@ -322,7 +264,7 @@ std::string server_service::healthtick()
|
|||||||
return red_cross;
|
return red_cross;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int> server_service::get_ports()
|
std::vector<int> service_runner::get_ports()
|
||||||
{
|
{
|
||||||
std::vector<int> ports;
|
std::vector<int> ports;
|
||||||
if (!m_server_env) {
|
if (!m_server_env) {
|
||||||
@ -331,22 +273,16 @@ std::vector<int> server_service::get_ports()
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string ssh_cmd = construct_ssh_cmd();
|
std::string ssh_cmd = construct_ssh_cmd();
|
||||||
std::string script_dir = get_script_dir();
|
std::string script_path = mRemote_service_template_path + "/_ports.sh";
|
||||||
std::string script_path = script_dir + "/_ports.sh";
|
|
||||||
|
|
||||||
// Check if service directory exists
|
|
||||||
if (!check_service_dir_exists(ssh_cmd)) {
|
|
||||||
return ports;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if ports script exists
|
// Check if ports script exists
|
||||||
if (!check_remote_file_exists(ssh_cmd, script_path)) {
|
if (!check_remote_file_exists(ssh_cmd, script_path)) {
|
||||||
return ports;
|
return ports;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the ports script and capture output
|
// Run the ports script and capture output
|
||||||
std::string run_cmd = "'cd " + script_dir +
|
std::string run_cmd = "'cd " + mRemote_service_template_path +
|
||||||
" && /bin/bash " + script_path + " " + get_env_path() + "'";
|
" && /bin/bash " + script_path + " " + mRemote_service_env_file + "'";
|
||||||
|
|
||||||
// Create a temporary file to store the output
|
// Create a temporary file to store the output
|
||||||
std::string temp_file = "/tmp/dropshell_ports_" + std::to_string(getpid());
|
std::string temp_file = "/tmp/dropshell_ports_" + std::to_string(getpid());
|
@ -7,17 +7,16 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "server_env.hpp"
|
#include "server_env.hpp"
|
||||||
|
#include "services.hpp"
|
||||||
|
|
||||||
namespace dropshell {
|
namespace dropshell {
|
||||||
|
|
||||||
std::vector<std::string> get_server_services(const std::string& server_name);
|
class service_runner {
|
||||||
|
|
||||||
class server_service {
|
|
||||||
public:
|
public:
|
||||||
server_service();
|
service_runner();
|
||||||
bool init(const std::string& server_name, const std::string& service_name);
|
bool init(const std::string& server_name, const std::string& service_name);
|
||||||
|
|
||||||
// install the service over ssh, using the credentials from _server.env (via server_env.hpp), by:
|
// install the service over ssh, using the credentials from server.env (via server_env.hpp), by:
|
||||||
// 1. check if the server_name exists, and the service_name refers to a valid template
|
// 1. check if the server_name exists, and the service_name refers to a valid template
|
||||||
// 2. check if service_name is valid for the server_name
|
// 2. check if service_name is valid for the server_name
|
||||||
// 3. create the service directory on the server at {DROPSHELL_DIR}/{service_name}
|
// 3. create the service directory on the server at {DROPSHELL_DIR}/{service_name}
|
||||||
@ -26,7 +25,7 @@ class server_service {
|
|||||||
// 5. running the install.sh script on the server, passing the {service_name}.env file as an argument
|
// 5. running the install.sh script on the server, passing the {service_name}.env file as an argument
|
||||||
bool install();
|
bool install();
|
||||||
|
|
||||||
// run a command over ssh, using the credentials from _server.env (via server_env.hpp)
|
// run a command over ssh, using the credentials from server.env (via server_env.hpp)
|
||||||
// first check that the command corresponds to a valid .sh file in the service directory
|
// first check that the command corresponds to a valid .sh file in the service directory
|
||||||
// then run the command, passing the {service_name}.env file as an argument
|
// then run the command, passing the {service_name}.env file as an argument
|
||||||
// do a lot of checks, such as:
|
// do a lot of checks, such as:
|
||||||
@ -37,7 +36,7 @@ class server_service {
|
|||||||
// checking that the {service_name}.env file exists in the service directory.
|
// checking that the {service_name}.env file exists in the service directory.
|
||||||
bool run_command(const std::string& command);
|
bool run_command(const std::string& command);
|
||||||
|
|
||||||
// backup the service over ssh, using the credentials from _server.env (via server_env.hpp)
|
// backup the service over ssh, using the credentials from server.env (via server_env.hpp)
|
||||||
// 1. run backup.sh on the server
|
// 1. run backup.sh on the server
|
||||||
// 2. create a backup file with format server-service-datetime.tgz
|
// 2. create a backup file with format server-service-datetime.tgz
|
||||||
// 3. store it in the server's DROPSHELL_DIR/backups folder
|
// 3. store it in the server's DROPSHELL_DIR/backups folder
|
||||||
@ -59,15 +58,17 @@ class server_service {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::string m_server_name;
|
std::string m_server_name;
|
||||||
std::string m_service_name;
|
ServiceInfo m_service_info;
|
||||||
std::unique_ptr<server_env> m_server_env;
|
std::unique_ptr<server_env> m_server_env;
|
||||||
|
|
||||||
|
std::string mRemote_service_path;
|
||||||
|
std::string mRemote_service_config_path;
|
||||||
|
std::string mRemote_service_template_path;
|
||||||
|
std::string mRemote_service_env_file;
|
||||||
|
|
||||||
// Helper methods
|
// Helper methods
|
||||||
std::string construct_ssh_cmd() const;
|
std::string construct_ssh_cmd() const;
|
||||||
std::string get_service_dir() const;
|
bool check_remote_dir_exists(const std::string& ssh_cmd, const std::string& dir_path) const;
|
||||||
std::string get_env_path() const;
|
|
||||||
std::string get_script_dir() const;
|
|
||||||
bool check_service_dir_exists(const std::string& ssh_cmd) const;
|
|
||||||
bool check_remote_file_exists(const std::string& ssh_cmd, const std::string& file_path) const;
|
bool check_remote_file_exists(const std::string& ssh_cmd, const std::string& file_path) const;
|
||||||
bool execute_ssh_command(const std::string& command, const std::string& error_msg) const;
|
bool execute_ssh_command(const std::string& command, const std::string& error_msg) const;
|
||||||
bool execute_local_command(const std::string& command, const std::string& error_msg) const;
|
bool execute_local_command(const std::string& command, const std::string& error_msg) const;
|
93
src/services.cpp
Normal file
93
src/services.cpp
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#include "services.hpp"
|
||||||
|
#include "utils/envmanager.hpp"
|
||||||
|
#include "utils/directories.hpp"
|
||||||
|
#include "templates.hpp"
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace fs = boost::filesystem;
|
||||||
|
|
||||||
|
namespace dropshell {
|
||||||
|
|
||||||
|
std::vector<ServiceInfo> get_server_services_info(const std::string& server_name) {
|
||||||
|
std::vector<ServiceInfo> services;
|
||||||
|
|
||||||
|
std::string serverpath = get_local_config_servers_path();
|
||||||
|
if (serverpath.empty()) {
|
||||||
|
std::cerr << "Error: Server directory not found: " << serverpath << std::endl;
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
fs::path server_dir = fs::path(serverpath) / server_name;
|
||||||
|
if (!fs::exists(server_dir)) {
|
||||||
|
std::cerr << "Error: Server directory not found:" << server_dir.string() << std::endl;
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
for (const auto& entry : fs::directory_iterator(server_dir)) {
|
||||||
|
if (fs::is_directory(entry)) {
|
||||||
|
ServiceInfo service = get_service_info(server_name, entry.path().filename().string());
|
||||||
|
if (!service.template_name.empty()) {
|
||||||
|
services.push_back(service);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ServiceInfo get_service_info(const std::string &server_name, const std::string &service_name)
|
||||||
|
{
|
||||||
|
std::string service_dir = get_local_service_path(server_name, service_name);
|
||||||
|
if (service_dir.empty()) {
|
||||||
|
std::cerr << "Error: Service directory not found: " << service_dir << std::endl;
|
||||||
|
return ServiceInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
ServiceInfo service;
|
||||||
|
std::string local_service_env_path = get_local_service_env_path(server_name, service_name);
|
||||||
|
envmanager env(local_service_env_path);
|
||||||
|
if (!env.load()) {
|
||||||
|
std::cerr << "Error: service.env missing from " << local_service_env_path << std::endl;
|
||||||
|
return ServiceInfo();
|
||||||
|
}
|
||||||
|
service.template_name = env.get_variable("TEMPLATE");
|
||||||
|
|
||||||
|
if (service.template_name.empty()) {
|
||||||
|
std::cerr << "Error: TEMPLATE variable not defined in " << local_service_env_path << std::endl;
|
||||||
|
return ServiceInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
template_info tinfo;
|
||||||
|
template_manager tm;
|
||||||
|
if (!tm.get_template_info(service.template_name, tinfo)) {
|
||||||
|
std::cerr << "Error: Template '" << service.template_name << "' not found" << std::endl;
|
||||||
|
return ServiceInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the template path
|
||||||
|
service.template_path = tinfo.path;
|
||||||
|
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<std::string> get_used_commands(const std::string &server_name, const std::string &service_name)
|
||||||
|
{
|
||||||
|
std::set<std::string> commands;
|
||||||
|
|
||||||
|
ServiceInfo service = get_service_info(server_name, service_name);
|
||||||
|
if (service.template_path.empty()) {
|
||||||
|
std::cerr << "Error: Service not found: " << service_name << std::endl;
|
||||||
|
return commands;
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate over all files in the template path, and add the command name to the set.
|
||||||
|
// commands are .sh files that don't begin with _
|
||||||
|
fs::path template_path = fs::path(service.template_path);
|
||||||
|
for (const auto& entry : fs::directory_iterator(template_path)) {
|
||||||
|
if (fs::is_regular_file(entry) && entry.path().extension() == ".sh" && (entry.path().filename().string().rfind("_", 0) != 0))
|
||||||
|
commands.insert(entry.path().filename().string());
|
||||||
|
}
|
||||||
|
return commands;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dropshell
|
24
src/services.hpp
Normal file
24
src/services.hpp
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#ifndef SERVICES_HPP
|
||||||
|
#define SERVICES_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
namespace dropshell {
|
||||||
|
|
||||||
|
struct ServiceInfo {
|
||||||
|
std::string path;
|
||||||
|
std::string service_name;
|
||||||
|
std::string template_name;
|
||||||
|
std::string template_path;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<ServiceInfo> get_server_services_info(const std::string& server_name);
|
||||||
|
ServiceInfo get_service_info(const std::string& server_name, const std::string& service_name);
|
||||||
|
std::set<std::string> get_used_commands(const std::string& server_name, const std::string& service_name);
|
||||||
|
} // namespace dropshell
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
@ -1,10 +1,13 @@
|
|||||||
#include "init_user_directory.hpp"
|
#include "init_user_directory.hpp"
|
||||||
#include "templates.hpp"
|
#include "templates.hpp"
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
|
#include "utils/directories.hpp"
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
namespace dropshell {
|
namespace dropshell {
|
||||||
|
|
||||||
@ -20,7 +23,7 @@ bool template_manager::get_templates(std::vector<template_info>& templates) {
|
|||||||
templates.clear();
|
templates.clear();
|
||||||
|
|
||||||
// System templates directory
|
// System templates directory
|
||||||
const std::string system_templates_dir = "/opt/dropshell/templates";
|
const std::string system_templates_dir = get_local_system_templates_path();
|
||||||
// User templates directory (from config)
|
// User templates directory (from config)
|
||||||
std::string user_templates_dir;
|
std::string user_templates_dir;
|
||||||
if (!get_user_directory(user_templates_dir)) {
|
if (!get_user_directory(user_templates_dir)) {
|
||||||
@ -77,4 +80,29 @@ bool template_manager::get_template_info(const std::string& name, template_info&
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void list_templates() {
|
||||||
|
template_manager tm;
|
||||||
|
std::vector<template_info> templates;
|
||||||
|
|
||||||
|
if (!tm.get_templates(templates)) {
|
||||||
|
std::cerr << "Error: Failed to get templates" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (templates.empty()) {
|
||||||
|
std::cout << "No templates found." << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Available templates:" << std::endl;
|
||||||
|
std::cout << std::left << std::setw(20) << "Name" << "Path" << std::endl;
|
||||||
|
std::cout << std::string(60, '-') << std::endl;
|
||||||
|
|
||||||
|
for (const auto& t : templates) {
|
||||||
|
std::cout << std::left << std::setw(20) << t.name << t.path << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dropshell
|
} // namespace dropshell
|
160
src/utils/directories.cpp
Normal file
160
src/utils/directories.cpp
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
#include "directories.hpp"
|
||||||
|
#include "config.hpp"
|
||||||
|
#include "server_env.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
|
namespace fs = boost::filesystem;
|
||||||
|
|
||||||
|
namespace dropshell {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
std::string get_local_dropshell_config_path()
|
||||||
|
{
|
||||||
|
// Try ~/.config/dropshell/dropshell.conf
|
||||||
|
const char* home = std::getenv("HOME");
|
||||||
|
if (home) {
|
||||||
|
fs::path user_path = fs::path(home) / ".config" / "dropshell" / "dropshell.env";
|
||||||
|
return user_path.string();
|
||||||
|
}
|
||||||
|
std::cerr << "Warning: Couldn't determine user directory" << std::endl;
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_local_system_templates_path()
|
||||||
|
{
|
||||||
|
return "/opt/dropshell/templates";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_local_config_path()
|
||||||
|
{
|
||||||
|
config *cfg = get_global_config();
|
||||||
|
std::string user_dir;
|
||||||
|
if (!cfg->get_local_config_directory(user_dir)) {
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
return user_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_local_config_templates_path()
|
||||||
|
{
|
||||||
|
std::string config_path = get_local_config_path();
|
||||||
|
if (config_path.empty()) {
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
return config_path + "/templates";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_local_config_servers_path()
|
||||||
|
{
|
||||||
|
std::string config_path = get_local_config_path();
|
||||||
|
if (config_path.empty()) {
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
return config_path + "/servers";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_local_config_backups_path()
|
||||||
|
{
|
||||||
|
std::string config_path = get_local_config_path();
|
||||||
|
if (config_path.empty()) {
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
return config_path + "/backups";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_local_server_path(const std::string &server_name)
|
||||||
|
{
|
||||||
|
std::string config_path = get_local_config_path();
|
||||||
|
if (config_path.empty())
|
||||||
|
return std::string();
|
||||||
|
return config_path + "/servers/" + server_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_local_server_env_path(const std::string &server_name)
|
||||||
|
{
|
||||||
|
std::string serverpath = get_local_server_path(server_name);
|
||||||
|
if (serverpath.empty())
|
||||||
|
return std::string();
|
||||||
|
return (fs::path(serverpath) / "server.env").string();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_local_service_path(const std::string &server_name, const std::string &service_name)
|
||||||
|
{
|
||||||
|
std::string serverpath = get_local_server_path(server_name);
|
||||||
|
if (serverpath.empty())
|
||||||
|
return std::string();
|
||||||
|
return (fs::path(serverpath) / service_name).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_local_service_env_path(const std::string &server_name, const std::string &service_name)
|
||||||
|
{
|
||||||
|
std::string servicepath = get_local_service_path(server_name, service_name);
|
||||||
|
if (servicepath.empty())
|
||||||
|
return std::string();
|
||||||
|
return (fs::path(servicepath) / "service.env").string();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// remote paths
|
||||||
|
// DROPSHELL_DIR
|
||||||
|
// |-- service name
|
||||||
|
// |-- config
|
||||||
|
// |-- service.env
|
||||||
|
// |-- (user config files)
|
||||||
|
// |-- template
|
||||||
|
// |-- (script files)
|
||||||
|
// |-- backups
|
||||||
|
|
||||||
|
std::string get_remote_DROPSHELL_path(const std::string &server_name)
|
||||||
|
{
|
||||||
|
server_env env(server_name);
|
||||||
|
if (!env.is_valid())
|
||||||
|
return std::string();
|
||||||
|
return env.get_DROPSHELL_DIR();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_remote_service_path(const std::string &server_name, const std::string &service_name)
|
||||||
|
{
|
||||||
|
std::string dropshell_path = get_remote_DROPSHELL_path(server_name);
|
||||||
|
if (dropshell_path.empty())
|
||||||
|
return std::string();
|
||||||
|
return (fs::path(dropshell_path) / service_name).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_remote_service_config_path(const std::string &server_name, const std::string &service_name)
|
||||||
|
{
|
||||||
|
std::string service_path = get_remote_service_path(server_name, service_name);
|
||||||
|
if (service_path.empty())
|
||||||
|
return std::string();
|
||||||
|
return (fs::path(service_path) / "config").string();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_remote_service_template_path(const std::string &server_name, const std::string &service_name)
|
||||||
|
{
|
||||||
|
std::string service_path = get_remote_service_path(server_name, service_name);
|
||||||
|
if (service_path.empty())
|
||||||
|
return std::string();
|
||||||
|
return (fs::path(service_path) / "template").string();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_remote_service_backups_path(const std::string &server_name, const std::string &service_name)
|
||||||
|
{
|
||||||
|
std::string service_path = get_remote_service_path(server_name, service_name);
|
||||||
|
if (service_path.empty())
|
||||||
|
return std::string();
|
||||||
|
return (fs::path(service_path) / "backups").string();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_remote_service_env_file(const std::string &server_name, const std::string &service_name)
|
||||||
|
{
|
||||||
|
std::string service_path = get_remote_service_config_path(server_name, service_name);
|
||||||
|
if (service_path.empty())
|
||||||
|
return std::string();
|
||||||
|
return (fs::path(service_path) / "service.env").string();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dropshell
|
40
src/utils/directories.hpp
Normal file
40
src/utils/directories.hpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#ifndef DIRECTORIES_HPP
|
||||||
|
#define DIRECTORIES_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace dropshell {
|
||||||
|
|
||||||
|
// local paths - return empty string on failure
|
||||||
|
std::string get_local_dropshell_config_path();
|
||||||
|
std::string get_local_system_templates_path();
|
||||||
|
std::string get_local_config_path();
|
||||||
|
std::string get_local_config_templates_path();
|
||||||
|
std::string get_local_config_servers_path();
|
||||||
|
std::string get_local_config_backups_path();
|
||||||
|
|
||||||
|
std::string get_local_server_path(const std::string &server_name);
|
||||||
|
std::string get_local_server_env_path(const std::string &server_name);
|
||||||
|
|
||||||
|
std::string get_local_service_path(const std::string &server_name, const std::string &service_name);
|
||||||
|
std::string get_local_service_env_path(const std::string &server_name, const std::string &service_name);
|
||||||
|
|
||||||
|
// remote paths
|
||||||
|
// DROPSHELL_DIR
|
||||||
|
// |-- service name
|
||||||
|
// |-- config
|
||||||
|
// |-- service.env
|
||||||
|
// |-- (user config files)
|
||||||
|
// |-- template
|
||||||
|
// |-- (script files)
|
||||||
|
// |-- backups
|
||||||
|
std::string get_remote_DROPSHELL_path(const std::string &server_name);
|
||||||
|
std::string get_remote_service_path(const std::string &server_name, const std::string &service_name);
|
||||||
|
std::string get_remote_service_config_path(const std::string &server_name, const std::string &service_name);
|
||||||
|
std::string get_remote_service_template_path(const std::string &server_name, const std::string &service_name);
|
||||||
|
std::string get_remote_service_backups_path(const std::string &server_name, const std::string &service_name);
|
||||||
|
|
||||||
|
std::string get_remote_service_env_file(const std::string &server_name, const std::string &service_name);
|
||||||
|
} // namespace dropshell
|
||||||
|
|
||||||
|
#endif
|
135
src/utils/envmanager.cpp
Normal file
135
src/utils/envmanager.cpp
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
#include "envmanager.hpp"
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <regex>
|
||||||
|
#include <cstdlib> // For std::getenv
|
||||||
|
|
||||||
|
namespace dropshell {
|
||||||
|
|
||||||
|
envmanager::envmanager(std::string path) : m_path(path) {
|
||||||
|
}
|
||||||
|
|
||||||
|
envmanager::~envmanager() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool envmanager::load() {
|
||||||
|
std::ifstream file(m_path);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_variables.clear();
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(file, line)) {
|
||||||
|
line=trim(line);
|
||||||
|
// Skip empty lines and comments
|
||||||
|
if (line.empty() || line[0] == '#') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pos = line.find('=');
|
||||||
|
if (pos != std::string::npos) {
|
||||||
|
std::string key = line.substr(0, pos);
|
||||||
|
std::string value = line.substr(pos + 1);
|
||||||
|
|
||||||
|
// trim whitespace from the key and value
|
||||||
|
m_variables[trim(key)] = trim(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void envmanager::save() {
|
||||||
|
std::ofstream file(m_path);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& pair : m_variables) {
|
||||||
|
file << pair.first << "=" << pair.second << std::endl;
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string envmanager::get_variable(std::string key) const {
|
||||||
|
key = trim(key);
|
||||||
|
|
||||||
|
// Use case-insensitive comparison to find the key
|
||||||
|
for (const auto& pair : m_variables) {
|
||||||
|
if (pair.first == key) {
|
||||||
|
return pair.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
void envmanager::get_all_variables(std::map<std::string, std::string>& variables) const {
|
||||||
|
variables = m_variables;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string envmanager::get_variable_substituted(std::string key) const {
|
||||||
|
std::string value = get_variable(key);
|
||||||
|
return expand_patterns(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void envmanager::get_all_variables_substituted(std::map<std::string, std::string>& variables) const {
|
||||||
|
variables.clear();
|
||||||
|
for (const auto& pair : m_variables) {
|
||||||
|
variables[pair.first] = expand_patterns(pair.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void envmanager::add_variables(std::map<std::string, std::string> variables) {
|
||||||
|
for (auto& pair : variables) {
|
||||||
|
set_variable(pair.first, pair.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void envmanager::set_variable(std::string key, std::string value) {
|
||||||
|
m_variables[trim(key)] = trim(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void envmanager::clear_variables() {
|
||||||
|
m_variables.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string envmanager::trim(std::string str) const {
|
||||||
|
// Trim leading whitespace
|
||||||
|
str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](unsigned char ch) {
|
||||||
|
return !std::isspace(ch);
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Trim trailing whitespace
|
||||||
|
str.erase(std::find_if(str.rbegin(), str.rend(), [](unsigned char ch) {
|
||||||
|
return !std::isspace(ch);
|
||||||
|
}).base(), str.end());
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string envmanager::expand_patterns(std::string str) const {
|
||||||
|
// Combined regex pattern for both ${var} and $var formats
|
||||||
|
std::regex var_pattern("\\$(?:\\{([^}]+)\\}|([a-zA-Z0-9_]+))");
|
||||||
|
std::string result = str;
|
||||||
|
std::smatch match;
|
||||||
|
|
||||||
|
while (std::regex_search(result, match, var_pattern)) {
|
||||||
|
// match[1] will contain capture from ${var} format
|
||||||
|
// match[2] will contain capture from $var format
|
||||||
|
std::string var_name = match[1].matched ? match[1].str() : match[2].str();
|
||||||
|
|
||||||
|
// Get value from system environment variables
|
||||||
|
const char* env_value = std::getenv(var_name.c_str());
|
||||||
|
std::string value = env_value ? env_value : "";
|
||||||
|
|
||||||
|
result = result.replace(match.position(), match.length(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dropshell
|
49
src/utils/envmanager.hpp
Normal file
49
src/utils/envmanager.hpp
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#ifndef ENV_MANAGER_HPP
|
||||||
|
#define ENV_MANAGER_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
namespace dropshell {
|
||||||
|
|
||||||
|
// envmanager is a class that manages the environment files for the application.
|
||||||
|
// it is responsible for loading the environment files, and providing a class to access the variables.
|
||||||
|
// it can also save the environment files.
|
||||||
|
class envmanager {
|
||||||
|
public:
|
||||||
|
envmanager(std::string path);
|
||||||
|
~envmanager();
|
||||||
|
|
||||||
|
// load all variables from the environment file
|
||||||
|
bool load();
|
||||||
|
|
||||||
|
// save all variables to the environment file
|
||||||
|
void save();
|
||||||
|
|
||||||
|
// get variables from the environment files. Trim whitespace from the values.
|
||||||
|
// keys are case-sensitive.
|
||||||
|
std::string get_variable(std::string key) const;
|
||||||
|
void get_all_variables(std::map<std::string, std::string>& variables) const;
|
||||||
|
|
||||||
|
// get variables, but replace patterns ${var} and $var with the actual environment variable in the returned string.
|
||||||
|
// trim whitespace from the values.
|
||||||
|
std::string get_variable_substituted(std::string key) const;
|
||||||
|
void get_all_variables_substituted(std::map<std::string, std::string>& variables) const;
|
||||||
|
|
||||||
|
// add variables to the environment files.
|
||||||
|
// trim whitespace from the values.
|
||||||
|
void add_variables(std::map<std::string, std::string> variables);
|
||||||
|
void set_variable(std::string key, std::string value);
|
||||||
|
void clear_variables();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string trim(std::string str) const;
|
||||||
|
std::string expand_patterns(std::string str) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_path;
|
||||||
|
std::map<std::string, std::string> m_variables;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dropshell
|
||||||
|
|
||||||
|
#endif
|
@ -1,16 +0,0 @@
|
|||||||
# This file contains environment variables specific to the server where the application will be deployed.
|
|
||||||
# Copy this file to .env and modify the values according to your server's configuration.
|
|
||||||
# Do not commit the actual .env file to version control as it may contain sensitive information.
|
|
||||||
|
|
||||||
# Application settings
|
|
||||||
CONTAINER_PORT=8181
|
|
||||||
HOST_PORT=80
|
|
||||||
|
|
||||||
# Deployment settings
|
|
||||||
LOCAL_DATA_FOLDER="${HOME}/.sk"
|
|
||||||
CONTAINER_NAME="squashkiwi"
|
|
||||||
|
|
||||||
# Image settings
|
|
||||||
IMAGE_REGISTRY="gitea.jde.nz"
|
|
||||||
IMAGE_REPO="squashkiwi/squashkiwi"
|
|
||||||
IMAGE_TAG="latest"
|
|
15
templates/squashkiwi/example/service.env
Normal file
15
templates/squashkiwi/example/service.env
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Service settings
|
||||||
|
TEMPLATE=squashkiwi
|
||||||
|
|
||||||
|
# Application settings
|
||||||
|
CONTAINER_PORT=8181
|
||||||
|
HOST_PORT=80
|
||||||
|
|
||||||
|
# Deployment settings
|
||||||
|
LOCAL_DATA_FOLDER="${HOME}/.sk"
|
||||||
|
CONTAINER_NAME="squashkiwi"
|
||||||
|
|
||||||
|
# Image settings
|
||||||
|
IMAGE_REGISTRY="gitea.jde.nz"
|
||||||
|
IMAGE_REPO="squashkiwi/squashkiwi"
|
||||||
|
IMAGE_TAG="latest"
|
Loading…
x
Reference in New Issue
Block a user