diff --git a/CMakeLists.txt b/CMakeLists.txt index a5e91c5..6523aaa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,6 +64,8 @@ add_executable(seadisk src/main.cpp src/seafile/fs/seafile_store.cpp include/seafile/fs/storage/repo_entry.hpp src/seafile/fs/storage/repo_entry.cpp + include/seafile/util/version.hpp + src/seafile/util/version.cpp ) target_compile_definitions(seadisk PRIVATE diff --git a/include/seafile/api/cached_seafile_api.hpp b/include/seafile/api/cached_seafile_api.hpp index 99c9dec..eecedf8 100644 --- a/include/seafile/api/cached_seafile_api.hpp +++ b/include/seafile/api/cached_seafile_api.hpp @@ -7,13 +7,11 @@ #include "seafile_api_interface.hpp" #include "seafile/cache/cache.hpp" -namespace seafile::api -{ - class CachedSeafileApi final : public ISeaFileApi - { +namespace seafile::api { + class CachedSeafileApi final : public ISeaFileApi { mutable cache::Cache file_info_cache; mutable cache::Cache dir_info_cache; - mutable cache::Cache> repo_entries_cache; + mutable cache::Cache > repo_entries_cache; mutable cache::Cache account_info_cache; std::shared_ptr underlayer_api; @@ -22,13 +20,11 @@ namespace seafile::api static std::string makeKey( std::string_view prefix, std::initializer_list args - ) - { + ) { std::string key(prefix); - for (auto& arg : args) - { - key += ":"; + for (auto &arg: args) { + key += ':'; key += arg; } @@ -36,66 +32,46 @@ namespace seafile::api } public: - explicit CachedSeafileApi(std::shared_ptr api) : underlayer_api(std::move(api)) - { + explicit CachedSeafileApi(std::shared_ptr api) : underlayer_api(std::move(api)) { }; tl::expected - authToken( - const std::string& username, - const std::string& password - ) override - { - return underlayer_api->authToken(username, password); + auth( + const std::string &username, + const std::string &password + ) override { + return underlayer_api->auth(username, password); } tl::expected - serverInfo() const override - { + serverInfo() const override { return underlayer_api->serverInfo(); } tl::expected - defaultRepo( - const std::string& token, - TokenType type - ) const override - { - return underlayer_api->defaultRepo(token, type); + defaultRepo() const override { + return underlayer_api->defaultRepo(); } tl::expected - repoInfo( - const std::string& repo_id, - const std::string& token, - TokenType type - ) const override - { - return underlayer_api->repoInfo(repo_id, token, type); + repoInfo(const std::string &repo_id) const override { + return underlayer_api->repoInfo(repo_id); } tl::expected, SeafileException> - repoDir( - const std::string& repo_id, - const std::string& path, - const std::string& token, - TokenType type - ) const override - { + repoDir(const std::string &repo_id, const std::string &path) const override { const auto key = makeKey("repoDir", {repo_id, path}); - if (const auto value = repo_entries_cache.get(key)) - { + if (const auto value = repo_entries_cache.get(key)) { return *value; } - auto result = underlayer_api->repoDir(repo_id, path, token, type); - if (result) - { + auto result = underlayer_api->repoDir(repo_id, path); + if (result) { repo_entries_cache.put(key, result.value(), default_ttl); } return result; @@ -103,43 +79,22 @@ namespace seafile::api tl::expected - accountInfo( - const std::string& token, - TokenType type - ) const override - { - if (const auto info = account_info_cache.get(token); info.has_value()) - { - return *info; - } - const auto result = underlayer_api->accountInfo(token, type); - if (result) - { - account_info_cache.put(token, result.value(), default_ttl); - } - return result; + accountInfo() const override { + return underlayer_api->accountInfo(); } tl::expected - fileDetail( - const std::string& repo_id, - const std::string& path, - const std::string& token, - TokenType type - ) const override - { + fileDetail(const std::string &repo_id, const std::string &path) const override { const auto key = makeKey("file", {repo_id, path}); - if (const auto value = file_info_cache.get(key)) - { + if (const auto value = file_info_cache.get(key)) { return *value; } - auto result = underlayer_api->fileDetail(repo_id, path, token, type); + auto result = underlayer_api->fileDetail(repo_id, path); - if (result) - { + if (result) { file_info_cache.put(key, result.value(), default_ttl); } @@ -149,55 +104,39 @@ namespace seafile::api tl::expected - downloadFileUrl( - const std::string& repo_id, - const std::string& path, - const std::string& token, - TokenType type - ) const override - { - return underlayer_api->downloadFileUrl(repo_id, path, token, type); + downloadFileUrl(const std::string &repo_id, const std::string &path) const override { + return underlayer_api->downloadFileUrl(repo_id, path); } tl::expected - downloadFile( - const std::string& repo_id, - const std::string& path, - std::optional start, - std::optional end, - const std::string& token, - TokenType type - ) const override - { - return underlayer_api->downloadFile(repo_id, path, start, end, token, type); + downloadFile(const std::string &repo_id, const std::string &path, std::optional start, + std::optional end) const override { + return underlayer_api->downloadFile(repo_id, path, start, end); } tl::expected - dirDetail( - const std::string& repo_id, - const std::string& path, - const std::string& token, - TokenType type - ) const override - { + dirDetail(const std::string &repo_id, const std::string &path) const override { const auto key = makeKey("dir", {repo_id, path}); - if (auto value = dir_info_cache.get(key)) - { + if (auto value = dir_info_cache.get(key)) { return *value; } - auto result = underlayer_api->dirDetail(repo_id, path, token, type); + auto result = underlayer_api->dirDetail(repo_id, path); - if (result) - { + if (result) { dir_info_cache.put(key, result.value(), default_ttl); } return result; } + + tl::expected, SeafileException> repos(const std::string &type, + const std::string &name_contains) const override { + return underlayer_api->repos(type, name_contains); + } }; } #endif //SEADISK_CACHED_SEAFILE_API_HPP diff --git a/include/seafile/api/seafile_api.hpp b/include/seafile/api/seafile_api.hpp index 6863448..5183e59 100644 --- a/include/seafile/api/seafile_api.hpp +++ b/include/seafile/api/seafile_api.hpp @@ -23,11 +23,14 @@ #include "seafile_api_interface.hpp" #include "server_info.hpp" #include "token_type.hpp" +#include "seafile/util/version.hpp" -namespace seafile::api { - class SeaFileApi final:public ISeaFileApi { +namespace seafile::api { + class SeaFileApi final : public ISeaFileApi { //base url of the seafile api server std::string base_url; + std::string token; + TokenType token_type = TokenType::BEARER; /** * @brief make a url string from given path @@ -105,16 +108,27 @@ namespace seafile::api { * @return token when success else SeafileException */ tl::expected - authToken(const std::string &username, const std::string &password) override { + auth(const std::string &username, const std::string &password) override { const nlohmann::json body = { {"username", username}, {"password", password} }; - return this->post("/api2/auth-token/", body); + const auto &auth_token = this->post("/api2/auth-token/", body); + if (auth_token) { + this->token = auth_token->token; + if (const auto &server_info = this->serverInfo()) { + if (const auto version = util::Version(server_info->version); version.major() < 11) { + this->token_type = TokenType::TOKEN; + } + } else { + return tl::make_unexpected(server_info.error()); + } + } + return auth_token; } tl::expected - serverInfo() const override{ + serverInfo() const override { const auto response = cpr::Get( cpr::Url{makeUrl("/api2/server-info/")}, cpr::Header{ @@ -126,12 +140,10 @@ namespace seafile::api { /** * @brief get the default repo of user - * @param token token - * @param token_type token type * @return default repo info */ tl::expected - defaultRepo(const std::string &token, const TokenType token_type) const override{ + defaultRepo() const override { const auto auth = makeAuthToken(token, token_type); const auto url = makeUrl("/api2/default-repo/"); const auto response = cpr::Get(cpr::Url{url}, @@ -143,7 +155,7 @@ namespace seafile::api { } tl::expected - repoInfo(const std::string &repo_id, const std::string &token, const TokenType token_type) const override{ + repoInfo(const std::string &repo_id) const override { const auto url = makeUrl(std::format("/api2/repos/{}/", repo_id)); const auto auth = makeAuthToken(token, token_type); @@ -155,17 +167,32 @@ namespace seafile::api { return parseResponse(response); } + tl::expected, SeafileException> + repos(const std::string &type, const std::string &name_contains) const override { + const auto url = makeUrl("/api2/repos/"); + const auto auth = makeAuthToken(token, token_type); + const auto response = cpr::Get(cpr::Url{url}, + cpr::Header{ + {"accept", "application/json"}, + {"authorization", auth} + }, + cpr::Parameters{ + {"type", type}, + {"nameContains", name_contains} + }); + + + return parseResponse >(response); + } + /** * @brief Lists all the items information in the directory * @param repo_id repo id * @param path path to query - * @param token token - * @param token_type token type * @return repo entries */ tl::expected, SeafileException> - repoDir(const std::string &repo_id, const std::string &path, const std::string &token, - const TokenType token_type) const override{ + repoDir(const std::string &repo_id, const std::string &path) const override { const auto url = cpr::Url{makeUrl(std::format("/api2/repos/{}/dir/", repo_id))}; const auto auth = makeAuthToken(token, token_type); @@ -181,8 +208,7 @@ namespace seafile::api { } tl::expected - accountInfo(const std::string &token, - const TokenType token_type) const override{ + accountInfo() const override { const auto url = cpr::Url{makeUrl("/api2/account/info/")}; const auto auth = makeAuthToken(token, token_type); @@ -195,8 +221,7 @@ namespace seafile::api { } tl::expected - fileDetail(const std::string &repo_id, const std::string &path, const std::string &token, - const TokenType token_type) const override{ + fileDetail(const std::string &repo_id, const std::string &path) const override { const auto url = cpr::Url{makeUrl(std::format("/api2/repos/{}/file/detail/", repo_id))}; const auto auth = makeAuthToken(token, token_type); @@ -211,8 +236,7 @@ namespace seafile::api { } tl::expected - downloadFileUrl(const std::string &repo_id, const std::string &path, const std::string &token, - const TokenType token_type) const override{ + downloadFileUrl(const std::string &repo_id, const std::string &path) const override { const auto auth = makeAuthToken(token, token_type); const auto url = cpr::Url{makeUrl(std::format("/api2/repos/{}/file/", repo_id))}; const auto response = cpr::Get(url, @@ -230,10 +254,8 @@ namespace seafile::api { downloadFile(const std::string &repo_id, const std::string &path, const std::optional start, - const std::optional end, - const std::string &token, - const TokenType token_type) const override{ - if (auto download_url = downloadFileUrl(repo_id, path, token, token_type); download_url.has_value()) { + const std::optional end) const override { + if (auto download_url = downloadFileUrl(repo_id, path); download_url.has_value()) { const auto auth = makeAuthToken(token, token_type); const auto url = cpr::Url{download_url.value()}; auto header = cpr::Header{ @@ -255,8 +277,8 @@ namespace seafile::api { } } - tl::expected dirDetail(const std::string &repo_id, const std::string &path, - const std::string &token, const TokenType token_type) const override{ + tl::expected + dirDetail(const std::string &repo_id, const std::string &path) const override { const auto auth = makeAuthToken(token, token_type); const auto url = cpr::Url{makeUrl(std::format("/api/v2.1/repos/{}/dir/detail/", repo_id))}; const auto response = cpr::Get(url, cpr::Header{ diff --git a/include/seafile/api/seafile_api_interface.hpp b/include/seafile/api/seafile_api_interface.hpp index 4477169..1dd0c4d 100644 --- a/include/seafile/api/seafile_api_interface.hpp +++ b/include/seafile/api/seafile_api_interface.hpp @@ -23,94 +23,76 @@ namespace seafile::api { - -class ISeaFileApi { -public: - - virtual ~ISeaFileApi() = default; + class ISeaFileApi { + public: + virtual ~ISeaFileApi() = default; - virtual tl::expected - authToken( - const std::string& username, - const std::string& password - ) = 0; + virtual tl::expected + auth( + const std::string &username, + const std::string &password + ) = 0; - virtual tl::expected - serverInfo() const = 0; + + virtual tl::expected + serverInfo() const = 0; - virtual tl::expected - defaultRepo( - const std::string& token, - TokenType token_type - ) const = 0; + virtual tl::expected + defaultRepo() const = 0; - virtual tl::expected - repoInfo( - const std::string& repo_id, - const std::string& token, - TokenType token_type - ) const = 0; + virtual tl::expected + repoInfo( + const std::string &repo_id + ) const = 0; - virtual tl::expected, SeafileException> - repoDir( - const std::string& repo_id, - const std::string& path, - const std::string& token, - TokenType token_type - ) const = 0; + virtual tl::expected, SeafileException> + repoDir( + const std::string &repo_id, + const std::string &path + ) const = 0; + + virtual tl::expected, SeafileException> + repos(const std::string &type, const std::string &name_contains) const = 0; - virtual tl::expected - accountInfo( - const std::string& token, - TokenType token_type - ) const = 0; + virtual tl::expected + accountInfo() const = 0; - virtual tl::expected - fileDetail( - const std::string& repo_id, - const std::string& path, - const std::string& token, - TokenType token_type - ) const = 0; + virtual tl::expected + fileDetail( + const std::string &repo_id, + const std::string &path + ) const = 0; - virtual tl::expected - downloadFileUrl( - const std::string& repo_id, - const std::string& path, - const std::string& token, - TokenType token_type - ) const = 0; + virtual tl::expected + downloadFileUrl( + const std::string &repo_id, + const std::string &path + ) const = 0; - virtual tl::expected - downloadFile( - const std::string& repo_id, - const std::string& path, - std::optional start, - std::optional end, - const std::string& token, - TokenType token_type - ) const = 0; + virtual tl::expected + downloadFile( + const std::string &repo_id, + const std::string &path, + std::optional start, + std::optional end + ) const = 0; - virtual tl::expected - dirDetail( - const std::string& repo_id, - const std::string& path, - const std::string& token, - TokenType token_type - ) const = 0; - -}; - + virtual tl::expected + dirDetail( + const std::string &repo_id, + const std::string &path + ) const = 0; + }; } #endif //SEADISK_SEAFILE_API_INTERFACE_HPP diff --git a/include/seafile/fs/seafile_file_system.hpp b/include/seafile/fs/seafile_file_system.hpp index 047ff62..e79cd93 100644 --- a/include/seafile/fs/seafile_file_system.hpp +++ b/include/seafile/fs/seafile_file_system.hpp @@ -23,8 +23,6 @@ namespace seafile::fs std::string repo_id; - std::string token; - TokenType token_type = TokenType::TOKEN; std::shared_ptr api; std::wstring volume_label; diff --git a/include/seafile/fs/seafile_store.hpp b/include/seafile/fs/seafile_store.hpp index 74d584e..044cf37 100644 --- a/include/seafile/fs/seafile_store.hpp +++ b/include/seafile/fs/seafile_store.hpp @@ -29,7 +29,7 @@ namespace seafile::fs [[nodiscard]] tl::expected defaultRepoId() const; ///初始化数据库信息 - [[nodiscard]] tl::expected init() const; + [[nodiscard]] tl::expected init(); /// 检查是否已经更新到最新 /// @return true 已经最新,false 需要更新 @@ -39,7 +39,19 @@ namespace seafile::fs tl::expected checkAndSync(const std::string& repo_id); /// 同步元数据 - [[nodiscard]] tl::expected sync(const std::string& repo_id) const; + [[nodiscard]] tl::expected syncRepoEntry(const std::string& repo_id) const; + + /// 同步用户信息 + tl::expected syncUserInfo(); + + /// 同步repo信息 + tl::expected syncRepos(); + + /// 同步 + tl::expected sync(); + + std::filesystem::path storePath() const; + /// 获取路径信息 /// @param path 路径 @@ -51,10 +63,8 @@ namespace seafile::fs struct Database; std::unique_ptr database; std::string api_server; - std::string user; + std::string username; std::string password; - std::string token; - api::TokenType token_type = api::TokenType::TOKEN; api::SeaFileApi api; }; } diff --git a/include/seafile/util/version.hpp b/include/seafile/util/version.hpp new file mode 100644 index 0000000..511d6bd --- /dev/null +++ b/include/seafile/util/version.hpp @@ -0,0 +1,55 @@ +// +// Created by Shell on 2026/7/21. +// + +#ifndef SEADISK_VERSION_HPP +#define SEADISK_VERSION_HPP +#include +#include + +namespace seafile::util +{ + + /** + * @brief Semantic version parser. + * + * Supports: + * 10 + * 10.0 + * 10.0.2 + */ + class Version final + { + public: + Version() = default; + + explicit Version(const std::string& version); + + [[nodiscard]] + int major() const noexcept; + + [[nodiscard]] + std::optional minor() const noexcept; + + [[nodiscard]] + std::optional patch() const noexcept; + + [[nodiscard]] + std::string toString() const; + + bool operator==(const Version& other) const; + + bool operator<(const Version& other) const; + + + private: + void parse(const std::string& version); + + private: + int major_{0}; + std::optional minor_; + std::optional patch_; + }; + +} +#endif //SEADISK_VERSION_HPP diff --git a/include/util/application_path.hpp b/include/util/application_path.hpp new file mode 100644 index 0000000..d532098 --- /dev/null +++ b/include/util/application_path.hpp @@ -0,0 +1,20 @@ +// +// Created by shell on 2025/12/4. +// + +#ifndef SEADISK_APPLICATION_PATH_HPP +#define SEADISK_APPLICATION_PATH_HPP +#include + +namespace shell::util { + class ApplicationPath final { + public: + /** + *@brief 获取当前程序的路径 + * @return 返回当前的程序路径 + */ + static std::filesystem::path get(); + }; +} + +#endif //SEADISK_APPLICATION_PATH_HPP diff --git a/include/util/icu_raii.hpp b/include/util/icu_raii.hpp index a1e0c2f..e2d53cb 100644 --- a/include/util/icu_raii.hpp +++ b/include/util/icu_raii.hpp @@ -2,8 +2,8 @@ // Created by Shell on 2026-04-13. // -#ifndef FORCAST_API_ICU_RAII_H -#define FORCAST_API_ICU_RAII_H +#ifndef SEADISK_ICU_RAII_H +#define SEADISK_ICU_RAII_H #include struct UCharsetDetectorDeleter final @@ -19,4 +19,4 @@ struct UCharsetDetectorDeleter final using UCharsetDetectorPtr = std::unique_ptr; -#endif //FORCAST_API_ICU_RAII_H +#endif //SEADISK_ICU_RAII_H diff --git a/include/util/string_util.hpp b/include/util/string_util.hpp index 2653fae..d22a41b 100644 --- a/include/util/string_util.hpp +++ b/include/util/string_util.hpp @@ -2,8 +2,8 @@ // Created by Shell on 2025-12-17. // -#ifndef FORCAST_API_CLIENT_STRING_UTIL_HPP -#define FORCAST_API_CLIENT_STRING_UTIL_HPP +#ifndef SEADISK_STRING_UTIL_HPP +#define SEADISK_STRING_UTIL_HPP #include #include @@ -24,4 +24,4 @@ namespace shell::util { #endif } -#endif //FORCAST_API_CLIENT_STRING_UTIL_HPP \ No newline at end of file +#endif //SEADISK_STRING_UTIL_HPP \ No newline at end of file diff --git a/src/seafile/fs/seafile_file_system.cpp b/src/seafile/fs/seafile_file_system.cpp index 3b2fc7c..f25cfe7 100644 --- a/src/seafile/fs/seafile_file_system.cpp +++ b/src/seafile/fs/seafile_file_system.cpp @@ -51,11 +51,11 @@ namespace seafile::fs tl::expected SeafileFileSystem::initialize(const std::string& username, const std::string& password) { - const auto authToken = api->authToken(username, password); + const auto authToken = api->auth(username, password); if (authToken.has_value()) { this->token = authToken.value().token; - if (const auto repo = api->defaultRepo(this->token, this->token_type); repo.has_value()) + if (const auto repo = api->defaultRepo(); repo.has_value()) { this->repo_id = repo.value().repo_id.value_or(""); return {}; @@ -102,7 +102,7 @@ namespace seafile::fs NTSTATUS SeafileFileSystem::GetVolumeInfo(FSP_FSCTL_VOLUME_INFO* VolumeInfo) const { - if (const auto userInfo = api->accountInfo(this->token, this->token_type); userInfo.has_value()) + if (const auto userInfo = api->accountInfo(); userInfo.has_value()) { const auto& info = userInfo.value(); VolumeInfo->FreeSize = info.total - info.usage; @@ -137,7 +137,7 @@ namespace seafile::fs if (path == "/") { - auto repo = api->repoInfo(repo_id, token, token_type); + auto repo = api->repoInfo(repo_id); if (!repo.has_value()) return STATUS_OBJECT_NAME_NOT_FOUND; const auto ctx = new SeaFileContext{repo->id, path, SeaFileContext::Type::DIRECTORY, 0, ""}; @@ -147,7 +147,7 @@ namespace seafile::fs return STATUS_SUCCESS; } - if (const auto dirInfo = api->dirDetail(repo_id, path, token, token_type); dirInfo.has_value()) + if (const auto dirInfo = api->dirDetail(repo_id, path); dirInfo.has_value()) { const auto& info = dirInfo.value(); const auto ctx = new SeaFileContext{info.repo_id, info.path, SeaFileContext::Type::DIRECTORY, 0, ""}; @@ -158,7 +158,7 @@ namespace seafile::fs } - if (const auto fileInfo = api->fileDetail(repo_id, path, token, token_type); fileInfo.has_value()) + if (const auto fileInfo = api->fileDetail(repo_id, path); fileInfo.has_value()) { const auto& info = fileInfo.value(); const auto ctx = new SeaFileContext(repo_id, path, SeaFileContext::Type::FILE, info.size, ""); @@ -177,8 +177,7 @@ namespace seafile::fs const auto fc = static_cast(FileContext); auto result = api->downloadFile( fc->repo_id, fc->path, - Offset, Offset + Length - 1, - token, token_type); + Offset, Offset + Length - 1); if (result.has_value()) { const auto& data = result.value(); @@ -197,7 +196,7 @@ namespace seafile::fs const auto fc = static_cast(FileContext); *PBytesTransferred = 0; - if (auto entries_result = api->repoDir(fc->repo_id, fc->path, token, token_type); entries_result.has_value()) + if (auto entries_result = api->repoDir(fc->repo_id, fc->path); entries_result.has_value()) { const auto& entries = entries_result.value(); size_t start = 0; @@ -266,13 +265,13 @@ namespace seafile::fs SetFileInfo(FileInfo, FILE_ATTRIBUTE_DIRECTORY, 0, 0); return STATUS_SUCCESS; } - auto repo = api->repoInfo(fc->repo_id, token, token_type); + auto repo = api->repoInfo(fc->repo_id); if (!repo.has_value()) return STATUS_UNSUCCESSFUL; SetFileInfo(FileInfo, FILE_ATTRIBUTE_DIRECTORY, repo->size, repo->mtime); return STATUS_SUCCESS; } - auto fdetail = api->fileDetail(fc->repo_id, fc->path, token, token_type); + auto fdetail = api->fileDetail(fc->repo_id, fc->path); if (!fdetail.has_value()) return STATUS_UNSUCCESSFUL; SetFileInfo(FileInfo, FILE_ATTRIBUTE_NORMAL, fdetail->size, fdetail->mtime); @@ -311,7 +310,7 @@ namespace seafile::fs return STATUS_OBJECT_NAME_NOT_FOUND; } - auto entries = api->repoDir(fc->repo_id, fc->path, token, token_type); + auto entries = api->repoDir(fc->repo_id, fc->path); if (!entries.has_value()) return STATUS_OBJECT_NAME_NOT_FOUND; diff --git a/src/seafile/fs/seafile_store.cpp b/src/seafile/fs/seafile_store.cpp index ca27cd5..4e9c02c 100644 --- a/src/seafile/fs/seafile_store.cpp +++ b/src/seafile/fs/seafile_store.cpp @@ -7,20 +7,18 @@ #include "seafile/api/repo_info.hpp" #include "seafile/fs/storage/repo_entry.hpp" #include +#include "util/application_path.hpp" // // Created by Shell on 2026/7/16. // -namespace seafile::fs -{ +namespace seafile::fs { using RepoEntry = storage::RepoEntry; using AccountInfo = api::AccountInfo; using RepoInfo = api::RepoInfo; - namespace - { - auto make_storage(const std::string& path) - { + namespace { + auto make_storage(const std::string &path) { // clang-format off return sqlite_orm::make_storage(path, sqlite_orm::make_table("account_info", @@ -30,7 +28,7 @@ namespace seafile::fs sqlite_orm::make_column("name", &AccountInfo::name), sqlite_orm::make_column("total", &AccountInfo::total), sqlite_orm::make_column("usage", &AccountInfo::usage), - sqlite_orm::make_column("login_id", &AccountInfo::login_id), + sqlite_orm::make_column("login_id", &AccountInfo::login_id,sqlite_orm::primary_key()), sqlite_orm::make_column("department", &AccountInfo::department), sqlite_orm::make_column("contact_email", &AccountInfo::contact_email), sqlite_orm::make_column("institution", &AccountInfo::institution), @@ -79,44 +77,34 @@ namespace seafile::fs } } - struct SeafileStore::Database - { + struct SeafileStore::Database { using Storage = decltype(make_storage("")); Storage storage; - explicit Database(const std::string& path) - : storage(make_storage(path)) - { + explicit Database(const std::string &path) + : storage(make_storage(path)) { } }; - tl::expected SeafileStore::auth() - { - if (const auto auth_info = this->api.authToken(this->user, this->password)) - { - this->token = auth_info->token; - this->token_type = api::TokenType::TOKEN; + tl::expected SeafileStore::auth() { + if (const auto auth_info = this->api.auth(this->username, this->password)) { return {}; - } - else - { + } else { return tl::make_unexpected(api::SeafileException(auth_info.error())); } } - SeafileStore::SeafileStore(const std::string& db_path, const std::string& server, const std::string& user, - const std::string& password) : api(server) - { + SeafileStore::SeafileStore(const std::string &db_path, const std::string &server, const std::string &user, + const std::string &password) : api(server) { this->database = std::make_unique(db_path); - this->user = user; + this->username = user; this->password = password; this->api_server = server; } tl::expected SeafileStore::findRepoEntry( - const std::string& repo_id, const std::string& path) - { - auto& storage = this->database->storage; + const std::string &repo_id, const std::string &path) { + auto &storage = this->database->storage; const auto entry = storage.get_optional( sqlite_orm::where( sqlite_orm::and_( @@ -124,20 +112,16 @@ namespace seafile::fs sqlite_orm::c(&RepoEntry::repo_id) == repo_id ) )); - if (entry) - { + if (entry) { return *entry; } return tl::make_unexpected(api::SeafileException("entry not exists")); } - tl::expected SeafileStore::defaultRepoId() const - { - const auto repo = this->api.defaultRepo(this->token,this->token_type); - if (repo) - { - if (repo->exists) - { + tl::expected SeafileStore::defaultRepoId() const { + const auto repo = this->api.defaultRepo(); + if (repo) { + if (repo->exists) { return repo->repo_id.value(); } return tl::make_unexpected(api::SeafileException("No default repo")); @@ -145,78 +129,90 @@ namespace seafile::fs return tl::make_unexpected(api::SeafileException(repo.error())); } - tl::expected SeafileStore::checkIsUpdated(const std::string& repo_id) - { - auto& storage = this->database->storage; + tl::expected SeafileStore::checkIsUpdated(const std::string &repo_id) { + auto &storage = this->database->storage; const auto repo = storage.get_optional( sqlite_orm::where( sqlite_orm::c(&RepoInfo::id) == repo_id )); - if (!repo) - { + if (!repo) { return false; } - const auto& commit_id = repo.value().head_commit_id; - const auto repo_info = this->api.repoInfo(repo_id, this->token, this->token_type); - if (repo_info) - { + const auto &commit_id = repo.value().head_commit_id; + const auto repo_info = this->api.repoInfo(repo_id); + if (repo_info) { return repo_info.value().head_commit_id == commit_id; } return tl::make_unexpected(repo_info.error()); } - tl::expected SeafileStore::sync(const std::string& repo_id) const - { + tl::expected SeafileStore::syncRepoEntry(const std::string &repo_id) const { std::stack entry_stack; std::string path = "/"; std::vector data; entry_stack.push(RepoEntry{.repo_id = repo_id, .path = path}); - while (!entry_stack.empty()) - { + while (!entry_stack.empty()) { const auto parent = entry_stack.top(); entry_stack.pop(); path = parent.path; - auto entries = this->api.repoDir(repo_id, path, this->token, this->token_type); - if (!entries) - { + auto entries = this->api.repoDir(repo_id, path); + if (!entries) { return tl::make_unexpected(entries.error()); } - for (auto& entry : *entries) - { + for (auto &entry: *entries) { const auto entry_info = RepoEntry::from_api_model(repo_id, std::format("{}/{}", path, entry.name), entry); - if (entry_info.type == "dir") - { + if (entry_info.type == "dir") { entry_stack.push(entry_info); } data.push_back(entry_info); } - }try - { - this->database->storage.transaction([&] - { - for (const auto& entry : data) - { + } + try { + this->database->storage.transaction([&] { + for (const auto &entry: data) { this->database->storage.insert(entry); } return true; }); - }catch (std::exception& e) - { + } catch (std::exception &e) { return tl::make_unexpected(api::SeafileException(e.what())); } return {}; } - tl::expected SeafileStore::checkAndSync(const std::string& repo_id) - { - if (!this->checkIsUpdated(repo_id)) - { - return this->sync(repo_id); + tl::expected SeafileStore::checkAndSync(const std::string &repo_id) { + if (!this->checkIsUpdated(repo_id)) { + return this->syncRepoEntry(repo_id); } return {}; } + + tl::expected SeafileStore::init() { + if (const auto &store_path = this->storePath(); !std::filesystem::exists(store_path)) { + this->database->storage.sync_schema(); + return this->sync(); + } + return {}; + } + + tl::expected SeafileStore::syncUserInfo() { + const auto user_info = this->api.accountInfo(); + if (!user_info) { + return tl::make_unexpected(user_info.error()); + } + this->database->storage.replace(*user_info); + return {}; + } + + tl::expected SeafileStore::syncRepos() { + return {}; + } + + std::filesystem::path SeafileStore::storePath() const { + return shell::util::ApplicationPath::get() / "seafile.sqlite"; + } } diff --git a/src/seafile/util/version.cpp b/src/seafile/util/version.cpp new file mode 100644 index 0000000..2a42cbc --- /dev/null +++ b/src/seafile/util/version.cpp @@ -0,0 +1,115 @@ +// +// Created by Shell on 2026/7/21. +// + + +#include +#include +#include +#include + +namespace seafile::util { + Version::Version(const std::string &version) { + parse(version); + } + + + int Version::major() const noexcept { + return major_; + } + + + std::optional Version::minor() const noexcept { + return minor_; + } + + + std::optional Version::patch() const noexcept { + return patch_; + } + + + std::string Version::toString() const { + std::string result = std::to_string(major_); + + if (minor_) { + result += "." + std::to_string(*minor_); + } + + if (patch_) { + result += "." + std::to_string(*patch_); + } + + return result; + } + + bool Version::operator==(const Version &other) const { + return major_ == other.major_ + && minor().value_or(0) == other.minor().value_or(0) + && patch().value_or(0) == other.patch().value_or(0); + } + + + bool Version::operator<(const Version &other) const { + if (major_ != other.major_) { + return major_ < other.major_; + } + + if (minor().value_or(0) != other.minor().value_or(0)) { + return minor().value_or(0) < other.minor().value_or(0); + } + + return patch().value_or(0) < other.patch().value_or(0); + } + + + void Version::parse(const std::string &version) { + std::stringstream ss(version); + std::string part; + + std::vector parts; + + while (std::getline(ss, part, '.')) { + if (part.empty()) { + throw std::invalid_argument( + "Invalid version: " + version + ); + } + + try { + parts.push_back(std::stoi(part)); + } catch (...) { + throw std::invalid_argument( + "Invalid version number: " + part + ); + } + } + + + if (parts.empty()) { + throw std::invalid_argument( + "Version cannot be empty" + ); + } + + + if (parts.size() > 3) { + throw std::invalid_argument( + "Version supports at most 3 components" + ); + } + + + major_ = parts[0]; + + + if (parts.size() >= 2) { + minor_ = parts[1]; + } + + + if (parts.size() >= 3) { + patch_ = parts[2]; + } + } +} diff --git a/src/util/application_path.cpp b/src/util/application_path.cpp new file mode 100644 index 0000000..870dae8 --- /dev/null +++ b/src/util/application_path.cpp @@ -0,0 +1,48 @@ +// +// Created by shell on 2025/12/4. +// + +#include "util/application_path.hpp" +#include +#include +#ifdef _WIN32 +#include +#elif __linux__ +#include +#else +#error "Getting application path operation is only supported on Windows or Linux" +#endif + +namespace shell::util { +#ifdef _WIN32 + std::filesystem::path ApplicationPath::get() { + std::wstring path(20, L'\0'); + while (true) { + const DWORD len = GetModuleFileNameW(nullptr, &path[0], path.size()); + if (len == 0) { + throw std::runtime_error("GetModuleFileName() failed"); + } + if (len < path.size()) { + path.resize(len); + return std::filesystem::path{path}; + } + path.resize(path.size() * 2); + } + } +#elif __linux__ + std::filesystem::path ApplicationPath::get() { + std::vector buffer(256); + while (true) { + const size_t len = readlink("/proc/self/exe", buffer.data(), buffer.size()); + if (len == -1) { + throw std::runtime_error("failed to read /proc/self/exe"); + } + if (len < buffer.size()) { + return std::filesystem::path{std::string(buffer.data(), len)}; + } + + buffer.reserve(buffer.size() * 2); + } + } +#endif +}