broken build - [refactor ui start/stop][prefer REPERTORY/REPERTORY_W]
Some checks failed
Blockstorage/repertory/pipeline/head There was a failure building this commit
BlockStorage/repertory/pipeline/head There was a failure building this commit

This commit is contained in:
2025-09-15 10:48:25 -05:00
parent 53b8c70c80
commit f722dd4b84
7 changed files with 355 additions and 290 deletions

View File

@@ -447,7 +447,7 @@ auto fuse_base::mount([[maybe_unused]] std::vector<std::string> orig_args,
return -1;
}
orig_args[0U] = utils::path::combine(".", {"repertory"});
orig_args[0U] = utils::path::combine(".", {REPERTORY});
orig_args.insert(std::next(orig_args.begin()), "-f");
utils::plist_cfg cfg{};

View File

@@ -36,9 +36,9 @@ private:
private:
std::atomic<bool> animations_{true};
utils::atomic<std::string> api_password_{"repertory"};
utils::atomic<std::string> api_password_{REPERTORY};
std::atomic<std::uint16_t> api_port_{default_ui_mgmt_port};
utils::atomic<std::string> api_user_{"repertory"};
utils::atomic<std::string> api_user_{REPERTORY};
std::atomic<bool> auto_start_{true};
std::unordered_map<provider_type,
std::unordered_map<std::string, std::string>>

View File

@@ -19,16 +19,17 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef REPERTORY_INCLUDE_UI_HANDLERS_HPP_
#define REPERTORY_INCLUDE_UI_HANDLERS_HPP_
#ifndef REPERTORY_INCLUDE_UI_SERVER_HPP_
#define REPERTORY_INCLUDE_UI_SERVER_HPP_
#include "events/consumers/console_consumer.hpp"
#include "events/consumers/logging_consumer.hpp"
#include "utils/common.hpp"
namespace repertory::ui {
class mgmt_app_config;
class handlers final {
class ui_server final {
private:
static constexpr auto nonce_length{128U};
static constexpr auto nonce_timeout{15U};
@@ -44,21 +45,21 @@ private:
};
public:
handlers(mgmt_app_config *config, httplib::Server *server);
ui_server(mgmt_app_config *config);
handlers() = delete;
handlers(const handlers &) = delete;
handlers(handlers &&) = delete;
ui_server() = delete;
ui_server(const ui_server &) = delete;
ui_server(ui_server &&) = delete;
~handlers();
~ui_server();
auto operator=(const handlers &) -> handlers & = delete;
auto operator=(handlers &&) -> handlers & = delete;
auto operator=(const ui_server &) -> ui_server & = delete;
auto operator=(ui_server &&) -> ui_server & = delete;
private:
mgmt_app_config *config_;
logging_consumer logging;
std::string repertory_binary_;
httplib::Server *server_;
private:
console_consumer console;
@@ -68,6 +69,7 @@ private:
std::unordered_map<std::string, nonce_data> nonce_lookup_;
std::condition_variable nonce_notify_;
std::unique_ptr<std::thread> nonce_thread_;
httplib::Server server_;
stop_type stop_requested{false};
mutable std::mutex test_mtx_;
@@ -127,12 +129,19 @@ private:
[[nodiscard]] auto mount(provider_type prov, std::string_view name,
const std::string &location) -> bool;
void notify_and_unlock(unique_mutex_lock &nonce_lock);
void removed_expired_nonces();
void set_key_value(provider_type prov, std::string_view name,
std::string_view key, std::string_view value,
std::optional<std::string> data_dir = std::nullopt) const;
public:
void start();
void stop();
};
} // namespace repertory::ui
#endif // REPERTORY_INCLUDE_UI_HANDLERS_HPP_
#endif // REPERTORY_INCLUDE_UI_SERVER_HPP_

View File

