Compare commits
12 Commits
master
...
eec2d2e9a9
Author | SHA1 | Date | |
---|---|---|---|
eec2d2e9a9 | |||
8c1c91e02b | |||
bb85015733 | |||
883de836c6 | |||
bf2bdd1b5d | |||
f1e82d8f9f | |||
f5c4aebdac | |||
f2f9e8fd15 | |||
2a673915af | |||
df3db38ae7 | |||
b0b69c6dd4 | |||
11b118a30f |
15
CHANGELOG.md
15
CHANGELOG.md
@ -1,7 +1,22 @@
|
||||
# Changelog
|
||||
|
||||
## v2.0.6-release
|
||||
|
||||
### Issues
|
||||
|
||||
* ~~\#12 [Unit Test] Complete all providers unit tests~~
|
||||
* ~~\#21 [Unit Test] Complete WinFSP unit tests~~
|
||||
* ~~\#22 [Unit Test] Complete FUSE unit tests~~
|
||||
* ~~\#33 Complete initial v2.0 documentation~~
|
||||
* \#42 [bug] Remote mount directory listing on Windows connected to Linux is failing
|
||||
|
||||
### Changes from v2.0.5-rc
|
||||
|
||||
* Drive letters in UI should always be lowercase
|
||||
|
||||
## v2.0.5-rc
|
||||
|
||||
<!-- markdownlint-disable-next-line -->
|
||||
### Issues
|
||||
|
||||
* \#39 Create management portal in Flutter
|
||||
|
@ -10,7 +10,7 @@ PROJECT_DESC="Mount utility for Sia and S3"
|
||||
|
||||
PROJECT_MAJOR_VERSION=2
|
||||
PROJECT_MINOR_VERSION=0
|
||||
PROJECT_REVISION_VERSION=5
|
||||
PROJECT_REVISION_VERSION=6
|
||||
PROJECT_RELEASE_NUM=0
|
||||
PROJECT_RELEASE_ITER=rc
|
||||
|
||||
|
@ -59,7 +59,8 @@ private:
|
||||
|
||||
static void populate_stat(const struct stat64 &unix_st, remote::stat &r_stat);
|
||||
|
||||
[[nodiscard]] auto update_to_windows_format(json &item) -> json &;
|
||||
[[nodiscard]] auto update_to_windows_format(const std::string &root_api_path,
|
||||
json &item) -> json &;
|
||||
|
||||
public:
|
||||
// FUSE Layer
|
||||
|
@ -374,13 +374,13 @@ auto fuse_base::init_impl(struct fuse_conn_info *conn) -> void * {
|
||||
if (not utils::file::change_to_process_directory()) {
|
||||
utils::error::raise_error(function_name,
|
||||
"failed to change to process directory");
|
||||
event_system::instance().raise<unmount_requested>();
|
||||
event_system::instance().raise<unmount_requested>(function_name);
|
||||
return this;
|
||||
}
|
||||
|
||||
if (not console_enabled_ && not repertory::project_initialize()) {
|
||||
utils::error::raise_error(function_name, "failed to initialize repertory");
|
||||
event_system::instance().raise<unmount_requested>();
|
||||
event_system::instance().raise<unmount_requested>(function_name);
|
||||
}
|
||||
|
||||
return this;
|
||||
|
@ -259,7 +259,7 @@ auto remote_fuse_drive::init_impl(struct fuse_conn_info *conn) -> void * {
|
||||
if (remote_instance_->fuse_init() != 0) {
|
||||
utils::error::raise_error(function_name,
|
||||
"failed to connect to remote server");
|
||||
event_system::instance().raise<unmount_requested>();
|
||||
event_system::instance().raise<unmount_requested>(function_name);
|
||||
} else {
|
||||
server_ = std::make_shared<server>(config_);
|
||||
server_->start();
|
||||
|
@ -1390,11 +1390,11 @@ auto remote_server::winfsp_read_directory(PVOID file_desc, PWSTR /*pattern*/,
|
||||
|
||||
item_list.clear();
|
||||
|
||||
const auto handle = reinterpret_cast<remote::file_handle>(file_desc);
|
||||
auto handle = reinterpret_cast<remote::file_handle>(file_desc);
|
||||
auto ret = static_cast<packet::error_type>(
|
||||
has_open_info(static_cast<native_handle>(handle), STATUS_INVALID_HANDLE));
|
||||
if (ret == STATUS_SUCCESS) {
|
||||
const auto api_path = construct_api_path(
|
||||
auto api_path = construct_api_path(
|
||||
get_open_file_path(static_cast<native_handle>(handle)));
|
||||
directory_iterator iterator(drive_.get_directory_items(api_path));
|
||||
auto offset = marker == nullptr
|
||||
@ -1405,7 +1405,7 @@ auto remote_server::winfsp_read_directory(PVOID file_desc, PWSTR /*pattern*/,
|
||||
json item;
|
||||
while (iterator.get_json(offset++, item) == 0) {
|
||||
try {
|
||||
item_list.emplace_back(update_to_windows_format(item));
|
||||
item_list.emplace_back(update_to_windows_format(api_path, item));
|
||||
} catch (const std::exception &e) {
|
||||
utils::error::raise_error(function_name, e, "exception occurred");
|
||||
}
|
||||
@ -1679,30 +1679,53 @@ auto remote_server::json_release_directory_snapshot(
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto remote_server::update_to_windows_format(json &item) -> json & {
|
||||
const auto api_path = item["path"].get<std::string>();
|
||||
item["meta"][META_ACCESSED] = std::to_string(
|
||||
utils::string::to_uint64(empty_as_zero(item["meta"][META_ACCESSED])));
|
||||
item["meta"][META_CREATION] = std::to_string(
|
||||
utils::string::to_uint64(empty_as_zero(item["meta"][META_CREATION])));
|
||||
item["meta"][META_MODIFIED] = std::to_string(
|
||||
utils::string::to_uint64(empty_as_zero(item["meta"][META_MODIFIED])));
|
||||
auto remote_server::update_to_windows_format(const std::string &root_api_path,
|
||||
json &item) -> json & {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
if (item["meta"][META_WRITTEN].empty() ||
|
||||
(item["meta"][META_WRITTEN].get<std::string>() == "0") ||
|
||||
(item["meta"][META_WRITTEN].get<std::string>() ==
|
||||
std::to_string(utils::time::WIN32_TIME_CONVERSION))) {
|
||||
drive_.set_item_meta(api_path, META_WRITTEN,
|
||||
item["meta"][META_MODIFIED].get<std::string>());
|
||||
item["meta"][META_WRITTEN] = item["meta"][META_MODIFIED];
|
||||
auto api_path = item[JSON_API_PATH].get<std::string>();
|
||||
if (api_path == "." || api_path == "..") {
|
||||
auto orig_api_path{api_path};
|
||||
|
||||
api_path = api_path == "."
|
||||
? root_api_path
|
||||
: utils::path::get_parent_api_path(root_api_path);
|
||||
|
||||
api_meta_map meta;
|
||||
auto res = drive_.get_item_meta(api_path, meta);
|
||||
if (res != api_error::success) {
|
||||
utils::error::raise_api_path_error(
|
||||
function_name, api_path, res,
|
||||
fmt::format("failed to get '{}' meta", orig_api_path));
|
||||
return item;
|
||||
}
|
||||
|
||||
item[JSON_META] = meta;
|
||||
}
|
||||
|
||||
if (item["meta"][META_ATTRIBUTES].empty()) {
|
||||
item["meta"][META_ATTRIBUTES] =
|
||||
item["directory"].get<bool>() ? std::to_string(FILE_ATTRIBUTE_DIRECTORY)
|
||||
: std::to_string(FILE_ATTRIBUTE_ARCHIVE);
|
||||
item[JSON_META][META_ACCESSED] = std::to_string(
|
||||
utils::string::to_uint64(empty_as_zero(item[JSON_META][META_ACCESSED])));
|
||||
item[JSON_META][META_CREATION] = std::to_string(
|
||||
utils::string::to_uint64(empty_as_zero(item[JSON_META][META_CREATION])));
|
||||
item[JSON_META][META_MODIFIED] = std::to_string(
|
||||
utils::string::to_uint64(empty_as_zero(item[JSON_META][META_MODIFIED])));
|
||||
|
||||
if (item[JSON_META][META_WRITTEN].empty() ||
|
||||
(item[JSON_META][META_WRITTEN].get<std::string>() == "0") ||
|
||||
(item[JSON_META][META_WRITTEN].get<std::string>() ==
|
||||
std::to_string(utils::time::WIN32_TIME_CONVERSION))) {
|
||||
drive_.set_item_meta(api_path, META_WRITTEN,
|
||||
item[JSON_META][META_MODIFIED].get<std::string>());
|
||||
item[JSON_META][META_WRITTEN] = item[JSON_META][META_MODIFIED];
|
||||
}
|
||||
|
||||
if (item[JSON_META][META_ATTRIBUTES].empty()) {
|
||||
item[JSON_META][META_ATTRIBUTES] =
|
||||
item[JSON_DIRECTORY].get<bool>()
|
||||
? std::to_string(FILE_ATTRIBUTE_DIRECTORY)
|
||||
: std::to_string(FILE_ATTRIBUTE_ARCHIVE);
|
||||
drive_.set_item_meta(api_path, META_ATTRIBUTES,
|
||||
item["meta"][META_ATTRIBUTES].get<std::string>());
|
||||
item[JSON_META][META_ATTRIBUTES].get<std::string>());
|
||||
}
|
||||
|
||||
return item;
|
||||
|
@ -361,11 +361,10 @@ auto remote_winfsp_drive::ReadDirectory(PVOID /*file_node*/, PVOID file_desc,
|
||||
&ret)) {
|
||||
auto item_found = false;
|
||||
for (const auto &item : item_list) {
|
||||
auto item_path = item["path"].get<std::string>();
|
||||
auto item_path = item[JSON_API_PATH].get<std::string>();
|
||||
auto display_name = utils::string::from_utf8(
|
||||
utils::path::strip_to_file_name(item_path));
|
||||
if (not marker || (marker && item_found)) {
|
||||
// if (not utils::path::is_ads_file_path(item_path)) {
|
||||
union {
|
||||
UINT8 B[FIELD_OFFSET(FSP_FSCTL_DIR_INFO, FileNameBuf) +
|
||||
((repertory::max_path_length + 1U) * sizeof(WCHAR))];
|
||||
@ -380,10 +379,8 @@ auto remote_winfsp_drive::ReadDirectory(PVOID /*file_node*/, PVOID file_desc,
|
||||
display_name.size()) *
|
||||
sizeof(WCHAR)));
|
||||
|
||||
if (not item["meta"].empty() ||
|
||||
((item_path != ".") && (item_path != ".."))) {
|
||||
populate_file_info(item, directory_info->FileInfo);
|
||||
}
|
||||
populate_file_info(item, directory_info->FileInfo);
|
||||
|
||||
if (ret == STATUS_SUCCESS) {
|
||||
::wcscpy_s(&directory_info->FileNameBuf[0],
|
||||
repertory::max_path_length, &display_name[0]);
|
||||
@ -394,7 +391,6 @@ auto remote_winfsp_drive::ReadDirectory(PVOID /*file_node*/, PVOID file_desc,
|
||||
break;
|
||||
}
|
||||
}
|
||||
// }
|
||||
} else {
|
||||
item_found = display_name == std::wstring(marker);
|
||||
}
|
||||
|
@ -1,187 +1,189 @@
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "rpc/server/server.hpp"
|
||||
|
||||
#include "app_config.hpp"
|
||||
#include "events/event_system.hpp"
|
||||
#include "events/types/service_start_begin.hpp"
|
||||
#include "events/types/service_start_end.hpp"
|
||||
#include "events/types/service_stop_begin.hpp"
|
||||
#include "events/types/service_stop_end.hpp"
|
||||
#include "events/types/unmount_requested.hpp"
|
||||
#include "rpc/common.hpp"
|
||||
#include "utils/error_utils.hpp"
|
||||
|
||||
namespace repertory {
|
||||
server::server(app_config &config) : config_(config) {}
|
||||
|
||||
void server::handle_get_config(const httplib::Request & /*req*/,
|
||||
httplib::Response &res) {
|
||||
auto data = config_.get_json();
|
||||
clean_json_config(config_.get_provider_type(), data);
|
||||
res.set_content(data.dump(), "application/json");
|
||||
res.status = http_error_codes::ok;
|
||||
}
|
||||
|
||||
void server::handle_get_config_value_by_name(const httplib::Request &req,
|
||||
httplib::Response &res) {
|
||||
auto name = req.get_param_value("name");
|
||||
auto data = json({{
|
||||
"value",
|
||||
clean_json_value(name, config_.get_value_by_name(name)),
|
||||
}});
|
||||
res.set_content(data.dump(), "application/json");
|
||||
res.status = http_error_codes::ok;
|
||||
}
|
||||
|
||||
void server::handle_set_config_value_by_name(const httplib::Request &req,
|
||||
httplib::Response &res) {
|
||||
auto name = req.get_param_value("name");
|
||||
auto value = req.get_param_value("value");
|
||||
|
||||
json data = {{
|
||||
"value",
|
||||
clean_json_value(name, config_.set_value_by_name(name, value)),
|
||||
}};
|
||||
res.set_content(data.dump(), "application/json");
|
||||
res.status = http_error_codes::ok;
|
||||
}
|
||||
|
||||
void server::handle_unmount(const httplib::Request & /*req*/,
|
||||
httplib::Response &res) {
|
||||
event_system::instance().raise<unmount_requested>();
|
||||
res.status = http_error_codes::ok;
|
||||
}
|
||||
|
||||
void server::initialize(httplib::Server &inst) {
|
||||
inst.Get("/api/v1/" + rpc_method::get_config, [this](auto &&req, auto &&res) {
|
||||
handle_get_config(std::forward<decltype(req)>(req),
|
||||
std::forward<decltype(res)>(res));
|
||||
});
|
||||
|
||||
inst.Get("/api/v1/" + rpc_method::get_config_value_by_name,
|
||||
[this](auto &&req, auto &&res) {
|
||||
handle_get_config_value_by_name(std::forward<decltype(req)>(req),
|
||||
std::forward<decltype(res)>(res));
|
||||
});
|
||||
|
||||
inst.Post("/api/v1/" + rpc_method::set_config_value_by_name,
|
||||
[this](auto &&req, auto &&res) {
|
||||
handle_set_config_value_by_name(std::forward<decltype(req)>(req),
|
||||
std::forward<decltype(res)>(res));
|
||||
});
|
||||
|
||||
inst.Post("/api/v1/" + rpc_method::unmount, [this](auto &&req, auto &&res) {
|
||||
handle_unmount(std::forward<decltype(req)>(req),
|
||||
std::forward<decltype(res)>(res));
|
||||
});
|
||||
}
|
||||
|
||||
void server::start() {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
mutex_lock lock(start_stop_mutex_);
|
||||
if (server_thread_) {
|
||||
return;
|
||||
}
|
||||
|
||||
event_system::instance().raise<service_start_begin>(function_name, "server");
|
||||
|
||||
server_ = std::make_unique<httplib::Server>();
|
||||
|
||||
server_->set_exception_handler([](const httplib::Request &req,
|
||||
httplib::Response &res,
|
||||
std::exception_ptr ptr) {
|
||||
json data = {{"path", req.path}};
|
||||
|
||||
try {
|
||||
std::rethrow_exception(ptr);
|
||||
} catch (std::exception &e) {
|
||||
data["error"] = (e.what() == nullptr) ? "unknown error" : e.what();
|
||||
utils::error::raise_error(function_name, e,
|
||||
"failed request: " + req.path);
|
||||
} catch (...) {
|
||||
data["error"] = "unknown error";
|
||||
utils::error::raise_error(function_name, "unknown error",
|
||||
"failed request: " + req.path);
|
||||
}
|
||||
|
||||
res.set_content(data.dump(), "application/json");
|
||||
res.status = http_error_codes::internal_error;
|
||||
});
|
||||
|
||||
server_->set_pre_routing_handler(
|
||||
[this](auto &&req, auto &&res) -> httplib::Server::HandlerResponse {
|
||||
if (rpc::check_authorization(config_, req)) {
|
||||
return httplib::Server::HandlerResponse::Unhandled;
|
||||
}
|
||||
|
||||
res.status = http_error_codes::unauthorized;
|
||||
return httplib::Server::HandlerResponse::Handled;
|
||||
});
|
||||
|
||||
initialize(*server_);
|
||||
|
||||
server_thread_ = std::make_unique<std::thread>([this]() {
|
||||
server_->set_socket_options([](auto &&sock) {
|
||||
#if defined(_WIN32)
|
||||
int enable{1};
|
||||
setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
|
||||
reinterpret_cast<const char *>(&enable), sizeof(enable));
|
||||
#else // !defined(_WIN32)
|
||||
linger opt{1, 0};
|
||||
setsockopt(sock, SOL_SOCKET, SO_LINGER,
|
||||
reinterpret_cast<const char *>(&opt), sizeof(opt));
|
||||
#endif // defined(_WIN32)
|
||||
});
|
||||
|
||||
server_->listen("127.0.0.1", config_.get_api_port());
|
||||
});
|
||||
event_system::instance().raise<service_start_end>(function_name, "server");
|
||||
}
|
||||
|
||||
void server::stop() {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
unique_mutex_lock lock(start_stop_mutex_);
|
||||
if (not server_thread_) {
|
||||
return;
|
||||
}
|
||||
|
||||
event_system::instance().raise<service_stop_begin>(function_name, "server");
|
||||
|
||||
server_->stop();
|
||||
|
||||
std::unique_ptr<std::thread> thread{nullptr};
|
||||
std::swap(thread, server_thread_);
|
||||
lock.unlock();
|
||||
|
||||
thread->join();
|
||||
thread.reset();
|
||||
|
||||
lock.lock();
|
||||
server_.reset();
|
||||
lock.unlock();
|
||||
|
||||
event_system::instance().raise<service_stop_end>(function_name, "server");
|
||||
}
|
||||
} // namespace repertory
|
||||
/*
|
||||
Copyright <2018-2025> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "rpc/server/server.hpp"
|
||||
|
||||
#include "app_config.hpp"
|
||||
#include "events/event_system.hpp"
|
||||
#include "events/types/service_start_begin.hpp"
|
||||
#include "events/types/service_start_end.hpp"
|
||||
#include "events/types/service_stop_begin.hpp"
|
||||
#include "events/types/service_stop_end.hpp"
|
||||
#include "events/types/unmount_requested.hpp"
|
||||
#include "rpc/common.hpp"
|
||||
#include "utils/error_utils.hpp"
|
||||
|
||||
namespace repertory {
|
||||
server::server(app_config &config) : config_(config) {}
|
||||
|
||||
void server::handle_get_config(const httplib::Request & /*req*/,
|
||||
httplib::Response &res) {
|
||||
auto data = config_.get_json();
|
||||
clean_json_config(config_.get_provider_type(), data);
|
||||
res.set_content(data.dump(), "application/json");
|
||||
res.status = http_error_codes::ok;
|
||||
}
|
||||
|
||||
void server::handle_get_config_value_by_name(const httplib::Request &req,
|
||||
httplib::Response &res) {
|
||||
auto name = req.get_param_value("name");
|
||||
auto data = json({{
|
||||
"value",
|
||||
clean_json_value(name, config_.get_value_by_name(name)),
|
||||
}});
|
||||
res.set_content(data.dump(), "application/json");
|
||||
res.status = http_error_codes::ok;
|
||||
}
|
||||
|
||||
void server::handle_set_config_value_by_name(const httplib::Request &req,
|
||||
httplib::Response &res) {
|
||||
auto name = req.get_param_value("name");
|
||||
auto value = req.get_param_value("value");
|
||||
|
||||
json data = {{
|
||||
"value",
|
||||
clean_json_value(name, config_.set_value_by_name(name, value)),
|
||||
}};
|
||||
res.set_content(data.dump(), "application/json");
|
||||
res.status = http_error_codes::ok;
|
||||
}
|
||||
|
||||
void server::handle_unmount(const httplib::Request & /*req*/,
|
||||
httplib::Response &res) {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
event_system::instance().raise<unmount_requested>(function_name);
|
||||
res.status = http_error_codes::ok;
|
||||
}
|
||||
|
||||
void server::initialize(httplib::Server &inst) {
|
||||
inst.Get("/api/v1/" + rpc_method::get_config, [this](auto &&req, auto &&res) {
|
||||
handle_get_config(std::forward<decltype(req)>(req),
|
||||
std::forward<decltype(res)>(res));
|
||||
});
|
||||
|
||||
inst.Get("/api/v1/" + rpc_method::get_config_value_by_name,
|
||||
[this](auto &&req, auto &&res) {
|
||||
handle_get_config_value_by_name(std::forward<decltype(req)>(req),
|
||||
std::forward<decltype(res)>(res));
|
||||
});
|
||||
|
||||
inst.Post("/api/v1/" + rpc_method::set_config_value_by_name,
|
||||
[this](auto &&req, auto &&res) {
|
||||
handle_set_config_value_by_name(std::forward<decltype(req)>(req),
|
||||
std::forward<decltype(res)>(res));
|
||||
});
|
||||
|
||||
inst.Post("/api/v1/" + rpc_method::unmount, [this](auto &&req, auto &&res) {
|
||||
handle_unmount(std::forward<decltype(req)>(req),
|
||||
std::forward<decltype(res)>(res));
|
||||
});
|
||||
}
|
||||
|
||||
void server::start() {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
mutex_lock lock(start_stop_mutex_);
|
||||
if (server_thread_) {
|
||||
return;
|
||||
}
|
||||
|
||||
event_system::instance().raise<service_start_begin>(function_name, "server");
|
||||
|
||||
server_ = std::make_unique<httplib::Server>();
|
||||
|
||||
server_->set_exception_handler([](const httplib::Request &req,
|
||||
httplib::Response &res,
|
||||
std::exception_ptr ptr) {
|
||||
json data = {{"path", req.path}};
|
||||
|
||||
try {
|
||||
std::rethrow_exception(ptr);
|
||||
} catch (std::exception &e) {
|
||||
data["error"] = (e.what() == nullptr) ? "unknown error" : e.what();
|
||||
utils::error::raise_error(function_name, e,
|
||||
"failed request: " + req.path);
|
||||
} catch (...) {
|
||||
data["error"] = "unknown error";
|
||||
utils::error::raise_error(function_name, "unknown error",
|
||||
"failed request: " + req.path);
|
||||
}
|
||||
|
||||
res.set_content(data.dump(), "application/json");
|
||||
res.status = http_error_codes::internal_error;
|
||||
});
|
||||
|
||||
server_->set_pre_routing_handler(
|
||||
[this](auto &&req, auto &&res) -> httplib::Server::HandlerResponse {
|
||||
if (rpc::check_authorization(config_, req)) {
|
||||
return httplib::Server::HandlerResponse::Unhandled;
|
||||
}
|
||||
|
||||
res.status = http_error_codes::unauthorized;
|
||||
return httplib::Server::HandlerResponse::Handled;
|
||||
});
|
||||
|
||||
initialize(*server_);
|
||||
|
||||
server_thread_ = std::make_unique<std::thread>([this]() {
|
||||
server_->set_socket_options([](auto &&sock) {
|
||||
#if defined(_WIN32)
|
||||
int enable{1};
|
||||
setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
|
||||
reinterpret_cast<const char *>(&enable), sizeof(enable));
|
||||
#else // !defined(_WIN32)
|
||||
linger opt{1, 0};
|
||||
setsockopt(sock, SOL_SOCKET, SO_LINGER,
|
||||
reinterpret_cast<const char *>(&opt), sizeof(opt));
|
||||
#endif // defined(_WIN32)
|
||||
});
|
||||
|
||||
server_->listen("127.0.0.1", config_.get_api_port());
|
||||
});
|
||||
event_system::instance().raise<service_start_end>(function_name, "server");
|
||||
}
|
||||
|
||||
void server::stop() {
|
||||
REPERTORY_USES_FUNCTION_NAME();
|
||||
|
||||
unique_mutex_lock lock(start_stop_mutex_);
|
||||
if (not server_thread_) {
|
||||
return;
|
||||
}
|
||||
|
||||
event_system::instance().raise<service_stop_begin>(function_name, "server");
|
||||
|
||||
server_->stop();
|
||||
|
||||
std::unique_ptr<std::thread> thread{nullptr};
|
||||
std::swap(thread, server_thread_);
|
||||
lock.unlock();
|
||||
|
||||
thread->join();
|
||||
thread.reset();
|
||||
|
||||
lock.lock();
|
||||
server_.reset();
|
||||
lock.unlock();
|
||||
|
||||
event_system::instance().raise<service_stop_end>(function_name, "server");
|
||||
}
|
||||
} // namespace repertory
|
||||
|
@ -23,6 +23,8 @@
|
||||
#include "backward.hpp"
|
||||
#endif // defined(PROJECT_ENABLE_BACKWARD_CPP)
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "cli/actions.hpp"
|
||||
#include "initialize.hpp"
|
||||
#include "types/repertory.hpp"
|
||||
@ -44,7 +46,7 @@ auto main(int argc, char **argv) -> int {
|
||||
std::vector<const char *> args;
|
||||
{
|
||||
auto args_span = std::span(argv, static_cast<std::size_t>(argc));
|
||||
std::copy(args_span.begin(), args_span.end(), std::back_inserter(args));
|
||||
std::ranges::copy(args_span, std::back_inserter(args));
|
||||
}
|
||||
|
||||
if (argc == 1) {
|
||||
|
@ -326,9 +326,9 @@ void handlers::handle_put_mount_location(const httplib::Request &req,
|
||||
void handlers::handle_get_available_locations(httplib::Response &res) {
|
||||
#if defined(_WIN32)
|
||||
constexpr const std::array<std::string_view, 26U> letters{
|
||||
"A:", "B:", "C:", "D:", "E:", "F:", "G:", "H:", "I:",
|
||||
"J:", "K:", "L:", "M:", "N:", "O:", "P:", "Q:", "R:",
|
||||
"S:", "T:", "U:", "V:", "W:", "X:", "Y:", "Z:",
|
||||
"a:", "b:", "c:", "d:", "e:", "f:", "g:", "h:", "i:",
|
||||
"j:", "k:", "l:", "m:", "n:", "o:", "p:", "q:", "r:",
|
||||
"s:", "t:", "u:", "v:", "w:", "x:", "y:", "z:",
|
||||
};
|
||||
|
||||
auto available = std::accumulate(
|
||||
|
Reference in New Issue
Block a user