[ui] Implement provider test button #49

This commit is contained in:
Scott E. Graves 2025-04-23 14:14:59 -05:00
parent 5f50e0204a
commit 3502bf2189
10 changed files with 258 additions and 88 deletions

View File

@ -49,6 +49,7 @@ static const option password_option = {"-pw", "--password"};
static const option remote_mount_option = {"-rm", "--remote_mount"}; static const option remote_mount_option = {"-rm", "--remote_mount"};
static const option set_option = {"-set", "--set"}; static const option set_option = {"-set", "--set"};
static const option status_option = {"-status", "--status"}; static const option status_option = {"-status", "--status"};
static const option test_option = {"-test", "--test"};
static const option ui_option = {"-ui", "--ui"}; static const option ui_option = {"-ui", "--ui"};
static const option ui_port_option = {"-up", "--ui_port"}; static const option ui_port_option = {"-up", "--ui_port"};
static const option unmount_option = {"-unmount", "--unmount"}; static const option unmount_option = {"-unmount", "--unmount"};
@ -77,6 +78,7 @@ static const std::vector<option> option_list = {
remote_mount_option, remote_mount_option,
set_option, set_option,
status_option, status_option,
test_option,
ui_option, ui_option,
ui_port_option, ui_port_option,
unmount_option, unmount_option,

View File

@ -793,8 +793,22 @@ auto s3_provider::is_file(const std::string &api_path, bool &exists) const
} }
auto s3_provider::is_online() const -> bool { auto s3_provider::is_online() const -> bool {
// TODO implement this REPERTORY_USES_FUNCTION_NAME();
return true;
try {
bool is_encrypted{};
std::string object_name;
head_object_result result{};
auto res{
get_object_info(true, "/", is_encrypted, object_name, result),
};
return res == api_error::success;
} catch (const std::exception &e) {
utils::error::raise_error(function_name, e, "exception occurred");
}
return false;
} }
auto s3_provider::read_file_bytes(const std::string &api_path, std::size_t size, auto s3_provider::read_file_bytes(const std::string &api_path, std::size_t size,

View File

@ -36,6 +36,7 @@
#include "cli/pinned_status.hpp" #include "cli/pinned_status.hpp"
#include "cli/set.hpp" #include "cli/set.hpp"
#include "cli/status.hpp" #include "cli/status.hpp"
#include "cli/test.hpp"
#include "cli/ui.hpp" #include "cli/ui.hpp"
#include "cli/unmount.hpp" #include "cli/unmount.hpp"
#include "cli/unpin_file.hpp" #include "cli/unpin_file.hpp"
@ -48,7 +49,7 @@ using action = std::function<exit_code(
struct option_hasher { struct option_hasher {
auto operator()(const utils::cli::option &opt) const -> std::size_t { auto operator()(const utils::cli::option &opt) const -> std::size_t {
return std::hash<std::string>()(opt[0U] + '|' + opt[1U]); return std::hash<std::string>()(opt.at(0U) + '|' + opt.at(1U));
} }
}; };
@ -71,6 +72,7 @@ static const std::unordered_map<utils::cli::option, action, option_hasher>
cli::actions::pinned_status}, cli::actions::pinned_status},
{utils::cli::options::set_option, cli::actions::set}, {utils::cli::options::set_option, cli::actions::set},
{utils::cli::options::status_option, cli::actions::status}, {utils::cli::options::status_option, cli::actions::status},
{utils::cli::options::test_option, cli::actions::test},
{utils::cli::options::ui_option, cli::actions::ui}, {utils::cli::options::ui_option, cli::actions::ui},
{utils::cli::options::unmount_option, cli::actions::unmount}, {utils::cli::options::unmount_option, cli::actions::unmount},
{utils::cli::options::unpin_file_option, cli::actions::unpin_file}, {utils::cli::options::unpin_file_option, cli::actions::unpin_file},

View File

@ -44,12 +44,12 @@ namespace repertory::cli::actions {
std::string required_version; std::string required_version;
std::string returned_version; std::string returned_version;
if (provider.check_version(required_version, returned_version)) { if (provider.check_version(required_version, returned_version)) {
fmt::println("Success:\n\tRequired: {}\n\tActual: {}", required_version, fmt::println("0\nSuccess:\n\tRequired: {}\n\tActual: {}", required_version,
returned_version); returned_version);
return exit_code::success; return exit_code::success;
} }
fmt::println("Failed:\n\tRequired: {}\n\tActual: {}", required_version, fmt::println("1\nFailed:\n\tRequired: {}\n\tActual: {}", required_version,
returned_version); returned_version);
return exit_code::incompatible_version; return exit_code::incompatible_version;
} }