@@ -26,8 +26,8 @@
#include "cli/actions.hpp"
#include "initialize.hpp"
#include "types/repertory.hpp"
#include "ui/handlers.hpp"
#include "ui/mgmt_app_config.hpp"
#include "ui/ui_server.hpp"
#include "utils/cli_utils.hpp"
#include "utils/polling.hpp"
@@ -80,18 +80,51 @@ auto main(int argc, char **argv) -> int {
if (not utils::file::change_to_process_directory()) {
ret = static_cast<std::int32_t>(exit_code::ui_failed);
} else {
httplib::Server server;
#if defined(__APPLE__)
if (not server.set_mount_point("/ui", "../Resources/web")) {
#else // !defined(__APPLE__)
if (not server.set_mount_point("/ui", "./web")) {
#endif // defined(__APPLE__)
ret = static_cast<std::int32_t>(exit_code::ui_failed);
} else {
const auto run_ui = [](auto *server) {
REPERTORY_USES_FUNCTION_NAME();
static std::atomic<ui::server *> active_server{server};
static const auto quit_handler = [](int /* sig */) {
REPERTORY_USES_FUNCTION_NAME();
auto *ptr = active_server.exchange(nullptr);
if (ptr == nullptr) {
return;
}
try {
ptr->stop();
} catch (const std::exception &ex) {
utils::error::raise_error(function_name, ex, "failed to stop ui");
}
};
std::signal(SIGINT, quit_handler);
#if !defined(_WIN32)
std::signal(SIGQUIT, quit_handler);
#endif // !defined(_WIN32)
std::signal(SIGTERM, quit_handler);
try {
ptr->start();
} catch (const std::exception &ex) {
utils::error::raise_error(function_name, ex, "failed to start ui");
}
try {
ptr->stop();
} catch (const std::exception &ex) {
utils::error::raise_error(function_name, ex, "failed to stop ui");
}
repertory::project_cleanup();
};
#if defined(_WIN32)
ui::handlers handlers(&config, &server);
ui::server server(&config);
run_ui(&server);
#else // !defined(_WIN32)
project_cleanup();
repertory::project_cleanup();
ret = utils::create_daemon([&]() -> int {
if (not repertory::project_initialize()) {
@@ -103,13 +136,12 @@ auto main(int argc, char **argv) -> int {
return -1;
}
ui::handlers handlers(&config, &server);
project_cleanup();
ui::server server(&config);
run_ui(&server);
return 0;
});
#endif // defined(_WIN32)
}
}
} else {
auto prov = utils::cli::get_provider_type_from_args(args);

View File

@@ -249,11 +249,11 @@ void mgmt_app_config::set_auto_start(bool auto_start) {
#if defined(__linux__)
if (auto_start) {
utils::autostart_cfg cfg{};
cfg.app_name = "repertory";
cfg.app_name = REPERTORY;
cfg.comment = "Mount utility for AWS S3 and Sia";
cfg.exec_args = {"-ui", "-lo"};
cfg.exec_path = utils::path::combine(".", {"repertory"});
cfg.icon_path = utils::path::combine(".", {"repertory.png"});
cfg.exec_path = utils::path::combine(".", {REPERTORY});
cfg.icon_path = utils::path::combine(".", {REPERTORY ".png"});
cfg.terminal = true;
if (utils::create_autostart_entry(cfg, false)) {
@@ -264,7 +264,7 @@ void mgmt_app_config::set_auto_start(bool auto_start) {
function_name, utils::get_last_error_code(),
"failed to create auto-start entry|name|repertory");
}
} else if (utils::remove_autostart_entry("repertory")) {
} else if (utils::remove_autostart_entry(REPERTORY)) {
utils::error::handle_info(function_name,
"removed auto-start entry|name|repertory");
} else {
@@ -278,9 +278,9 @@ void mgmt_app_config::set_auto_start(bool auto_start) {
if (auto_start) {
utils::shortcut_cfg cfg{};
cfg.arguments = L"-ui -lo --hidden";
cfg.exe_path = utils::path::combine(L".", {L"repertory"});
cfg.exe_path = utils::path::combine(L".", {REPERTORY_W});
cfg.icon_path = utils::path::combine(L".", {L"icon.ico"});
cfg.shortcut_name = L"repertory";
cfg.shortcut_name = REPERTORY_W;
cfg.working_directory = utils::path::absolute(L".");
if (utils::create_shortcut(cfg, false)) {
@@ -291,7 +291,7 @@ void mgmt_app_config::set_auto_start(bool auto_start) {
function_name, utils::get_last_error_code(),
"failed to create auto-start entry|name|repertory");
}
} else if (utils::remove_shortcut(L"repertory")) {
} else if (utils::remove_shortcut(REPERTORY_W)) {
utils::error::handle_info(function_name,
"removed auto-start entry|name|repertory");
} else {
@@ -307,7 +307,7 @@ void mgmt_app_config::set_auto_start(bool auto_start) {
if (auto_start) {
utils::plist_cfg cfg{};
cfg.args = {
utils::path::combine(".", {"repertory"}),
utils::path::combine(".", {REPERTORY}),
"-ui",
"-lo",
};

View File

@@ -19,9 +19,11 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "ui/handlers.hpp"
#include "ui/ui_server.hpp"
#include "app_config.hpp"
#include "events/consumers/console_consumer.hpp"
#include "events/consumers/logging_consumer.hpp"
#include "events/event_system.hpp"
#include "platform/platform.hpp"
#include "types/repertory.hpp"
@@ -120,43 +122,27 @@ namespace {
} // namespace
namespace repertory::ui {
handlers::handlers(mgmt_app_config *config, httplib::Server *server)
ui_server::ui_server(mgmt_app_config *config)
: config_(config),
logging(config->get_event_level(),
utils::path::combine(app_config::get_root_data_directory(),
{
"ui",
"logs",
})),
#if defined(_WIN32)
repertory_binary_(utils::path::combine(".", {"repertory.exe"})),
repertory_binary_(utils::path::combine(".", {REPERTORY ".exe"}))
#else // !defined(_WIN32)
repertory_binary_(utils::path::combine(".", {"repertory"})),
repertory_binary_(utils::path::combine(".", {REPERTORY}))
#endif // defined(_WIN32)
server_(server) {
REPERTORY_USES_FUNCTION_NAME();
{
#if defined(__APPLE__)
server_.set_mount_point("/ui", "../Resources/web");
#else // !defined(__APPLE__)
server_.set_mount_point("/ui", "./web");
#endif // defined(__APPLE__)
#if defined(_WIN32)
if (config_->get_hidden()) {
::ShowWindow(::GetConsoleWindow(), SW_HIDE);
}
#endif // defined(_WIN32)
if (not config_->get_launch_only()) {
#if defined(_WIN32)
system(
fmt::format(
R"(start "Repertory Management Portal" "http://127.0.0.1:{}/ui")",
config_->get_api_port())
.c_str());
#elif defined(__linux__)
system(fmt::format(R"(xdg-open "http://127.0.0.1:{}/ui")",
config_->get_api_port())
.c_str());
#elif defined(__APPLE__)
system(
fmt::format(R"(open "http://127.0.0.1:{}/ui")", config_->get_api_port())
.c_str());
#else
build fails here
#endif
}
server_->set_socket_options([](auto &&sock) {
server_.set_socket_options([](auto &&sock) {
#if defined(_WIN32)
BOOL enable = TRUE;
::setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
@@ -176,7 +162,7 @@ handlers::handlers(mgmt_app_config *config, httplib::Server *server)
#endif
});
server_->set_pre_routing_handler(
server_.set_pre_routing_handler(
[this](const httplib::Request &req,
auto &&res) -> httplib::Server::HandlerResponse {
if (req.path == "/api/v1/nonce" || req.path == "/ui" ||
@@ -190,7 +176,7 @@ handlers::handlers(mgmt_app_config *config, httplib::Server *server)
auth, fmt::format("{}_", config_->get_api_user()))) {
auto nonce = auth.substr(config_->get_api_user().length() + 1U);
mutex_lock lock(nonce_mtx_);
mutex_lock nonce_lock(nonce_mtx_);
if (nonce_lookup_.contains(nonce)) {
nonce_lookup_.erase(nonce);
return httplib::Server::HandlerResponse::Unhandled;
@@ -201,9 +187,11 @@ handlers::handlers(mgmt_app_config *config, httplib::Server *server)
return httplib::Server::HandlerResponse::Handled;
});
server_->set_exception_handler([](const httplib::Request &req,
server_.set_exception_handler([](const httplib::Request &req,
httplib::Response &res,
std::exception_ptr ptr) {
REPERTORY_USES_FUNCTION_NAME();
json data{
{"path", req.path},
};
@@ -227,196 +215,71 @@ handlers::handlers(mgmt_app_config *config, httplib::Server *server)
: http_error_codes::internal_error;
});
server->Get("/api/v1/locations", [](auto && /* req */, auto &&res) {
server_.Get("/api/v1/locations", [](auto && /* req */, auto &&res) {
handle_get_available_locations(res);
});
server->Get("/api/v1/mount",
server_.Get("/api/v1/mount",
[this](auto &&req, auto &&res) { handle_get_mount(req, res); });
server->Get("/api/v1/mount_location", [this](auto &&req, auto &&res) {
server_.Get("/api/v1/mount_location", [this](auto &&req, auto &&res) {
handle_get_mount_location(req, res);
});
server->Get("/api/v1/mount_list", [](auto && /* req */, auto &&res) {
server_.Get("/api/v1/mount_list", [](auto && /* req */, auto &&res) {
handle_get_mount_list(res);
});
server->Get("/api/v1/mount_status", [this](auto &&req, auto &&res) {
server_.Get("/api/v1/mount_status", [this](auto &&req, auto &&res) {
handle_get_mount_status(req, res);
});
server->Get("/api/v1/nonce",
server_.Get("/api/v1/nonce",
[this](auto && /* req */, auto &&res) { handle_get_nonce(res); });
server->Get("/api/v1/settings", [this](auto && /* req */, auto &&res) {
server_.Get("/api/v1/settings", [this](auto && /* req */, auto &&res) {
handle_get_settings(res);
});
server->Get("/api/v1/test",
server_.Get("/api/v1/test",
[this](auto &&req, auto &&res) { handle_get_test(req, res); });
server->Post("/api/v1/add_mount", [this](auto &&req, auto &&res) {
server_.Post("/api/v1/add_mount", [this](auto &&req, auto &&res) {
handle_post_add_mount(req, res);
});
server->Post("/api/v1/mount",
server_.Post("/api/v1/mount",
[this](auto &&req, auto &&res) { handle_post_mount(req, res); });
server->Put("/api/v1/mount_auto_start", [this](auto &&req, auto &&res) {
server_.Put("/api/v1/mount_auto_start", [this](auto &&req, auto &&res) {
handle_put_mount_auto_start(req, res);
});
server->Put("/api/v1/mount_location", [this](auto &&req, auto &&res) {
server_.Put("/api/v1/mount_location", [this](auto &&req, auto &&res) {
handle_put_mount_location(req, res);
});
server->Put("/api/v1/set_value_by_name", [this](auto &&req, auto &&res) {
server_.Put("/api/v1/set_value_by_name", [this](auto &&req, auto &&res) {
handle_put_set_value_by_name(req, res);
});
server->Put("/api/v1/setting",
server_.Put("/api/v1/setting",
[this](auto &&req, auto &&res) { handle_put_setting(req, res); });
server->Put("/api/v1/settings", [this](auto &&req, auto &&res) {
server_.Put("/api/v1/settings", [this](auto &&req, auto &&res) {
handle_put_settings(req, res);
});
static std::atomic<httplib::Server *> this_server{server_};
static const auto quit_handler = [](int /* sig */) {
auto *ptr = this_server.exchange(nullptr);
if (ptr == nullptr) {
return;
}
#if defined(_WIN32)
std::jthread([ptr]() { ptr->stop(); });
#else // !defined(_WIN32)
ptr->stop();
if (config_->get_hidden()) {
::ShowWindow(::GetConsoleWindow(), SW_HIDE);
}
#endif // defined(_WIN32)
};
std::signal(SIGINT, quit_handler);
#if !defined(_WIN32)
std::signal(SIGQUIT, quit_handler);
#endif // !defined(_WIN32)
std::signal(SIGTERM, quit_handler);
event_system::instance().start();
{
auto deadline = std::chrono::steady_clock::now() + 30s;
std::string host{"127.0.0.1"};
auto desired_port = config_->get_api_port();
for (;;) {
if (server_->bind_to_port(host, desired_port)) {
break; // bound successfully; proceed to route registration
}
const auto err = get_last_net_error();
if (!is_addr_in_use(err)) {
utils::error::raise_error(function_name,
utils::error::create_error_message({
"failed to bind",
"host",
host,
"port",
std::to_string(desired_port),
"errno",
std::to_string(err),
}));
return; // abort constructor on non-EADDRINUSE errors
}
ui_server::~ui_server() { stop(); }
if (std::chrono::steady_clock::now() >= deadline) {
utils::error::raise_error(function_name,
utils::error::create_error_message({
"bind timeout (port in use)",
"host",
host,
"port",
std::to_string(desired_port),
}));
return; // abort constructor on timeout
}
std::this_thread::sleep_for(250ms);
}
std::thread([this]() {
for (const auto &[prov, names] : config_->get_auto_start_list()) {
for (const auto &name : names) {
try {
auto location = config_->get_mount_location(prov, name);
if (location.empty()) {
utils::error::raise_error(function_name,
utils::error::create_error_message({
"failed to auto-mount",
"provider",
provider_type_to_string(prov),
"name",
name,
"location is empty",
}));
} else if (not mount(prov, name, location)) {
utils::error::raise_error(function_name,
utils::error::create_error_message({
"failed to auto-mount",
"provider",
provider_type_to_string(prov),
"name",
name,
"mount failed",
}));
}
} catch (const std::exception &e) {
utils::error::raise_error(function_name, e,
utils::error::create_error_message({
"failed to auto-mount",
"provider",
provider_type_to_string(prov),
"name",
name,
}));
} catch (...) {
utils::error::raise_error(function_name, "unknown error",
utils::error::create_error_message({
"failed to auto-mount",
"provider",
provider_type_to_string(prov),
"name",
name,
}));
}
}
}
}).join();
nonce_thread_ =
std::make_unique<std::thread>([this]() { removed_expired_nonces(); });
server_->listen_after_bind();
}
quit_handler(SIGTERM);
}
handlers::~handlers() {
if (nonce_thread_) {
stop_requested = true;
unique_mutex_lock lock(nonce_mtx_);
nonce_notify_.notify_all();
lock.unlock();
nonce_thread_->join();
nonce_thread_.reset();
}
event_system::instance().stop();
}
auto handlers::data_directory_exists(provider_type prov,
auto ui_server::data_directory_exists(provider_type prov,
std::string_view name) const -> bool {
auto data_dir = utils::path::combine(app_config::get_root_data_directory(),
{
@@ -428,14 +291,14 @@ auto handlers::data_directory_exists(provider_type prov,
return ret;
}
unique_mutex_lock lock(mtx_);
unique_mutex_lock nonce_lock(mtx_);
mtx_lookup_.erase(
fmt::format("{}-{}", name, app_config::get_provider_name(prov)));
lock.unlock();
nonce_lock.unlock();
return ret;
}
void handlers::generate_config(provider_type prov, std::string_view name,
void ui_server::generate_config(provider_type prov, std::string_view name,
const json &cfg,
std::optional<std::string> data_dir) const {
REPERTORY_USES_FUNCTION_NAME();
@@ -484,7 +347,7 @@ void handlers::generate_config(provider_type prov, std::string_view name,
}
}
void handlers::handle_put_mount_auto_start(const httplib::Request &req,
void ui_server::handle_put_mount_auto_start(const httplib::Request &req,
httplib::Response &res) const {
auto prov = provider_type_from_string(req.get_param_value("type"));
auto name = req.get_param_value("name");
@@ -499,7 +362,7 @@ void handlers::handle_put_mount_auto_start(const httplib::Request &req,
res.status = http_error_codes::ok;
}
void handlers::handle_put_mount_location(const httplib::Request &req,
void ui_server::handle_put_mount_location(const httplib::Request &req,
httplib::Response &res) const {
auto prov = provider_type_from_string(req.get_param_value("type"));
auto name = req.get_param_value("name");
@@ -514,7 +377,7 @@ void handlers::handle_put_mount_location(const httplib::Request &req,
res.status = http_error_codes::ok;
}
void handlers::handle_get_available_locations(httplib::Response &res) {
void ui_server::handle_get_available_locations(httplib::Response &res) {
#if defined(_WIN32)
constexpr std::array<std::string_view, 26U> letters{
"a:", "b:", "c:", "d:", "e:", "f:", "g:", "h:", "i:",
@@ -543,7 +406,7 @@ void handlers::handle_get_available_locations(httplib::Response &res) {
res.status = http_error_codes::ok;
}
void handlers::handle_get_mount(const httplib::Request &req,
void ui_server::handle_get_mount(const httplib::Request &req,
httplib::Response &res) const {
REPERTORY_USES_FUNCTION_NAME();
@@ -572,7 +435,7 @@ void handlers::handle_get_mount(const httplib::Request &req,
res.status = http_error_codes::ok;
}
void handlers::handle_get_mount_list(httplib::Response &res) {
void ui_server::handle_get_mount_list(httplib::Response &res) {
auto data_dir = utils::file::directory{app_config::get_root_data_directory()};
nlohmann::json result;
@@ -601,7 +464,7 @@ void handlers::handle_get_mount_list(httplib::Response &res) {
res.status = http_error_codes::ok;
}
void handlers::handle_get_mount_location(const httplib::Request &req,
void ui_server::handle_get_mount_location(const httplib::Request &req,
httplib::Response &res) const {
auto name = req.get_param_value("name");
auto prov = provider_type_from_string(req.get_param_value("type"));
@@ -620,7 +483,7 @@ void handlers::handle_get_mount_location(const httplib::Request &req,
res.status = http_error_codes::ok;
}
void handlers::handle_get_mount_status(const httplib::Request &req,
void ui_server::handle_get_mount_status(const httplib::Request &req,
httplib::Response &res) const {
auto name = req.get_param_value("name");
auto prov = provider_type_from_string(req.get_param_value("type"));
@@ -646,8 +509,8 @@ void handlers::handle_get_mount_status(const httplib::Request &req,
res.status = http_error_codes::ok;
}
void handlers::handle_get_nonce(httplib::Response &res) {
mutex_lock lock(nonce_mtx_);
void ui_server::handle_get_nonce(httplib::Response &res) {
mutex_lock nonce_lock(nonce_mtx_);
nonce_data nonce{};
nonce_lookup_[nonce.nonce] = nonce;
@@ -657,7 +520,7 @@ void handlers::handle_get_nonce(httplib::Response &res) {
res.status = http_error_codes::ok;
}
void handlers::handle_get_settings(httplib::Response &res) const {
void ui_server::handle_get_settings(httplib::Response &res) const {
auto settings = config_->to_json();
settings[JSON_API_PASSWORD] = "";
settings.erase(JSON_MOUNT_LOCATIONS);
@@ -665,18 +528,18 @@ void handlers::handle_get_settings(httplib::Response &res) const {
res.status = http_error_codes::ok;
}
void handlers::handle_get_test(const httplib::Request &req,
void ui_server::handle_get_test(const httplib::Request &req,
httplib::Response &res) const {
REPERTORY_USES_FUNCTION_NAME();
unique_mutex_lock lock(test_mtx_);
unique_mutex_lock nonce_lock(test_mtx_);
auto name = req.get_param_value("name");
auto prov = provider_type_from_string(req.get_param_value("type"));
auto cfg = nlohmann::json::parse(req.get_param_value("config"));
auto data_dir = utils::path::combine(
utils::directory::temp(), {utils::file::create_temp_name("repertory")});
utils::directory::temp(), {utils::file::create_temp_name(REPERTORY)});
try {
generate_config(prov, name, cfg, data_dir);
@@ -698,7 +561,7 @@ void handlers::handle_get_test(const httplib::Request &req,
fmt::format("failed to remove data directory|{}", data_dir));
}
void handlers::handle_post_add_mount(const httplib::Request &req,
void ui_server::handle_post_add_mount(const httplib::Request &req,
httplib::Response &res) const {
auto name = req.get_param_value("name");
auto prov = provider_type_from_string(req.get_param_value("type"));
@@ -713,7 +576,7 @@ void handlers::handle_post_add_mount(const httplib::Request &req,
res.status = http_error_codes::ok;
}
void handlers::handle_post_mount(const httplib::Request &req,
void ui_server::handle_post_mount(const httplib::Request &req,
httplib::Response &res) {
auto name = req.get_param_value("name");
auto prov = provider_type_from_string(req.get_param_value("type"));
@@ -740,7 +603,7 @@ void handlers::handle_post_mount(const httplib::Request &req,
res.status = http_error_codes::internal_error;
}
void handlers::handle_put_set_value_by_name(const httplib::Request &req,
void ui_server::handle_put_set_value_by_name(const httplib::Request &req,
httplib::Response &res) const {
auto name = req.get_param_value("name");
auto prov = provider_type_from_string(req.get_param_value("type"));
@@ -762,7 +625,7 @@ void handlers::handle_put_set_value_by_name(const httplib::Request &req,
res.status = http_error_codes::ok;
}
void handlers::handle_put_setting(const httplib::Request &req,
void ui_server::handle_put_setting(const httplib::Request &req,
httplib::Response &res) const {
auto name = req.get_param_value("name");
auto value = req.get_param_value("value");
@@ -776,7 +639,7 @@ void handlers::handle_put_setting(const httplib::Request &req,
res.status = http_error_codes::ok;
}
void handlers::handle_put_settings(const httplib::Request &req,
void ui_server::handle_put_settings(const httplib::Request &req,
httplib::Response &res) const {
auto data = nlohmann::json::parse(req.get_param_value("data"));
@@ -800,7 +663,7 @@ void handlers::handle_put_settings(const httplib::Request &req,
res.status = http_error_codes::ok;
}
auto handlers::launch_process(provider_type prov, std::string_view name,
auto ui_server::launch_process(provider_type prov, std::string_view name,
std::vector<std::string> args,
bool background) const
-> std::vector<std::string> {
@@ -840,10 +703,10 @@ auto handlers::launch_process(provider_type prov, std::string_view name,
});
}
unique_mutex_lock lock(mtx_);
unique_mutex_lock nonce_lock(mtx_);
auto &inst_mtx = mtx_lookup_[fmt::format(
"{}-{}", name, app_config::get_provider_name(prov))];
lock.unlock();
nonce_lock.unlock();
recur_mutex_lock inst_lock(inst_mtx);
if (background) {
@@ -912,7 +775,7 @@ auto handlers::launch_process(provider_type prov, std::string_view name,
false);
}
auto handlers::mount(provider_type prov, std::string_view name,
auto ui_server::mount(provider_type prov, std::string_view name,
const std::string &location) -> bool {
#if defined(_WIN32)
if (utils::file::directory{location}.exists()) {
@@ -927,28 +790,33 @@ auto handlers::mount(provider_type prov, std::string_view name,
config_->set_mount_location(prov, name, location);
static std::mutex mount_mtx;
mutex_lock lock(mount_mtx);
mutex_lock nonce_lock(mount_mtx);
launch_process(prov, name, {location}, true);
return true;
}
void handlers::removed_expired_nonces() {
unique_mutex_lock lock(nonce_mtx_);
lock.unlock();
void ui_server::notify_and_unlock(unique_mutex_lock &nonce_lock) {
nonce_notify_.notify_all();
nonce_lock.unlock();
}
void ui_server::removed_expired_nonces() {
unique_mutex_lock nonce_lock(nonce_mtx_);
notify_and_unlock(nonce_lock);
while (not stop_requested) {
lock.lock();
nonce_lock.lock();
auto nonces = nonce_lookup_;
lock.unlock();
notify_and_unlock(nonce_lock);
for (const auto &[key, value] : nonces) {
if (std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now() - value.creation)
.count() >= nonce_timeout) {
lock.lock();
nonce_lock.lock();
nonce_lookup_.erase(key);
lock.unlock();
notify_and_unlock(nonce_lock);
}
}
@@ -956,16 +824,16 @@ void handlers::removed_expired_nonces() {
break;
}
lock.lock();
nonce_lock.lock();
if (stop_requested) {
break;
}
nonce_notify_.wait_for(lock, std::chrono::seconds(1U));
lock.unlock();
nonce_notify_.wait_for(nonce_lock, std::chrono::seconds(1U));
notify_and_unlock(nonce_lock);
}
}
void handlers::set_key_value(provider_type prov, std::string_view name,
void ui_server::set_key_value(provider_type prov, std::string_view name,
std::string_view key, std::string_view value,
std::optional<std::string> data_dir) const {
std::vector<std::string> args;
@@ -978,4 +846,160 @@ void handlers::set_key_value(provider_type prov, std::string_view name,
args.emplace_back(value);
launch_process(prov, name, args, false);
}
void ui_server::start() {
REPERTORY_USES_FUNCTION_NAME();
unique_mutex_lock nonce_lock(nonce_mtx_);
if (nonce_thread_) {
return;
}
stop_requested = false;
event_system::instance().start();
nonce_thread_ =
std::make_unique<std::thread>([this]() { removed_expired_nonces(); });
const auto launch_ui = [this]() {
if (not config_->get_launch_only()) {
#if defined(_WIN32)
system(
fmt::format(
R"(start "Repertory Management Portal" "http://127.0.0.1:{}/ui")",
config_->get_api_port())
.c_str());
#elif defined(__linux__)
system(fmt::format(R"(xdg-open "http://127.0.0.1:{}/ui")",
config_->get_api_port())
.c_str());
#elif defined(__APPLE__)
system(fmt::format(R"(open "http://127.0.0.1:{}/ui")",
config_->get_api_port())
.c_str());
#else
build fails here
#endif
}
};
auto should_launch{true};
lock_data ui_lock(provider_type::unknown, "ui");
auto res = ui_lock.grab_lock(1U);
if (res == lock_result::locked) {
auto deadline{std::chrono::steady_clock::now() + 30s};
std::string host{"127.0.0.1"};
auto desired_port{config_->get_api_port()};
auto success{false};
while (not success && std::chrono::steady_clock::now() >= deadline) {
success = server_.bind_to_port(host, desired_port);
if (success) {
break;
}
utils::error::raise_error(function_name,
utils::error::create_error_message({
"failed to bind",
"host",
host,
"port",
std::to_string(desired_port),
"error",
std::to_string(get_last_net_error()),
}));
std::this_thread::sleep_for(250ms);
}
if (success) {
std::thread([this]() {
for (const auto &[prov, names] : config_->get_auto_start_list()) {
for (const auto &name : names) {
try {
auto location = config_->get_mount_location(prov, name);
if (location.empty()) {
utils::error::raise_error(function_name,
utils::error::create_error_message({
"failed to auto-mount",
"provider",
provider_type_to_string(prov),
"name",
name,
"location is empty",
}));
} else if (not mount(prov, name, location)) {
utils::error::raise_error(function_name,
utils::error::create_error_message({
"failed to auto-mount",
"provider",
provider_type_to_string(prov),
"name",
name,
"mount failed",
}));
}
} catch (const std::exception &e) {
utils::error::raise_error(function_name, e,
utils::error::create_error_message({
"failed to auto-mount",
"provider",
provider_type_to_string(prov),
"name",
name,
}));
} catch (...) {
utils::error::raise_error(function_name, "unknown error",
utils::error::create_error_message({
"failed to auto-mount",
"provider",
provider_type_to_string(prov),
"name",
name,
}));
}
}
}
}).join();
server_.listen_after_bind();
} else {
utils::error::raise_error(function_name,
utils::error::create_error_message({
"bind timeout (port in use)",
"host",
host,
"port",
std::to_string(desired_port),
}));
should_launch = false;
}
}
notify_and_unlock(nonce_lock);
if (not should_launch) {
return;
}
launch_ui();
}
void ui_server::stop() {
unique_mutex_lock nonce_lock(nonce_mtx_);
if (not nonce_thread_) {
notify_and_unlock(nonce_lock);
return;
}
stop_requested = true;
notify_and_unlock(nonce_lock);
nonce_thread_->join();
nonce_lock.lock();
nonce_thread_.reset();
notify_and_unlock(nonce_lock);
event_system::instance().stop();
}
} // namespace repertory::ui

View File

@@ -27,7 +27,7 @@
namespace repertory {
TEST(curl_comm_test, can_create_s3_host_config) {
s3_config config{};
config.bucket = "repertory";
config.bucket = REPERTORY;
config.url = "https://s3.test.com";
config.region = "any";
config.use_path_style = false;
@@ -40,7 +40,7 @@ TEST(curl_comm_test, can_create_s3_host_config) {
TEST(curl_comm_test, can_create_s3_host_config_with_path_style) {
s3_config config{};
config.bucket = "repertory";
config.bucket = REPERTORY;
config.url = "https://s3.test.com";
config.region = "any";
config.use_path_style = true;
@@ -53,7 +53,7 @@ TEST(curl_comm_test, can_create_s3_host_config_with_path_style) {
TEST(curl_comm_test, can_create_s3_host_config_with_region) {
s3_config config{};
config.bucket = "repertory";
config.bucket = REPERTORY;
config.url = "https://s3.test.com";
config.region = "any";
config.use_region_in_url = true;
@@ -67,7 +67,7 @@ TEST(curl_comm_test, can_create_s3_host_config_with_region) {
TEST(curl_comm_test, can_create_s3_host_config_with_region_and_path_style) {
s3_config config{};
config.bucket = "repertory";
config.bucket = REPERTORY;
config.url = "https://s3.test.com";
config.region = "any";
config.use_region_in_url = true;