diff --git a/CMakeLists.txt b/CMakeLists.txt index a95d434..6839fc2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ find_package(argparse CONFIG REQUIRED) find_package(ada CONFIG REQUIRED) find_package(spdlog CONFIG REQUIRED) find_package(ICU REQUIRED COMPONENTS uc i18n) +find_package(SqliteOrm CONFIG REQUIRED) cmake_host_system_information( RESULT WINFSP_DIR @@ -58,6 +59,10 @@ add_executable(seadisk src/main.cpp include/seafile/api/seafile_api_interface.hpp include/seafile/api/seafile_api_interface.hpp include/seafile/api/cached_seafile_api.hpp + include/seafile/fs/seafile_store.hpp + include/seafile/fs/seafile_store.hpp + src/seafile/fs/seafile_store.cpp + include/seafile/fs/storage/repo_entry.hpp ) target_compile_definitions(seadisk PRIVATE @@ -88,6 +93,7 @@ target_link_libraries(seadisk PRIVATE spdlog::spdlog ICU::uc ICU::i18n + sqlite_orm::sqlite_orm ) target_link_options(seadisk PRIVATE diff --git a/include/seafile/fs/seafile_store.hpp b/include/seafile/fs/seafile_store.hpp new file mode 100644 index 0000000..36e7721 --- /dev/null +++ b/include/seafile/fs/seafile_store.hpp @@ -0,0 +1,57 @@ +// +// Created by Shell on 2026/7/16. +// + +#ifndef SEADISK_SEAFILE_STORE_HPP +#define SEADISK_SEAFILE_STORE_HPP +#include "seafile/api/seafile_api.hpp" +#include + +#include "storage/repo_entry.hpp" + +namespace seafile::fs { + + + class SeafileStore final { + public: + + /// construction + /// @param db_path database file path + /// @param server the server url of seafile + /// @param user user + /// @param password password + SeafileStore(const std::string &db_path, const std::string &server, const std::string &user, + const std::string &password); + + SeafileStore(const SeafileStore &) = default; + + tl::expected auth(); + + /// 检查是否已经更新到最新 + /// @return true 已经最新,false 需要更新 + tl::expected checkIsUpdated(const std::string& repo_id); + + /// 检查是否需要更新元素据,如果需要同步则自动同步 + tl::expected checkAndSync(const std::string& repo_id,); + + /// 同步元数据 + tl::expected sync(const std::string& repo_id); + + /// 获取路径信息 + /// @param path 路径 + /// @return 路径信息 + tl::expected findRepoEntry(const std::string& repo_id, const std::string &path); + + private: + struct Database; + std::unique_ptr database; + std::string api_server; + std::string user; + std::string password; + std::string token; + api::TokenType token_type = api::TokenType::TOKEN; + api::SeaFileApi api; + }; +} + +#endif //SEADISK_SEAFILE_STORE_HPP diff --git a/include/seafile/fs/storage/repo_entry.hpp b/include/seafile/fs/storage/repo_entry.hpp new file mode 100644 index 0000000..0632c03 --- /dev/null +++ b/include/seafile/fs/storage/repo_entry.hpp @@ -0,0 +1,61 @@ +// +// Created by Shell on 2026/7/16. +// + +#ifndef SEADISK_STORAGE_REPO_ENTRY_HPP +#define SEADISK_STORAGE_REPO_ENTRY_HPP +#include "seafile/api/repo_entry.hpp" +#include + +namespace seafile::fs::storage { + struct RepoEntry final { + ///repo id + std::string repo_id; + + ///entry path + std::string path; + + /// Entry type ("file" or "dir"). + std::string type; + + /// File or directory name. + std::string name; + + /// Object identifier. + std::string id; + + /// Last modification time (Unix timestamp, seconds). + std::int64_t mtime{}; + + /// Internal email address of the last modifier. + std::optional modifier_email; + + /// File size in bytes. For directories this may be 0. + std::optional size{}; + + /// Whether the file is currently locked. + std::optional is_locked{}; + + /// Lock owner email or identifier. Null if unlocked. + std::optional lock_owner; + + /// Lock timestamp (Unix timestamp, seconds). + std::optional lock_time{}; + + /// Whether the current user owns the lock. + std::optional locked_by_me{}; + + /// Current user's permission (e.g. "r", "rw"). + std::optional permission; + + /// Contact email address of the last modifier. + std::optional modifier_contact_email; + + /// Display name of the last modifier. + std::optional modifier_name; + + /// Whether the entry is starred by the current user. + std::optional starred{}; + }; +} +#endif //SEADISK_STORAGE_REPO_ENTRY_HPP diff --git a/src/seafile/fs/seafile_store.cpp b/src/seafile/fs/seafile_store.cpp new file mode 100644 index 0000000..587cae5 --- /dev/null +++ b/src/seafile/fs/seafile_store.cpp @@ -0,0 +1,128 @@ +#include "seafile/fs/seafile_store.hpp" + +#include +#include + +#include "seafile/api/account_info.hpp" +#include "seafile/api/repo_info.hpp" +#include "seafile/fs/storage/repo_entry.hpp" + +// +// Created by Shell on 2026/7/16. +// +namespace seafile::fs { + using RepoEntry = storage::RepoEntry; + using AccountInfo = api::AccountInfo; + using RepoInfo = api::RepoInfo; + + namespace { + auto make_storage(const std::string &path) { + // clang-format off + return sqlite_orm::make_storage(path, + sqlite_orm::make_table("account_info", + sqlite_orm::make_column("space_usage", &AccountInfo::space_usage), + sqlite_orm::make_column("avatar_url", &AccountInfo::avatar_url), + sqlite_orm::make_column("email", &AccountInfo::email), + 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("department", &AccountInfo::department), + sqlite_orm::make_column("contact_email", &AccountInfo::contact_email), + sqlite_orm::make_column("institution", &AccountInfo::institution), + sqlite_orm::make_column("is_staff", &AccountInfo::is_staff), + sqlite_orm::make_column("file_updates_email_interval", &AccountInfo::file_updates_email_interval), + sqlite_orm::make_column("collaborate_email_interval",&AccountInfo::collaborate_email_interval) + ), + sqlite_orm::make_table("repo_info", + sqlite_orm::make_column("type",&RepoInfo::type), + sqlite_orm::make_column("id",&RepoInfo::id), + sqlite_orm::make_column("owner",&RepoInfo::owner), + sqlite_orm::make_column("name",&RepoInfo::name), + sqlite_orm::make_column("mtime",&RepoInfo::mtime), + sqlite_orm::make_column("size",&RepoInfo::size), + sqlite_orm::make_column("encrypted",&RepoInfo::encrypted), + sqlite_orm::make_column("root",&RepoInfo::root), + sqlite_orm::make_column("permission",&RepoInfo::permission), + sqlite_orm::make_column("modifier_email",&RepoInfo::modifier_email), + sqlite_orm::make_column("modifier_contact_email",&RepoInfo::modifier_contact_email), + sqlite_orm::make_column("modifier_name",&RepoInfo::modifier_name), + sqlite_orm::make_column("file_count",&RepoInfo::file_count), + sqlite_orm::make_column("head_commit_id",&RepoInfo::head_commit_id), + sqlite_orm::make_column("enc_version",&RepoInfo::enc_version), + sqlite_orm::make_column("salt",&RepoInfo::salt), + sqlite_orm::make_column("magic",&RepoInfo::magic), + sqlite_orm::make_column("random_key",&RepoInfo::random_key) + ), + sqlite_orm::make_table("repo_entry", + sqlite_orm::make_column("type",&RepoEntry::type), + sqlite_orm::make_column("name",&RepoEntry::name), + sqlite_orm::make_column("path",&RepoEntry::path), + sqlite_orm::make_column("id",&RepoEntry::id), + sqlite_orm::make_column("mtime",&RepoEntry::mtime), + sqlite_orm::make_column("modifier_email",&RepoEntry::modifier_email), + sqlite_orm::make_column("size",&RepoEntry::size), + sqlite_orm::make_column("is_locked",&RepoEntry::is_locked), + sqlite_orm::make_column("lock_owner",&RepoEntry::lock_owner), + sqlite_orm::make_column("lock_time",&RepoEntry::lock_time), + sqlite_orm::make_column("locked_by_me",&RepoEntry::locked_by_me), + sqlite_orm::make_column("permission",&RepoEntry::permission), + sqlite_orm::make_column("modifier_contact_email",&RepoEntry::modifier_contact_email), + sqlite_orm::make_column("modifier_name",&RepoEntry::modifier_name), + sqlite_orm::make_column("starred",&RepoEntry::starred)) + ); + //clang-format on + } + } + + struct SeafileStore::Database { + using Storage = decltype(make_storage("")); + Storage storage; + + explicit Database(const std::string &path) + : storage(make_storage(path)) { + } + }; + + 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->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 auto entry = storage.get_optional( + sqlite_orm::where( + sqlite_orm::and_( + sqlite_orm::c(&RepoEntry::path) == path, + sqlite_orm::c(&RepoEntry::repo_id) == repo_id + ) + )); + if (entry) { + return *entry; + } + return tl::make_unexpected(api::SeafileException("entry not exists")); + } + + 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) { + return true; + } + 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) { + return repo_info.value().head_commit_id == commit_id; + } + + return tl::make_unexpected(repo_info.error()); + } +} diff --git a/vcpkg.json b/vcpkg.json index 5b419f4..475d4d9 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -27,5 +27,8 @@ }, { "name" : "spdlog", "version>=" : "1.17.0" + }, { + "name" : "sqlite-orm", + "version>=" : "1.9.1" } ] } \ No newline at end of file