View File

@ -0,0 +1,45 @@
/*
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.
*/
#ifndef REPERTORY_INCLUDE_CLI_TEST_HPP_
#define REPERTORY_INCLUDE_CLI_TEST_HPP_
#include "cli/common.hpp"
namespace repertory::cli::actions {
[[nodiscard]] inline auto
test(std::vector<const char *> /* args */, const std::string &data_directory,
const provider_type &prov, const std::string & /*unique_id*/,
std::string /*user*/, std::string /*password*/) -> exit_code {
app_config config(prov, data_directory);
if (prov == provider_type::remote) {
return exit_code::exception;
}
auto provider{create_provider(prov, config)};
auto is_online{provider->is_online()};
fmt::println("{}\nProvider is {}!", utils::string::from_bool(is_online),
is_online ? "online" : "offline");
return is_online ? exit_code::success : exit_code::exception;
}
} // namespace repertory::cli::actions
#endif // REPERTORY_INCLUDE_CLI_TEST_HPP_

View File

@ -69,11 +69,16 @@ private:
std::condition_variable nonce_notify_; std::condition_variable nonce_notify_;
std::unique_ptr<std::thread> nonce_thread_; std::unique_ptr<std::thread> nonce_thread_;
stop_type stop_requested{false}; stop_type stop_requested{false};
mutable std::mutex test_mtx_;
private: private:
[[nodiscard]] auto data_directory_exists(provider_type prov, [[nodiscard]] auto data_directory_exists(provider_type prov,
std::string_view name) const -> bool; std::string_view name) const -> bool;
void
generate_config(provider_type prov, std::string_view name, const json &cfg,
std::optional<std::string> data_dir = std::nullopt) const;
static void handle_get_available_locations(httplib::Response &res); static void handle_get_available_locations(httplib::Response &res);
void handle_get_mount(const httplib::Request &req, void handle_get_mount(const httplib::Request &req,
@ -91,6 +96,9 @@ private:
void handle_get_settings(httplib::Response &res) const; void handle_get_settings(httplib::Response &res) const;
void handle_get_test(const httplib::Request &req,
httplib::Response &res) const;
void handle_post_add_mount(const httplib::Request &req, void handle_post_add_mount(const httplib::Request &req,
httplib::Response &res) const; httplib::Response &res) const;
@ -113,7 +121,8 @@ private:
void removed_expired_nonces(); void removed_expired_nonces();
void set_key_value(provider_type prov, std::string_view name, void set_key_value(provider_type prov, std::string_view name,
std::string_view key, std::string_view value) const; std::string_view key, std::string_view value,
std::optional<std::string> data_dir = std::nullopt) const;
}; };
} // namespace repertory::ui } // namespace repertory::ui

View File

