diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml
new file mode 100644
index 0000000..97063b9
--- /dev/null
+++ b/.github/workflows/docker-build.yml
@@ -0,0 +1,44 @@
+name: Publish Docker image
+
+on:
+ release:
+ types: [published]
+ push:
+ branches: [ "master" ]
+
+ pull_request:
+ branches: [ "master" ]
+
+
+jobs:
+ push_to_registries:
+ name: Push Docker image to multiple registries
+ runs-on: ubuntu-latest
+ permissions:
+ packages: write
+ contents: read
+ steps:
+ - name: Check out the repo
+ uses: actions/checkout@v4
+
+ - name: Log in to the Container registry
+ uses: docker/login-action@v2
+ with:
+ registry: ghcr.io
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Extract metadata (tags, labels) for Docker
+ id: meta
+ uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
+ with:
+ images: |
+ ghcr.io/${{ github.repository }}
+
+ - name: Build and push Docker images
+ uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671
+ with:
+ context: .
+ push: true
+ tags: ${{ steps.meta.outputs.tags }}
+ labels: ${{ steps.meta.outputs.labels }}
\ No newline at end of file
diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json
new file mode 100644
index 0000000..92dd19c
--- /dev/null
+++ b/.vscode/c_cpp_properties.json
@@ -0,0 +1,16 @@
+{
+ "configurations": [
+ {
+ "name": "Linux",
+ "includePath": [
+ "${workspaceFolder}/**"
+ ],
+ "defines": [],
+ "compilerPath": "/usr/bin/gcc",
+ "cStandard": "c23",
+ "cppStandard": "gnu++23",
+ "intelliSenseMode": "linux-gcc-x64"
+ }
+ ],
+ "version": 4
+}
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..ab7e3ec
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,31 @@
+{
+ "configurations": [
+ {
+ "name": "C/C++: g++ сборка и отладка активного файла",
+ "type": "cppdbg",
+ "request": "launch",
+ "program": "${fileDirname}/${fileBasenameNoExtension}",
+ "args": [],
+ "stopAtEntry": false,
+ "cwd": "${fileDirname}",
+ "environment": [],
+ "externalConsole": false,
+ "MIMode": "gdb",
+ "setupCommands": [
+ {
+ "description": "Включить автоматическое форматирование для gdb",
+ "text": "-enable-pretty-printing",
+ "ignoreFailures": true
+ },
+ {
+ "description": "Задать для варианта приложения дизассемблирования значение Intel",
+ "text": "-gdb-set disassembly-flavor intel",
+ "ignoreFailures": true
+ }
+ ],
+ "preLaunchTask": "C/C++: g++ сборка активного файла",
+ "miDebuggerPath": "/usr/bin/gdb"
+ }
+ ],
+ "version": "2.0.0"
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..ecf7319
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,9 @@
+{
+ "files.associations": {
+ ".fantomasignore": "ignore",
+ "*.py": "python",
+ "regex": "cpp",
+ "string_view": "cpp",
+ "algorithm": "cpp"
+ }
+}
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000..4c665ba
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,25 @@
+{
+ "tasks": [
+ {
+ "type": "cppbuild",
+ "label": "C/C++: g++ сборка активного файла",
+ "command": "/usr/bin/g++",
+ "args": [
+ "-fdiagnostics-color=always",
+ "-g",
+ "${file}",
+ "-o",
+ "${fileDirname}/${fileBasenameNoExtension}"
+ ],
+ "options": {
+ "cwd": "${fileDirname}"
+ },
+ "problemMatcher": [
+ "$gcc"
+ ],
+ "group": "build",
+ "detail": "Задача создана отладчиком."
+ }
+ ],
+ "version": "2.0.0"
+}
\ No newline at end of file
diff --git a/Client/index.html b/Client/index.html
index 10b1042..0ff031d 100644
--- a/Client/index.html
+++ b/Client/index.html
@@ -47,8 +47,8 @@
-
-
SpCloud support only rar
+
+
SpCloud support only zipped files
@@ -69,8 +69,8 @@
-
-
SpCloud support only rar
+
+
SpCloud support only zipped files
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..c624687
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,20 @@
+# Используйте официальный образ GCC в качестве базового
+FROM gcc:13
+
+# Установите CMake и другие зависимости
+RUN apt-get update && \
+ apt-get install -y cmake make
+
+EXPOSE 8081
+# Создайте директорию для вашего проекта
+WORKDIR /app
+
+# Скопируйте исходный код в контейнер
+COPY . /app
+
+# Builing it
+RUN cmake .
+RUN cmake --build .
+WORKDIR /app/SpCloudMain
+# Укажите команду для запуска вашего приложения
+CMD ["./SpCloudMain"]
diff --git a/SpCloudMain/CMakeLists.txt b/SpCloudMain/CMakeLists.txt
index ffe4bb5..645d29a 100644
--- a/SpCloudMain/CMakeLists.txt
+++ b/SpCloudMain/CMakeLists.txt
@@ -13,7 +13,9 @@ add_executable(SpCloudMain
"Service/DiscordService.cpp"
"Service/MongoDbService.cpp"
"Service/Logger.cpp"
- "Models/User.cpp" "Models/App.cpp")
+ "Models/User.cpp"
+ "Models/App.cpp"
+ )
if (CMAKE_VERSION VERSION_GREATER 3.12)
set_property(TARGET SpCloudMain PROPERTY CXX_STANDARD 20)
diff --git a/SpCloudMain/Controllers/PublishController.cpp b/SpCloudMain/Controllers/PublishController.cpp
index 63d7a54..c5dcf14 100644
--- a/SpCloudMain/Controllers/PublishController.cpp
+++ b/SpCloudMain/Controllers/PublishController.cpp
@@ -36,10 +36,10 @@ public:
const auto& filename = this->publish_app_path + req.files.begin()->second.filename;
- if (filename.size() >= 4 && filename.substr(filename.size() - 4) == ".rar") {
+ if (filename.size() >= 4) {
if (file_processing->save_file(filename, content)) {
- std::string app_final_file_path = app->get_name() + app->get_user_id();
+ std::string app_final_file_path = app->get_name() + "_" + app->get_user_id();
logger_.log(INFO, "app_final_file_path: " + app_final_file_path);
@@ -49,12 +49,9 @@ public:
{
check_port_and_increase_if_not_available();
- file_processing->adjust_nginx_configuration_and_reloud(app->get_name(), std::to_string(last_available_port));//
+ file_processing->adjust_nginx_configuration_and_reload(app->get_name(), std::to_string(last_available_port));//
- file_processing->create_service_file_dotnet(this->publish_app_path, app_final_file_path,
- std::to_string(last_available_port), true);
-
- //this->dotnet_publish(this->publish_app_path + app_final_file_path, last_available_port);//Test
+ this->dotnet_publish(this->publish_app_path + app_final_file_path, app_final_file_path, last_available_port);//Test
app->set_url("https://" + app->get_name() + ".almavid.ru/");
@@ -63,10 +60,7 @@ public:
if (app->get_target() == "dotnet")
{
- file_processing->create_service_file_dotnet(this->publish_app_path, app_final_file_path,
- std::to_string(last_available_port), false);
-
- //this->dotnet_publish(this->publish_app_path + app_final_file_path);//Test
+ this->dotnet_publish(this->publish_app_path + app_final_file_path, app_final_file_path);
app->set_url("Worker Service");
@@ -94,27 +88,33 @@ public:
const auto& content = req.files.begin()->second.content;
const auto& filename = this->publish_app_path + req.files.begin()->second.filename;
+
+
+ if (file_processing->save_file(filename, content)) {
+
+ std::string app_final_file_path = app->get_name() + app->get_user_id();
- if (filename.size() >= 4 && filename.substr(filename.size() - 4) == ".rar") {
- if (file_processing->save_file(filename, content)) {
+ logger_.log(INFO, "app_final_file_path: " + app_final_file_path);
- std::string app_final_file_path = app->get_name() + app->get_user_id();
+ file_processing->delete_file(this->publish_app_path + app_final_file_path);
- logger_.log(INFO, "app_final_file_path: " + app_final_file_path);
-
- file_processing->delete_file(this->publish_app_path + app_final_file_path);
-
- file_processing->unzip(filename, this->publish_app_path + app_final_file_path);
-
- file_processing->stop_and_start_service_file(app_final_file_path);
-
- file_processing->delete_file(filename);
- }
- else {
- return "Invalid file type. Only .rar files are allowed." + filename;
+ file_processing->unzip(filename, this->publish_app_path + app_final_file_path);
+ file_processing->stop_service_file(app_final_file_path);
+
+ if (app->get_target() == "dotnet network") {
+ check_port_and_increase_if_not_available();
+ this->dotnet_publish(this->publish_app_path + app_final_file_path, app_final_file_path, last_available_port);
+ }else {
+ this->dotnet_publish(this->publish_app_path + app_final_file_path, app_final_file_path);
}
+
+ file_processing->delete_file(filename);
}
+ else {
+ return "Invalid file type. Only .rar files are allowed." + filename;
+ }
+
return "Success";
}
@@ -125,48 +125,38 @@ public:
logger_.log(INFO, "app_final_file_path: " + app_final_file_path);
+ file_processing->stop_service_file(app_final_file_path);
+
file_processing->delete_file(this->publish_app_path + app_final_file_path);
file_processing->delete_file("/etc/systemd/system/" + app_final_file_path + ".service");
file_processing->remove_nginx_configuration_block_and_reload(app->get_name());
- file_processing->stop_service_file(app_final_file_path);
-
return "Success";
}
private:
- void dotnet_publish(const std::string& path, int port)
+ void dotnet_publish(const std::string& path, const std::string& folderName, int port)
{
- std::string dll_file_name = file_processing->find_file_by_suffix(path, "exe");
- size_t pos = dll_file_name.find(".exe");
- if (pos != std::string::npos) {
- dll_file_name.replace(pos, 4, ".dll");
- }
+ std::thread commandThreadBuild(&CommandService::execute_command, "docker build -t " + folderName + ".");
+ std::thread commandThreadRun(&CommandService::execute_command, "docker run -d -e ASPNETCORE_URLS=http://0.0.0.0:" + std::to_string(port) + "-p" + std::to_string(port)+":"+std::to_string(port) + "--name " + folderName + " " + folderName);
- std::string command = R"(dotnet )" + path + "/" + dll_file_name + " --urls http://localhost:" + std::to_string(port);
+ logger_.log(INFO, "docker running container : " + folderName + " with port : " + std::to_string(port));
- logger_.log(INFO, "dotnet_publish command : " + command);
-
- std::thread commandThread(&CommandService::execute_command, command);
-
- commandThread.detach();
+ commandThreadBuild.detach();
+ commandThreadRun.detach();
}
- void dotnet_publish(const std::string& path)//Todo test publishing not network app
+ void dotnet_publish(const std::string& path, const std::string& folderName)
{
- std::string dll_file_name = file_processing->find_file_by_suffix(path, "exe");
- size_t pos = dll_file_name.find(".exe");
- if (pos != std::string::npos) {
- dll_file_name.replace(pos, 4, ".dll");
- }
+ std::thread commandThreadBuild(&CommandService::execute_command, "docker build -t " + folderName + ".");
+ std::thread commandThreadRun(&CommandService::execute_command, "docker run -d --name " + folderName + " " + folderName);
- std::string command = R"(dotnet )" + path + "/" + dll_file_name;
+ logger_.log(INFO, "docker running container : " + folderName);
- std::thread commandThread(&CommandService::execute_command, command);
-
- commandThread.detach();
+ commandThreadBuild.detach();
+ commandThreadRun.detach();
}
void check_port_and_increase_if_not_available()
diff --git a/SpCloudMain/Service/FileProcessingService.cpp b/SpCloudMain/Service/FileProcessingService.cpp
index f527f4c..d8ac153 100644
--- a/SpCloudMain/Service/FileProcessingService.cpp
+++ b/SpCloudMain/Service/FileProcessingService.cpp
@@ -26,7 +26,7 @@ public:
}
- void adjust_nginx_configuration_and_reloud(const std::string& filename, std::string port)
+ void adjust_nginx_configuration_and_reload(const std::string& filename, std::string port)
{
std::lock_guard lock(nginx_config_mutex);
@@ -86,7 +86,7 @@ public:
file_out << temp_content;
file_out.close();
- std::string command = "sudo systemctl reload nginx";
+ std::string command = "cd ~/SpCloud/Infrastructure/ && docker compose -f nginx.yml up --build -d ";
std::thread commandThread(&CommandService::execute_command, command);
@@ -156,7 +156,7 @@ public:
outFile.close();
- std::string command = "sudo systemctl reload nginx";
+ std::string command = "cd ~/SpCloud/Infrastructure/ && docker compose -f nginx.yml up --build -d ";
std::thread commandThread(&CommandService::execute_command, command);
@@ -207,81 +207,25 @@ public:
void stop_and_start_service_file(std::string name)
{
- std::string command_stop = "sudo systemctl stop " + name + ".service";
+ std::string command_reload = "docker stop " + name;
- std::string command_start = "sudo systemctl start " + name + ".service";
+ std::string response_reload = execute_and_log_command(command_reload);
- std::string response_reload = execute_and_log_command(command_stop);
-
- std::string response_enable = execute_and_log_command(command_start);
}
+ //Outdated
void stop_service_file(std::string name)
{
- std::string command_stop = "sudo systemctl stop " + name + ".service";
+ std::string command_stop = "docker restart " + name;
std::string response_reload = execute_and_log_command(command_stop);
}
void create_service_file_dotnet(std::string path, std::string name, std::string port, bool is_asp)
{
- logger_.log(INFO, "Start create_service_file_dotnet");
+ logger_.log(INFO, "Outdated. Please didnt use create_service_file_dotnet");
- std::string dll_file_name = find_file_by_suffix(path + "/" + name, "exe");
-
- size_t pos = dll_file_name.find(".exe");
-
- if (pos != std::string::npos) {
-
- dll_file_name.replace(pos, 4, ".dll");
- }
-
- 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";
-
- std::string exec_start_command = "/usr/bin/dotnet /home/danilt2000/SpCloud/" + name + "/" + dll_file_name;
- logger_.log(INFO, "ExecStart command: " + exec_start_command);
- logger_.log(INFO, "ExecStart create_service_file_dotnet");
-
- serviceFile << "[Service]\n";
- serviceFile << "ExecStart=" << exec_start_command << "\n";
- serviceFile << "WorkingDirectory=/home/danilt2000/SpCloud/" + name + "\n";
- serviceFile << "Restart=always\n";
- serviceFile << "User=danilt2000\n";
-
- if (is_asp)
- {
- serviceFile << "Environment=ASPNETCORE_URLS=http://0.0.0.0:" + port + "\n";
- }
-
-
- serviceFile << "Environment=PATH=/usr/bin\n";
- serviceFile << "Environment=NODE_ENV=production\n\n";
-
- serviceFile << "[Install]\n";
- serviceFile << "WantedBy=multi-user.target\n";
-
- serviceFile.close();
-
- std::string command_reload = "sudo systemctl daemon-reload";
- std::string command_enable = "sudo systemctl enable " + name + ".service";
- std::string command_start = "sudo systemctl start " + name + ".service";
-
- std::string response_reload = execute_and_log_command(command_reload);
- std::string response_enable = execute_and_log_command(command_enable);
- std::string response_start = execute_and_log_command(command_start);
-
-
- logger_.log(INFO, "Service file " + filename + " created successfully.\n");
- }
- else {
- logger_.log(INFO, "Unable to open file " + filename + " for writing: " + strerror(errno) + "\n");
- }
+
}
std::string execute_and_log_command(const std::string& command) {
std::string result = execute_command(command);
@@ -308,8 +252,13 @@ public:
void unzip(const std::string& file_path, const std::string& final_files_directory) {
create_directory(final_files_directory);
- std::string command = "unrar x " + file_path + " " + final_files_directory;
+ std::string command = "";
+ if (std::filesystem::path(file_path).extension() == ".rar") {
+ command = "unrar x " + file_path + " " + final_files_directory;
+ }else {
+ command = "tar -xzf " + file_path + " -C " + final_files_directory;
+ }
logger_.log(INFO, "Start unzip command" + command);
std::thread commandThread(&CommandService::execute_command, command);
@@ -319,7 +268,7 @@ public:
std::string find_file_by_suffix(const std::string& directory, const std::string& suffix) {
for (const auto& entry : std::filesystem::recursive_directory_iterator(directory)) {
- if (entry.is_regular_file() && entry.path().filename().string().ends_with(suffix)) {
+ if (entry.is_regular_file() && std::filesystem::path(entry.path().filename()).extension() == suffix) {
return entry.path().filename().string();
}
}
diff --git a/SpCloudMain/Service/MongoDbService.cpp b/SpCloudMain/Service/MongoDbService.cpp
index 2a830d3..7038305 100644
--- a/SpCloudMain/Service/MongoDbService.cpp
+++ b/SpCloudMain/Service/MongoDbService.cpp
@@ -331,6 +331,29 @@ public:
return response;
}
+ std::string get_app(std::string name)//Todo test this method
+ {
+ 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/find' "
+ "--header 'Content-Type: application/json' "
+ "--header 'api-key: " + std::string(std::getenv("MongoDbApiKey")) + "' "
+ "--data-raw '" + json_data + "'";
+
+ auto request = std::async(std::launch::async, &MongoDbService::execute_command, this, command);
+
+ std::string response = request.get();
+
+ return response;
+ }
+
std::string get_app_list(std::string user_id)//Todo test this method
{
std::string json_data = R"({
diff --git a/SpCloudMain/SpCloudMain.cpp b/SpCloudMain/SpCloudMain.cpp
index d1812b7..6fcfa8d 100644
--- a/SpCloudMain/SpCloudMain.cpp
+++ b/SpCloudMain/SpCloudMain.cpp
@@ -6,6 +6,8 @@
#include "Service/DiscordService.cpp"
#include "Service/MongoDbService.cpp"
#include "Models/App.cpp"
+#include "json.hpp"
+#include
using namespace std;
@@ -62,10 +64,10 @@ int main()
std::string user_id = req.get_file_value("UserId").content;
std::string name = req.get_file_value("Name").content;
+
if (name.empty() || name == " ")
{
res.set_content("Select another app name", "text/plain");
-
return;
}
@@ -146,8 +148,8 @@ int main()
return;
}
-
- App* app = new App(name, user_id, "url", "local_url", "target", "service_name");
+ nlohmann::json json_data = nlohmann::json::parse(mongo_service.get_app(name));
+ App* app = new App(name, user_id, json_data["url"], json_data["url_on_local_machine"], json_data["target"], json_data["service_name"]);
publish_controller.process_update(req, app);
diff --git a/SpCloudMain/json.hpp b/SpCloudMain/json.hpp
new file mode 100644
index 0000000..8b72ea6
--- /dev/null
+++ b/SpCloudMain/json.hpp
@@ -0,0 +1,24765 @@
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.3
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann
+// SPDX-License-Identifier: MIT
+
+/****************************************************************************\
+ * Note on documentation: The source files contain links to the online *
+ * documentation of the public API at https://json.nlohmann.me. This URL *
+ * contains the most recent documentation and should also be applicable to *
+ * previous versions; documentation for deprecated functions is not *
+ * removed, but marked deprecated. See "Generate documentation" section in *
+ * file docs/README.md. *
+\****************************************************************************/
+
+#ifndef INCLUDE_NLOHMANN_JSON_HPP_
+#define INCLUDE_NLOHMANN_JSON_HPP_
+
+#include // all_of, find, for_each
+#include // nullptr_t, ptrdiff_t, size_t
+#include // hash, less
+#include // initializer_list
+#ifndef JSON_NO_IO
+ #include // istream, ostream
+#endif // JSON_NO_IO
+#include // random_access_iterator_tag
+#include // unique_ptr
+#include // string, stoi, to_string
+#include // declval, forward, move, pair, swap
+#include // vector
+
+// #include
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.3
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann
+// SPDX-License-Identifier: MIT
+
+
+
+#include
+
+// #include
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.3
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann
+// SPDX-License-Identifier: MIT
+
+
+
+// This file contains all macro definitions affecting or depending on the ABI
+
+#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK
+ #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH)
+ #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 3
+ #warning "Already included a different version of the library!"
+ #endif
+ #endif
+#endif
+
+#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum)
+#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum)
+#define NLOHMANN_JSON_VERSION_PATCH 3 // NOLINT(modernize-macro-to-enum)
+
+#ifndef JSON_DIAGNOSTICS
+ #define JSON_DIAGNOSTICS 0
+#endif
+
+#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+ #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
+#endif
+
+#if JSON_DIAGNOSTICS
+ #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
+#else
+ #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS
+#endif
+
+#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+ #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp
+#else
+ #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
+#endif
+
+#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
+ #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
+#endif
+
+// Construct the namespace ABI tags component
+#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b
+#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \
+ NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b)
+
+#define NLOHMANN_JSON_ABI_TAGS \
+ NLOHMANN_JSON_ABI_TAGS_CONCAT( \
+ NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \
+ NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON)
+
+// Construct the namespace version component
+#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
+ _v ## major ## _ ## minor ## _ ## patch
+#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \
+ NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch)
+
+#if NLOHMANN_JSON_NAMESPACE_NO_VERSION
+#define NLOHMANN_JSON_NAMESPACE_VERSION
+#else
+#define NLOHMANN_JSON_NAMESPACE_VERSION \
+ NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \
+ NLOHMANN_JSON_VERSION_MINOR, \
+ NLOHMANN_JSON_VERSION_PATCH)
+#endif
+
+// Combine namespace components
+#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b
+#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \
+ NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b)
+
+#ifndef NLOHMANN_JSON_NAMESPACE
+#define NLOHMANN_JSON_NAMESPACE \
+ nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \
+ NLOHMANN_JSON_ABI_TAGS, \
+ NLOHMANN_JSON_NAMESPACE_VERSION)
+#endif
+
+#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN
+#define NLOHMANN_JSON_NAMESPACE_BEGIN \
+ namespace nlohmann \
+ { \
+ inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \
+ NLOHMANN_JSON_ABI_TAGS, \
+ NLOHMANN_JSON_NAMESPACE_VERSION) \
+ {
+#endif
+
+#ifndef NLOHMANN_JSON_NAMESPACE_END
+#define NLOHMANN_JSON_NAMESPACE_END \
+ } /* namespace (inline namespace) NOLINT(readability/namespace) */ \
+ } // namespace nlohmann
+#endif
+
+// #include
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.11.3
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann
+// SPDX-License-Identifier: MIT
+
+
+
+#include // transform
+#include // array
+#include // forward_list
+#include // inserter, front_inserter, end
+#include