From d9cd2aa88a6c364292f37c6ff669d91733d00c37 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Mon, 3 Mar 2025 19:56:56 -0600 Subject: [PATCH] Create management portal in Flutter (#40) Reviewed-on: https://git.fifthgrid.com/BlockStorage/repertory/pulls/40 --- .cspell/words.txt | 2 + config.sh | 2 + repertory/librepertory/include/app_config.hpp | 2 + repertory/librepertory/include/rpc/common.hpp | 82 +++ .../include/rpc/server/server.hpp | 2 - .../librepertory/include/types/repertory.hpp | 11 + .../librepertory/include/utils/cli_utils.hpp | 23 +- repertory/librepertory/src/app_config.cpp | 44 +- .../librepertory/src/rpc/server/server.cpp | 51 +- .../librepertory/src/types/repertory.cpp | 31 + repertory/repertory/include/cli/actions.hpp | 2 + repertory/repertory/include/cli/help.hpp | 6 + repertory/repertory/include/cli/ui.hpp | 63 ++ repertory/repertory/include/ui/handlers.hpp | 70 +++ .../repertory/include/ui/mgmt_app_config.hpp | 62 ++ repertory/repertory/main.cpp | 13 +- repertory/repertory/src/ui/handlers.cpp | 368 ++++++++++++ .../repertory/src/ui/mgmt_app_config.cpp | 170 ++++++ web/repertory/.cspell/words.txt | 4 + web/repertory/.gitignore | 47 ++ web/repertory/.metadata | 30 + web/repertory/README.md | 16 + web/repertory/analysis_options.yaml | 28 + web/repertory/lib/constants.dart | 1 + .../lib/errors/duplicate_mount_exception.dart | 6 + web/repertory/lib/helpers.dart | 18 + web/repertory/lib/main.dart | 94 +++ web/repertory/lib/models/mount.dart | 83 +++ web/repertory/lib/models/mount_list.dart | 67 +++ web/repertory/lib/types/mount_config.dart | 31 + .../lib/widgets/mount_list_widget.dart | 31 + web/repertory/lib/widgets/mount_settings.dart | 568 ++++++++++++++++++ web/repertory/lib/widgets/mount_widget.dart | 112 ++++ web/repertory/pubspec.yaml | 93 +++ web/repertory/test/widget_test.dart | 30 + web/repertory/web/favicon.png | Bin 0 -> 917 bytes web/repertory/web/icons/Icon-192.png | Bin 0 -> 5292 bytes web/repertory/web/icons/Icon-512.png | Bin 0 -> 8252 bytes web/repertory/web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes web/repertory/web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes web/repertory/web/index.html | 38 ++ web/repertory/web/manifest.json | 35 ++ 42 files changed, 2252 insertions(+), 84 deletions(-) create mode 100644 repertory/librepertory/include/rpc/common.hpp create mode 100644 repertory/repertory/include/cli/ui.hpp create mode 100644 repertory/repertory/include/ui/handlers.hpp create mode 100644 repertory/repertory/include/ui/mgmt_app_config.hpp create mode 100644 repertory/repertory/src/ui/handlers.cpp create mode 100644 repertory/repertory/src/ui/mgmt_app_config.cpp create mode 100644 web/repertory/.cspell/words.txt create mode 100644 web/repertory/.gitignore create mode 100644 web/repertory/.metadata create mode 100644 web/repertory/README.md create mode 100644 web/repertory/analysis_options.yaml create mode 100644 web/repertory/lib/constants.dart create mode 100644 web/repertory/lib/errors/duplicate_mount_exception.dart create mode 100644 web/repertory/lib/helpers.dart create mode 100644 web/repertory/lib/main.dart create mode 100644 web/repertory/lib/models/mount.dart create mode 100644 web/repertory/lib/models/mount_list.dart create mode 100644 web/repertory/lib/types/mount_config.dart create mode 100644 web/repertory/lib/widgets/mount_list_widget.dart create mode 100644 web/repertory/lib/widgets/mount_settings.dart create mode 100644 web/repertory/lib/widgets/mount_widget.dart create mode 100644 web/repertory/pubspec.yaml create mode 100644 web/repertory/test/widget_test.dart create mode 100644 web/repertory/web/favicon.png create mode 100644 web/repertory/web/icons/Icon-192.png create mode 100644 web/repertory/web/icons/Icon-512.png create mode 100644 web/repertory/web/icons/Icon-maskable-192.png create mode 100644 web/repertory/web/icons/Icon-maskable-512.png create mode 100644 web/repertory/web/index.html create mode 100644 web/repertory/web/manifest.json diff --git a/.cspell/words.txt b/.cspell/words.txt index 105d1a80..8b743f2b 100644 --- a/.cspell/words.txt +++ b/.cspell/words.txt @@ -154,8 +154,10 @@ mtune musl-libc nana ncrypt +nlohmann nlohmann_json nmakeprg +nohup nominmax ntstatus nullptr diff --git a/config.sh b/config.sh index a2290464..7e647523 100755 --- a/config.sh +++ b/config.sh @@ -19,6 +19,8 @@ PROJECT_APP_LIST=(${PROJECT_NAME}) PROJECT_PRIVATE_KEY=${DEVELOPER_PRIVATE_KEY} PROJECT_PUBLIC_KEY=${DEVELOPER_PUBLIC_KEY} +PROJECT_FLUTTER_BASE_HREF="/ui/" + PROJECT_ENABLE_WIN32_LONG_PATH_NAMES=OFF PROJECT_ENABLE_BACKWARD_CPP=OFF diff --git a/repertory/librepertory/include/app_config.hpp b/repertory/librepertory/include/app_config.hpp index 572a135e..7d825e3e 100644 --- a/repertory/librepertory/include/app_config.hpp +++ b/repertory/librepertory/include/app_config.hpp @@ -52,6 +52,8 @@ public: [[nodiscard]] static auto get_provider_name(const provider_type &prov) -> std::string; + [[nodiscard]] static auto get_root_data_directory() -> std::string; + public: [[nodiscard]] static auto get_stop_requested() -> bool; diff --git a/repertory/librepertory/include/rpc/common.hpp b/repertory/librepertory/include/rpc/common.hpp new file mode 100644 index 00000000..b7ed4bf5 --- /dev/null +++ b/repertory/librepertory/include/rpc/common.hpp @@ -0,0 +1,82 @@ +/* + Copyright <2018-2025> + + 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_RPC_COMMON_HPP_ +#define REPERTORY_INCLUDE_RPC_COMMON_HPP_ + +#include "utils/base64.hpp" +#include "utils/error_utils.hpp" +#include "utils/string.hpp" + +namespace repertory::rpc { +[[nodiscard]] auto check_authorization(const auto &cfg, + const httplib::Request &req) -> bool { + REPERTORY_USES_FUNCTION_NAME(); + + if (cfg.get_api_auth().empty() || cfg.get_api_user().empty()) { + utils::error::raise_error(function_name, + "authorization user or password is not set"); + return false; + } + + auto authorization = req.get_header_value("Authorization"); + if (authorization.empty()) { + utils::error::raise_error(function_name, + "'Authorization' header is not set"); + return false; + } + + auto auth_parts = utils::string::split(authorization, ' ', true); + if (auth_parts.empty()) { + utils::error::raise_error(function_name, "'Authorization' header is empty"); + return false; + } + + auto auth_type = auth_parts[0U]; + if (auth_type != "Basic") { + utils::error::raise_error(function_name, + "authorization type is not 'Basic'"); + return false; + } + + auto data = macaron::Base64::Decode(authorization.substr(6U)); + auto auth_str = std::string(data.begin(), data.end()); + + auto auth = utils::string::split(auth_str, ':', false); + if (auth.size() < 2U) { + utils::error::raise_error(function_name, "authorization data is not valid"); + return false; + } + + auto user = auth.at(0U); + auth.erase(auth.begin()); + + auto pwd = utils::string::join(auth, ':'); + if ((user != cfg.get_api_user()) || (pwd != cfg.get_api_auth())) { + utils::error::raise_error(function_name, "authorization failed"); + return false; + } + + return true; +} +} // namespace repertory::rpc + +#endif // REPERTORY_INCLUDE_RPC_COMMON_HPP_ diff --git a/repertory/librepertory/include/rpc/server/server.hpp b/repertory/librepertory/include/rpc/server/server.hpp index 48735e10..5892d732 100644 --- a/repertory/librepertory/include/rpc/server/server.hpp +++ b/repertory/librepertory/include/rpc/server/server.hpp @@ -40,8 +40,6 @@ private: std::mutex start_stop_mutex_; private: - [[nodiscard]] auto check_authorization(const httplib::Request &req) -> bool; - void handle_get_config(const httplib::Request &req, httplib::Response &res); void handle_get_config_value_by_name(const httplib::Request &req, diff --git a/repertory/librepertory/include/types/repertory.hpp b/repertory/librepertory/include/types/repertory.hpp index fce1cbc2..b5a3fed5 100644 --- a/repertory/librepertory/include/types/repertory.hpp +++ b/repertory/librepertory/include/types/repertory.hpp @@ -38,6 +38,7 @@ constexpr const auto default_retry_read_count{6U}; constexpr const auto default_ring_buffer_file_size{512U}; constexpr const auto default_task_wait_ms{100U}; constexpr const auto default_timeout_ms{60000U}; +constexpr const auto default_ui_mgmt_port{std::uint16_t{30000U}}; constexpr const auto max_ring_buffer_file_size{std::uint16_t(1024U)}; constexpr const auto max_s3_object_name_length{1024U}; constexpr const auto min_cache_size_bytes{ @@ -280,6 +281,8 @@ enum class exit_code : std::int32_t { pin_failed = -16, unpin_failed = -17, init_failed = -18, + ui_mount_failed = -19, + exception = -20, }; enum http_error_codes : std::int32_t { @@ -304,6 +307,13 @@ enum class provider_type : std::size_t { unknown, }; +[[nodiscard]] auto +provider_type_from_string(std::string_view type, + provider_type default_type = provider_type::unknown) + -> provider_type; + +[[nodiscard]] auto provider_type_to_string(provider_type type) -> std::string; + #if defined(_WIN32) struct open_file_data final { PVOID directory_buffer{nullptr}; @@ -487,6 +497,7 @@ inline constexpr const auto JSON_MAX_UPLOAD_COUNT{"MaxUploadCount"}; inline constexpr const auto JSON_MED_FREQ_INTERVAL_SECS{ "MedFreqIntervalSeconds"}; inline constexpr const auto JSON_META{"Meta"}; +inline constexpr const auto JSON_MOUNT_LOCATIONS{"MountLocations"}; inline constexpr const auto JSON_ONLINE_CHECK_RETRY_SECS{ "OnlineCheckRetrySeconds"}; inline constexpr const auto JSON_PATH{"Path"}; diff --git a/repertory/librepertory/include/utils/cli_utils.hpp b/repertory/librepertory/include/utils/cli_utils.hpp index 06ce1f41..c0f9ed01 100644 --- a/repertory/librepertory/include/utils/cli_utils.hpp +++ b/repertory/librepertory/include/utils/cli_utils.hpp @@ -49,6 +49,8 @@ static const option password_option = {"-pw", "--password"}; static const option remote_mount_option = {"-rm", "--remote_mount"}; static const option set_option = {"-set", "--set"}; static const option status_option = {"-status", "--status"}; +static const option ui_option = {"-ui", "--ui"}; +static const option ui_port_option = {"-up", "--ui_port"}; static const option unmount_option = {"-unmount", "--unmount"}; static const option unpin_file_option = {"-uf", "--unpin_file"}; static const option user_option = {"-us", "--user"}; @@ -75,6 +77,8 @@ static const std::vector