@ -98,6 +98,7 @@ namespace {
return std::string{value}; return std::string{value};
} }
} // namespace } // namespace
namespace repertory::ui { namespace repertory::ui {
@ -200,6 +201,9 @@ handlers::handlers(mgmt_app_config *config, httplib::Server *server)
handle_get_settings(res); handle_get_settings(res);
}); });
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); handle_post_add_mount(req, res);
}); });
@ -308,6 +312,46 @@ auto handlers::data_directory_exists(provider_type prov,
return ret; return ret;
} }
void handlers::generate_config(provider_type prov, std::string_view name,
const json &cfg,
std::optional<std::string> data_dir) const {
std::map<std::string, std::string> values{};
for (const auto &[key, value] : cfg.items()) {
if (value.is_object()) {
for (const auto &[key2, value2] : value.items()) {
auto sub_key = fmt::format("{}.{}", key, key2);
auto skip{false};
auto decrypted = decrypt_value(
config_, sub_key, value2.template get<std::string>(), skip);
if (skip) {
continue;
}
values[sub_key] = decrypted;
}
continue;
}
auto skip{false};
auto decrypted =
decrypt_value(config_, key, value.template get<std::string>(), skip);
if (skip) {
continue;
}
values[key] = decrypted;
}
if (data_dir.has_value()) {
launch_process(prov, name, {"-dd", data_dir.value(), "-gc"});
} else {
launch_process(prov, name, {"-gc"});
}
for (const auto &[key, value] : values) {
set_key_value(prov, name, key, value, data_dir);
}
}
void handlers::handle_put_mount_location(const httplib::Request &req, void handlers::handle_put_mount_location(const httplib::Request &req,
httplib::Response &res) const { httplib::Response &res) const {
REPERTORY_USES_FUNCTION_NAME(); REPERTORY_USES_FUNCTION_NAME();
@ -367,7 +411,6 @@ void handlers::handle_get_mount(const httplib::Request &req,
} }
auto lines = launch_process(prov, name, {"-dc"}); auto lines = launch_process(prov, name, {"-dc"});
if (lines.at(0U) != "0") { if (lines.at(0U) != "0") {
throw utils::error::create_exception(function_name, { throw utils::error::create_exception(function_name, {
"command failed", "command failed",
@ -475,6 +518,26 @@ void handlers::handle_get_settings(httplib::Response &res) const {
res.status = http_error_codes::ok; res.status = http_error_codes::ok;
} }
void handlers::handle_get_test(const httplib::Request &req,
httplib::Response &res) const {
unique_mutex_lock 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")});
generate_config(prov, name, cfg, data_dir);
auto lines = launch_process(prov, name, {"-dd", data_dir, "-test"});
res.status = lines.at(0U) == "0" ? http_error_codes::ok
: http_error_codes::internal_error;
utils::file::directory{data_dir}.remove_recursively();
}
void handlers::handle_post_add_mount(const httplib::Request &req, void handlers::handle_post_add_mount(const httplib::Request &req,
httplib::Response &res) const { httplib::Response &res) const {
auto name = req.get_param_value("name"); auto name = req.get_param_value("name");
@ -485,38 +548,9 @@ void handlers::handle_post_add_mount(const httplib::Request &req,
} }
auto cfg = nlohmann::json::parse(req.get_param_value("config")); auto cfg = nlohmann::json::parse(req.get_param_value("config"));
generate_config(prov, name, cfg);
std::map<std::string, std::string> values{}; launch_process(prov, name, {"-test"});
for (const auto &[key, value] : cfg.items()) {
if (value.is_object()) {
for (const auto &[key2, value2] : value.items()) {
auto sub_key = fmt::format("{}.{}", key, key2);
auto skip{false};
auto decrypted = decrypt_value(
config_, sub_key, value2.template get<std::string>(), skip);
if (skip) {
continue;
}
values[sub_key] = decrypted;
}
continue;
}
auto skip{false};
auto decrypted =
decrypt_value(config_, key, value.template get<std::string>(), skip);
if (skip) {
continue;
}
values[key] = decrypted;
}
launch_process(prov, name, {"-gc"});
for (auto &[key, value] : values) {
set_key_value(prov, name, key, value);
}
res.status = http_error_codes::ok; res.status = http_error_codes::ok;
} }
@ -743,9 +777,13 @@ void handlers::removed_expired_nonces() {
} }
void handlers::set_key_value(provider_type prov, std::string_view name, void handlers::set_key_value(provider_type prov, std::string_view name,
std::string_view key, std::string_view key, std::string_view value,
std::string_view value) const { std::optional<std::string> data_dir) const {
std::vector<std::string> args; std::vector<std::string> args;
if (data_dir.has_value()) {
args.emplace_back("-dd");
args.emplace_back(data_dir.value());
}
args.emplace_back("-set"); args.emplace_back("-set");
args.emplace_back(key); args.emplace_back(key);
args.emplace_back(value); args.emplace_back(value);

View File

@ -30,6 +30,10 @@
#include "utils/types/file/i_file.hpp" #include "utils/types/file/i_file.hpp"
#include "utils/types/file/i_fs_item.hpp" #include "utils/types/file/i_fs_item.hpp"
namespace repertory::utils::directory {
[[nodiscard]] auto temp() -> std::string;
}
namespace repertory::utils::file { namespace repertory::utils::file {
[[nodiscard]] auto change_to_process_directory() -> bool; [[nodiscard]] auto change_to_process_directory() -> bool;
@ -37,13 +41,13 @@ namespace repertory::utils::file {
[[nodiscard]] auto create_temp_name(std::string_view file_part) -> std::string; [[nodiscard]] auto create_temp_name(std::string_view file_part) -> std::string;
// INFO: has test // INFO: has test
[[nodiscard]] auto [[nodiscard]] auto create_temp_name(std::wstring_view file_part)
create_temp_name(std::wstring_view file_part) -> std::wstring; -> std::wstring;
// INFO: has test // INFO: has test
[[nodiscard]] inline auto [[nodiscard]] inline auto
directory_exists_in_path(std::string_view path, directory_exists_in_path(std::string_view path, std::string_view sub_directory)
std::string_view sub_directory) -> bool; -> bool;
// INFO: has test // INFO: has test
[[nodiscard]] inline auto [[nodiscard]] inline auto
@ -51,45 +55,46 @@ directory_exists_in_path(std::wstring_view path,
std::wstring_view sub_directory) -> bool; std::wstring_view sub_directory) -> bool;
// INFO: has test // INFO: has test
[[nodiscard]] inline auto [[nodiscard]] inline auto file_exists_in_path(std::string_view path,
file_exists_in_path(std::string_view path, std::string_view file_name) -> bool; std::string_view file_name)
-> bool;
// INFO: has test // INFO: has test
[[nodiscard]] inline auto [[nodiscard]] inline auto file_exists_in_path(std::wstring_view path,
file_exists_in_path(std::wstring_view path, std::wstring_view file_name)
std::wstring_view file_name) -> bool; -> bool;
// INFO: has test // INFO: has test
[[nodiscard]] auto [[nodiscard]] auto get_free_drive_space(std::string_view path)
get_free_drive_space(std::string_view path) -> std::optional<std::uint64_t>; -> std::optional<std::uint64_t>;
// INFO: has test // INFO: has test
[[nodiscard]] auto [[nodiscard]] auto get_free_drive_space(std::wstring_view path)
get_free_drive_space(std::wstring_view path) -> std::optional<std::uint64_t>; -> std::optional<std::uint64_t>;
// INFO: has test // INFO: has test
[[nodiscard]] auto get_time(std::string_view path, [[nodiscard]] auto get_time(std::string_view path, time_type type)
time_type type) -> std::optional<std::uint64_t>; -> std::optional<std::uint64_t>;
// INFO: has test // INFO: has test
[[nodiscard]] auto get_time(std::wstring_view path, [[nodiscard]] auto get_time(std::wstring_view path, time_type type)
time_type type) -> std::optional<std::uint64_t>; -> std::optional<std::uint64_t>;
// INFO: has test // INFO: has test
[[nodiscard]] auto [[nodiscard]] auto get_times(std::string_view path)
get_times(std::string_view path) -> std::optional<file_times>; -> std::optional<file_times>;
// INFO: has test // INFO: has test
[[nodiscard]] auto [[nodiscard]] auto get_times(std::wstring_view path)
get_times(std::wstring_view path) -> std::optional<file_times>; -> std::optional<file_times>;
// INFO: has test // INFO: has test
[[nodiscard]] auto [[nodiscard]] auto get_total_drive_space(std::string_view path)
get_total_drive_space(std::string_view path) -> std::optional<std::uint64_t>; -> std::optional<std::uint64_t>;
// INFO: has test // INFO: has test
[[nodiscard]] auto [[nodiscard]] auto get_total_drive_space(std::wstring_view path)
get_total_drive_space(std::wstring_view path) -> std::optional<std::uint64_t>; -> std::optional<std::uint64_t>;
#if defined(PROJECT_ENABLE_LIBDSM) #if defined(PROJECT_ENABLE_LIBDSM)
[[nodiscard]] auto [[nodiscard]] auto
@ -97,20 +102,20 @@ smb_create_and_validate_relative_path(std::string_view smb_path,
std::string_view rel_path) -> std::string; std::string_view rel_path) -> std::string;
// INFO: has test // INFO: has test
[[nodiscard]] auto [[nodiscard]] auto smb_create_relative_path(std::string_view smb_path)
smb_create_relative_path(std::string_view smb_path) -> std::string; -> std::string;
// INFO: has test // INFO: has test
[[nodiscard]] auto [[nodiscard]] auto smb_create_search_path(std::string_view smb_path)
smb_create_search_path(std::string_view smb_path) -> std::string; -> std::string;
// INFO: has test // INFO: has test
[[nodiscard]] auto [[nodiscard]] auto smb_create_smb_path(std::string_view smb_path,
smb_create_smb_path(std::string_view smb_path, std::string_view rel_path)
std::string_view rel_path) -> std::string; -> std::string;
[[nodiscard]] auto [[nodiscard]] auto smb_get_parent_path(std::string_view smb_path)
smb_get_parent_path(std::string_view smb_path) -> std::string; -> std::string;
[[nodiscard]] auto smb_get_root_path(std::string_view smb_path) -> std::string; [[nodiscard]] auto smb_get_root_path(std::string_view smb_path) -> std::string;
@ -139,27 +144,30 @@ read_json_file(std::string_view path, nlohmann::json &data,
std::optional<std::string_view> password = std::nullopt) -> bool; std::optional<std::string_view> password = std::nullopt) -> bool;
// INFO: has test // INFO: has test
[[nodiscard]] auto read_json_file( [[nodiscard]] auto
std::wstring_view path, nlohmann::json &data, read_json_file(std::wstring_view path, nlohmann::json &data,
std::optional<std::wstring_view> password = std::nullopt) -> bool; std::optional<std::wstring_view> password = std::nullopt)
-> bool;
// INFO: has test // INFO: has test
[[nodiscard]] auto write_json_file( [[nodiscard]] auto
std::string_view path, const nlohmann::json &data, write_json_file(std::string_view path, const nlohmann::json &data,
std::optional<std::string_view> password = std::nullopt) -> bool; std::optional<std::string_view> password = std::nullopt)
-> bool;
// INFO: has test // INFO: has test
[[nodiscard]] auto write_json_file( [[nodiscard]] auto
std::wstring_view path, const nlohmann::json &data, write_json_file(std::wstring_view path, const nlohmann::json &data,
std::optional<std::wstring_view> password = std::nullopt) -> bool; std::optional<std::wstring_view> password = std::nullopt)
-> bool;
#else // !defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST) #else // !defined(PROJECT_ENABLE_LIBSODIUM) && defined(PROJECT_ENABLE_BOOST)
// INFO: has test // INFO: has test
[[nodiscard]] auto read_json_file(std::string_view path, [[nodiscard]] auto read_json_file(std::string_view path, nlohmann::json &data)
nlohmann::json &data) -> bool; -> bool;
// INFO: has test // INFO: has test
[[nodiscard]] auto read_json_file(std::wstring_view path, [[nodiscard]] auto read_json_file(std::wstring_view path, nlohmann::json &data)
nlohmann::json &data) -> bool; -> bool;
// INFO: has test // INFO: has test
[[nodiscard]] auto write_json_file(std::string_view path, [[nodiscard]] auto write_json_file(std::string_view path,

View File

@ -0,0 +1,52 @@
/*
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 "utils/file.hpp"
#include "utils/common.hpp"
#include "utils/error.hpp"
#if defined(_WIN32)
#include "utils/path.hpp"
#endif // defined(_WIN32)
namespace repertory::utils::directory {
auto temp() -> std::string {
REPERTORY_USES_FUNCTION_NAME();
#if defined(_WIN32)
auto ret{utils::get_environment_variable("TEMP")};
if (ret.empty()) {
ret = utils::path::combine(utils::get_environment_variable("LOCALAPPDATA"),
{"Temp"});
}
#else // !defined(_WIN32)
std::string ret{"/tmp"};
#endif // defined(_WIN32)
if (not utils::file::directory{ret}.create_directory()) {
utils::error::handle_error(function_name,
utils::error::create_error_message(
{"failed to create directory", ret}));
}
return ret;
}
} // namespace repertory::utils::directory

View File

@ -289,7 +289,7 @@ class Mount with ChangeNotifier {
final response = await http.put( final response = await http.put(
Uri.parse( Uri.parse(
Uri.encodeFull( Uri.encodeFull(
'${getBaseUri()}/api/v1/test?auth=$auth&name=$name&type=$type&settings=${jsonEncode(mountConfig.settings)}', '${getBaseUri()}/api/v1/test?auth=$auth&name=$name&type=$type&config=${jsonEncode(mountConfig.settings)}',
), ),
), ),
); );