From 23563581632a11330d0ad6581555de606182d541 Mon Sep 17 00:00:00 2001 From: Hepatica Date: Sat, 17 Aug 2024 00:09:51 +0200 Subject: [PATCH] Adjust app introduce port changinf logic and process file processing fuctnions + adjust drawio file diagramm --- .$SpCloud.drawio.dtmp | 181 ------------------ SpCloud.drawio | 22 ++- SpCloudMain/Controllers/PublishController.cpp | 92 ++++++--- SpCloudMain/Models/App.cpp | 61 ++---- SpCloudMain/Service/DiscordService.cpp | 57 +----- SpCloudMain/Service/FileProcessingService.cpp | 120 +++++++++++- SpCloudMain/Service/MongoDbService.cpp | 28 +++ SpCloudMain/SpCloudMain.cpp | 56 +++--- 8 files changed, 267 insertions(+), 350 deletions(-) delete mode 100644 .$SpCloud.drawio.dtmp diff --git a/.$SpCloud.drawio.dtmp b/.$SpCloud.drawio.dtmp deleted file mode 100644 index 7da353a..0000000 --- a/.$SpCloud.drawio.dtmp +++ /dev/null @@ -1,181 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SpCloud.drawio b/SpCloud.drawio index a64815b..91ec9e6 100644 --- a/SpCloud.drawio +++ b/SpCloud.drawio @@ -1,6 +1,6 @@ - + - + @@ -68,7 +68,7 @@ - + @@ -118,8 +118,8 @@ - - + + @@ -134,7 +134,7 @@ - + @@ -145,8 +145,8 @@ - - + + @@ -175,6 +175,12 @@ + + + + + + diff --git a/SpCloudMain/Controllers/PublishController.cpp b/SpCloudMain/Controllers/PublishController.cpp index d098078..7ca47eb 100644 --- a/SpCloudMain/Controllers/PublishController.cpp +++ b/SpCloudMain/Controllers/PublishController.cpp @@ -1,10 +1,11 @@ -#include "../httplib.h" +#include "../httplib.h" //#include //#include "Service/AuthorizationService.cpp" //#include "Service/FileProcessingService.cpp" #include "../Service/AuthorizationService.cpp" #include "../Service/FileProcessingService.cpp" +#include "../Models/App.cpp" //#include "Service/Logger.cpp" class PublishController @@ -20,6 +21,8 @@ private: Logger& logger_; + int last_available_port = 8081; + //std::string publish_app_path = "C:/Temps/";// Todo delete if not needed public: @@ -29,49 +32,47 @@ public: } public: - void process_publish(const httplib::Request& req, httplib::Response& res) + std::string process_publish(const httplib::Request& req, App* app) { - //if (this->authorization.is_user_authorized()) - if (true)//Todo change to is user authorized - { - const auto& content = req.files.begin()->second.content; + const auto& content = req.files.begin()->second.content; - const auto& filename = this->publish_app_path + req.files.begin()->second.filename; + const auto& filename = this->publish_app_path + req.files.begin()->second.filename; - if (filename.size() >= 4 && filename.substr(filename.size() - 4) == ".rar") { - if (file_processing->save_file(filename, content)) { + if (filename.size() >= 4 && filename.substr(filename.size() - 4) == ".rar") { + //if (true) {//TODO UNCOMMIT WHEN STARING TO WRITE PUBLISH PROCESS + if (file_processing->save_file(filename, content)) { - std::string random_string = generate_random_string(20);//TODO VERY IMPORTANT CHANGE THIS RANDOM GENERATING TO GENERATE UNIQUE STRING + file_processing->unzip(filename, this->publish_app_path + app->get_user_id());//TODO UNCOMMIT WHEN STARING TO WRITE PUBLISH PROCESS - file_processing->unzip(filename, this->publish_app_path + random_string); + /*check_port_and_increase_if_not_available(); - this->dotnet_publish(this->publish_app_path + random_string); + file_processing->adjust_nginx_configuration_and_reloud(app->get_name(), std::to_string(last_available_port));*/ - res.set_content("File uploaded successfully: " + filename, "text/plain"); - } - else { - res.status = 500; - res.set_content("Failed to save file, please ensure you are putting rar file" - + filename, "text/plain"); - } + //file_processing->create_service_file(app->get_name());//TODO UNCOMMIT WHEN STARING TO WRITE PUBLISH PROCESS + + //this->dotnet_publish(this->publish_app_path + app->get_user_id(), last_available_port);//TODO UNCOMMIT WHEN STARING TO WRITE PUBLISH PROCESS + + //Todo introduce old binary file + + file_processing->delete_file(filename); + + return "File uploaded successfully: " + filename; } else { - res.status = 400; - res.set_content("Invalid file type. Only .rar files are allowed.", - "text/plain"); + return "Failed to save file, please ensure you are putting rar file" + filename; } } - else - { - //Todo add logging and exiting from function with bead request + else { + return "Invalid file type. Only .rar files are allowed." + filename; + } } private: - void dotnet_publish(const std::string& path) - { + void dotnet_publish(const std::string& path, int port) + {//Todo adjust to build setting from mongodb std::string dll_file_name = file_processing->find_file_by_suffix(path, "exe"); - + //Todo introduce deleting old rar file after publishing size_t pos = dll_file_name.find(".exe"); if (pos != std::string::npos) { dll_file_name.replace(pos, 4, ".dll"); @@ -84,6 +85,41 @@ private: commandThread.detach(); } + void check_port_and_increase_if_not_available() + { + while (true) + { + std::string port_str = std::to_string(last_available_port); + + std::string command = "ss -tuln | grep :" + port_str; + + auto request = std::async(std::launch::async, &PublishController::execute_command, this, command); + + std::string response = request.get(); + + if (!response.empty()) + { + last_available_port++; + } + else + { + break; + } + } + + } + + std::string execute_command(const std::string& command) { + std::array buffer; + std::string result; + std::shared_ptr pipe(popen(command.c_str(), "r"), pclose); + if (!pipe) throw std::runtime_error("popen() failed!"); + while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) { + result += buffer.data(); + } + return result; + } + static std::string generate_random_string(size_t length, const std::string& char_set = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") { std::random_device rd; std::mt19937 generator(rd()); diff --git a/SpCloudMain/Models/App.cpp b/SpCloudMain/Models/App.cpp index 874271b..9ab3f19 100644 --- a/SpCloudMain/Models/App.cpp +++ b/SpCloudMain/Models/App.cpp @@ -1,50 +1,29 @@ #include - +#ifndef APP_H +#define APP_H class App { private: - int id; std::string name; - std::string authToken; - bool isBanned; + std::string user_id; + std::string url; + std::string url_on_local_machine; + std::string target; public: - // Конструктор - App(int id, const std::string& name, const std::string& authToken, bool isBanned) - : id(id), name(name), authToken(authToken), isBanned(isBanned) {} + App(const std::string& name, const std::string& user_id, const std::string& url, + const std::string& url_on_local_machine, const std::string& target) + : name(name), user_id(user_id), url(url), url_on_local_machine(url_on_local_machine), target(target) {} - // Геттеры и сеттеры для Id - int getId() const { - return id; - } + std::string get_name() const { return name; } + std::string get_user_id() const { return user_id; } + std::string get_url() const { return url; } + std::string get_url_on_local_machine() const { return url_on_local_machine; } + std::string get_target() const { return target; } - void setId(int id) { - this->id = id; - } - - // Геттеры и сеттеры для Name - std::string getName() const { - return name; - } - - void setName(const std::string& name) { - this->name = name; - } - - // Геттеры и сеттеры для AuthToken - std::string getAuthToken() const { - return authToken; - } - - void setAuthToken(const std::string& authToken) { - this->authToken = authToken; - } - - // Геттеры и сеттеры для IsBanned - bool getIsBanned() const { - return isBanned; - } - - void setIsBanned(bool isBanned) { - this->isBanned = isBanned; - } + void set_name(const std::string& name) { this->name = name; } + void set_user_id(const std::string& user_id) { this->user_id = user_id; } + void set_url(const std::string& url) { this->url = url; } + void set_url_on_local_machine(const std::string& url_on_local_machine) { this->url_on_local_machine = url_on_local_machine; } + void set_target(const std::string& target) { this->target = target; } }; +#endif // APP_H diff --git a/SpCloudMain/Service/DiscordService.cpp b/SpCloudMain/Service/DiscordService.cpp index efaab2b..0ee9cd4 100644 --- a/SpCloudMain/Service/DiscordService.cpp +++ b/SpCloudMain/Service/DiscordService.cpp @@ -21,29 +21,6 @@ public: { std::string auth_code_processed = extract_code(auth_code); - //std::string body = "client_id=1273414933874479185&"//Todo delete comments if not needed - // "client_secret=S_vG4frjlxWoi8mic_GlcxUO0aWxXwRJ&" - // "grant_type=authorization_code&" - // "code=" + auth_code_processed + "&" - // "redirect_uri=https://www.sp-donate.ru/pay/Hepatir"; - - //httplib::Headers headers = { - // {"Content-Type", "application/x-www-form-urlencoded"} - //}; - - //httplib::Client client("discord.com/api"); - ////// Выполняем простой GET-запрос на http://httpbin.org/get - ////auto res = client.Get("/get"); - //auto res = client.Post("/oauth2/token", headers, body, "application/x-www-form-urlencoded"); - ////auto res = client_.Post("/oauth2/token", headers, body, "application/x-www-form-urlencoded"); - - //if (res && res->status == 200) { - // return res->body; - //} - //else { - // return "Error: ";//Todo write handling this - //} - std::string command = "curl --location https://discord.com/api/oauth2/token " "--header \"Content-Type: application/x-www-form-urlencoded\" " "--data-urlencode \"client_id=1273414933874479185\" " @@ -62,19 +39,6 @@ public: auto me_request = std::async(std::launch::async, &DiscordService::execute_command, this, command); - //httplib::Headers headers = { - // {"Authorization", "Bearer " + access_token} - //}; - - //auto res = client_.Get("/users/@me", headers); - - //if (res && res->status == 200) { - // return res->body; - //} - //else { - // return "Error: ";//Todo write handling this - //} - std::string user_id = extract_user_id(me_request.get()); return user_id; @@ -107,35 +71,16 @@ public: return result; } - //std::string extract_user_id(const std::string& input) { - // std::string search_pattern = "\"discord_id\":\""; - // std::size_t start_pos = input.find(search_pattern); - // if (start_pos == std::string::npos) { - // throw std::runtime_error("discord_id not found"); - // } - - // start_pos += search_pattern.length(); // move to the start of the user_id - // std::size_t end_pos = input.find("\"", start_pos); - // if (end_pos == std::string::npos) { - // throw std::runtime_error("End of discord_id not found"); - // } - - // return input.substr(start_pos, end_pos - start_pos); - //} - - std::string extract_code(const std::string& json_str) { std::string key = "\"code\":"; size_t start = json_str.find(key); if (start != std::string::npos) { start += key.length(); - // Пропускаем любые пробелы или символы ':' while (start < json_str.length() && (json_str[start] == ' ' || json_str[start] == '\"' || json_str[start] == ':')) { start++; } - // Найти конец строки size_t end = json_str.find('\"', start); if (end != std::string::npos) { return json_str.substr(start, end - start); @@ -154,6 +99,6 @@ public: return response.substr(start_pos, end_pos - start_pos); } } - return ""; // Вернуть пустую строку, если токен не найден + return ""; } }; diff --git a/SpCloudMain/Service/FileProcessingService.cpp b/SpCloudMain/Service/FileProcessingService.cpp index 86b8e54..db6a7d1 100644 --- a/SpCloudMain/Service/FileProcessingService.cpp +++ b/SpCloudMain/Service/FileProcessingService.cpp @@ -7,6 +7,7 @@ #include #include "CommandService.cpp" #include "Logger.cpp" +#include class FileProcessingService { Logger& logger_; @@ -18,6 +19,79 @@ public: } + void adjust_nginx_configuration_and_reloud(const std::string& filename, std::string port) + { + std::string file_path = "/etc/nginx/nginx.conf"; + + std::string new_text = + "server {\n" + " listen 443 ssl;\n" + " server_name " + filename + ".almavid.ru;\n\n" + " ssl_certificate /etc/letsencrypt/live/almavid.ru/fullchain.pem;\n" + " ssl_certificate_key /etc/letsencrypt/live/almavid.ru/privkey.pem;\n" + " ssl_protocols TLSv1 TLSv1.1 TLSv1.2;\n" + " ssl_ciphers HIGH:!aNULL:!MD5;\n\n" + " client_max_body_size 2G; // Allow file uploads up to 2GB\n\n" + " location / {\n" + " proxy_pass http://localhost:" + port + ";\n" + " proxy_set_header Host $host;\n" + " proxy_set_header X-Real-IP $remote_addr;\n" + " proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n" + " proxy_set_header X-Forwarded-Proto $scheme;\n\n" + " # Support for WebSocket\n" + " proxy_http_version 1.1;\n" + " proxy_set_header Upgrade $http_upgrade;\n" + " proxy_set_header Connection \"upgrade\";\n" + " }\n\n" + "}\n"; + + std::ofstream file(file_path, std::ios::app); + + if (!file.is_open()) { + logger_.log(INFO, "Error: Could not open file " + file_path + strerror(errno) + '\n'); + return; + } + + file << new_text << '\n'; + + file.close(); + + // Reload Nginx to apply the changes + /*int result = std::system("sudo systemctl reload nginx"); + if (result != 0) { + std::cerr << "Error: Failed to reload Nginx" << std::endl; + return 1; + }*/ + + std::string command = "sudo systemctl reload nginx"; + + std::thread commandThread(&CommandService::execute_command, command); + + commandThread.join(); + + logger_.log(INFO, "Nginx reloaded successfully."); + } + + void delete_file(const std::string& file_path) const + { + try { + // Delete the original file + if (std::filesystem::exists(file_path)) { + std::filesystem::remove(file_path); + logger_.log(INFO, "Deleted file: " + file_path); + } + + // Delete the directory recursively//Todo test if method will not work + /*if (std::filesystem::exists(final_files_directory)) { + std::filesystem::remove_all(final_files_directory); + logger_.log(INFO, "Deleted directory: " + final_files_directory); + }*/ + } + catch (const std::filesystem::filesystem_error& e) { + logger_.log(ERROR, "Error during deletion: " + std::string(e.what())); + } + } + bool save_file(const std::string& filename, const std::string& content) { logger_.log(INFO, "Start saving file method"); @@ -35,6 +109,48 @@ public: return ofs.good(); } + void create_service_file(std::string name) + { + logger_.log(INFO, "Start create_service_file"); + + std::string filename = "/etc/systemd/system/" + name + ".service"; + std::ofstream serviceFile(filename); + + if (serviceFile.is_open()) { + serviceFile << "[Unit]\n"; + serviceFile << "Description=" << name << " Service\n"; + serviceFile << "After=network.target\n\n"; + + serviceFile << "[Service]\n"; + serviceFile << "ExecStart=/home/danilt2000/SpCloud/" + name + "/build/ " + name + "\n"; + //serviceFile << "ExecStart=/home/danilt2000/SpCloud/SpCloudMain/build/SpCloudMain\n"; + serviceFile << "WorkingDirectory=/home/danilt2000/SpCloud/" + name + "/build\n"; + //serviceFile << "WorkingDirectory=/home/danilt2000/SpCloud/SpCloudMain/build\n"; + serviceFile << "Restart=always\n"; + serviceFile << "User=danilt2000\n"; + serviceFile << "Environment=PATH=/usr/bin\n"; + serviceFile << "Environment=NODE_ENV=production\n\n"; + + serviceFile << "[Install]\n"; + serviceFile << "WantedBy=multi-user.target\n"; + + std::string command = "sudo systemctl daemon-reload"; + + std::thread commandThread(&CommandService::execute_command, command); + + commandThread.join(); + + //Todo check service ->sudo systemctl status .service + + serviceFile.close(); + + logger_.log(INFO, "Service file " + filename + " created successfully.\n"); + } + else { + logger_.log(INFO, "Unable to open file " + filename + " for writing: " + strerror(errno) + "\n"); + } + } + void create_directory(const std::string& path) { std::filesystem::create_directories(path); } @@ -60,8 +176,4 @@ public: } return ""; //todo add throwing exception } - -/*private: - std::mutex file_mutex; */// Мьютекс для синхронизации доступа к файлу - }; diff --git a/SpCloudMain/Service/MongoDbService.cpp b/SpCloudMain/Service/MongoDbService.cpp index b107176..0c19abf 100644 --- a/SpCloudMain/Service/MongoDbService.cpp +++ b/SpCloudMain/Service/MongoDbService.cpp @@ -86,6 +86,34 @@ public: return "Success"; } + std::string is_app_name_free(std::string name) + { + std::string json_data = R"({ + "dataSource": "SpCloudCluster", + "database": "SpCloud", + "collection": "Apps", + "filter": { + "name": ")" + name + R"(" + } + })"; + + std::string command = "curl --location 'https://eu-central-1.aws.data.mongodb-api.com/app/data-zvcqvrr/endpoint/data/v1/action/findOne' " + "--header 'Content-Type: application/json' " + "--header 'api-key: Q1NfSCrruUAzsxdrjhZd3sjSwiqbdSFmCLeaCatZiuohUXsvEq9RtEAeG0JL2Jd7' " + "--data-raw '" + json_data + "'"; + + auto request = std::async(std::launch::async, &MongoDbService::execute_command, this, command); + + std::string response = request.get(); + + if (response == "{\"document\":null}") + { + return "Success"; + } + + return "App name isn't free please select another one"; + } + std::string add_app(std::string name, std::string user_id, std::string url, std::string url_on_local_mahcine, std::string target) { std::string json_data = R"({ diff --git a/SpCloudMain/SpCloudMain.cpp b/SpCloudMain/SpCloudMain.cpp index 595d2ea..26304f7 100644 --- a/SpCloudMain/SpCloudMain.cpp +++ b/SpCloudMain/SpCloudMain.cpp @@ -1,16 +1,12 @@ -// SpCloudMain.cpp : Defines the entry point for the application. -// - -// ReSharper disable CppClangTidyBugproneSuspiciousInclude +// ReSharper disable CppClangTidyBugproneSuspiciousInclude #include "SpCloudMain.h" #include "httplib.h" #include "Controllers/PublishController.cpp" #include "Service/DiscordService.cpp" #include "Service/MongoDbService.cpp" +#include "Models/App.cpp" -//#include "Service/AuthorizationService.cpp" -//#include "Service/FileProcessingService.cpp" using namespace std; int main() @@ -52,19 +48,37 @@ int main() { logger.log(INFO, "Start publish from main"); - string is_user_can_publish_response = mongo_service.is_user_can_publish("khBuvDWPHOhPSiQNVQZm9PM0VF29dqAaDBjWX4BnxJKzRvg0Gm");//TODO UNCOMMENT AND FIX + std::string user_id = req.get_file_value("UserId").content; + std::string name = req.get_file_value("Name").content; + ranges::transform(name, name.begin(), [](unsigned char c) { return std::tolower(c); }); + std::string target = req.get_file_value("Target").content; + std::string authorization_token = req.get_header_value("Authorization"); + + string is_user_can_publish_response = mongo_service.is_user_can_publish(authorization_token); if (is_user_can_publish_response != "Success") { - res.set_content(is_user_can_publish_response, "text/plain");//Todo add app address showing + res.set_content(is_user_can_publish_response, "text/plain"); return; } - //publish_controller.process_publish(req, res);//TODO UNCOMMENT AND FIX + string is_app_name_response = mongo_service.is_app_name_free(name); - //mongo_service.add_app("test", "test", "test", "test", "test");//TODO UNCOMMENT AND FIX + if (is_app_name_response != "Success") + { + res.set_content(is_app_name_response, "text/plain"); + return; + } + + App* app = new App(name, user_id, "url", "local_url", target); + + publish_controller.process_publish(req, app); + + //mongo_service.add_app("test", "test", "test", "test", "test");//TODO UNCOMMENT AND FIX + + delete app; res.set_content("App is running on address ????", "text/plain");//Todo add app address showing }); @@ -79,27 +93,5 @@ int main() res.set_content(result, "text/plain"); }); - - /*httplib::Client cli("https://discord.com/api/oauth2/token"); - - std::string jsonData = R"({ - "dataSource": "Cluster0", - "database": "myDatabase", - "collection": "items", - "document": { - "name": "Item Name", - "value": "Item Value" - } - })"; - - auto res = cli.Post("/app/data-abcde/endpoint/data/v1/action/insertOne", jsonData, "application/json"); - - if (res && res->status == 200) { - std::cout << "Success: " << res->body << std::endl; - } - else { - std::cerr << "Error: " << res.error() << std::endl; - }*/ - svr.listen("0.0.0.0", 8081); }