broken build - [refactor ui start/stop][prefer REPERTORY/REPERTORY_W]
This commit is contained in:
@@ -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{};
|
||||
|
@@ -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>>
|
||||
|
@@ -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_
|
@@ -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);
|
||||
|
||||
|
@@ -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",
|
||||
};
|
||||
|
@@ -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
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user