// // Created by Shell on 2026/6/17. // #ifndef SEADISK_SEAFILE_API_HPP #define SEADISK_SEAFILE_API_HPP #include #include #include #include "exception.hpp" #include #include #include #include "account_info.hpp" #include "auth_token.hpp" #include "default_repo.hpp" #include "dir_info.hpp" #include "file_info.hpp" #include "repo_entry.hpp" #include "repo_info.hpp" #include "server_info.hpp" #include "token_type.hpp" namespace seafile { class SeaFileApi final { //base url of the seafile api server std::string base_url; /** * @brief make a url string from given path * @param path url path * @return the whole url string */ [[nodiscard]] std::string makeUrl(const std::string &path) const { auto url = ada::parse(this->base_url); url->set_pathname(path); return std::string(url->get_href()); } template static tl::expected parseResponse(const cpr::Response &response) { if (response.error) { return tl::make_unexpected( SeafileException( response.error.message)); } if (response.status_code != 200 && response.status_code != 206) { return tl::make_unexpected( SeafileException( std::format("Http Error: {}", response.status_code))); } try { return nlohmann::json::parse(response.text) .get(); } catch (const std::exception &e) { return tl::make_unexpected( SeafileException(e.what())); } } static std::string makeAuthToken(const std::string &token, const TokenType &token_type) { return token_type == TokenType::BEARER ? std::format("Bearer {}", token) : std::format("Token {}", token); } /** * @brief post body and fetch result * @tparam T return type * @param path target url path * @param body post body data * @return T if success else SeafileException */ template tl::expected post(const std::string &path, const nlohmann::json &body) { const auto url = makeUrl(path); const auto response = cpr::Post( cpr::Url{url}, cpr::Header{ {"accept", "application/json"}, {"content-type", "application/json"} }, cpr::Body{body.dump()}); return parseResponse(response); } public: /** * @brief primary constructor * @param base_url base url of seafile server */ explicit SeaFileApi(std::string base_url) : base_url(std::move(base_url)) { } /** * @brief Obtain account token * @param username username * @param password password * @return token when success else SeafileException */ tl::expected authToken(const std::string &username, const std::string &password) { const nlohmann::json body = { {"username", username}, {"password", password} }; return this->post("/api2/auth-token/", body); } tl::expected serverInfo() const { const auto response = cpr::Get( cpr::Url{makeUrl("/api2/server-info/")}, cpr::Header{ {"accept", "application/json"} }); return parseResponse(response); } /** * @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 { const auto auth = makeAuthToken(token, token_type); const auto url = makeUrl("/api2/default-repo/"); const auto response = cpr::Get(cpr::Url{url}, cpr::Header{ {"accept", "application/json"}, {"authorization", auth} }); return parseResponse(response); } tl::expected repoInfo(const std::string &repo_id, const std::string &token, const TokenType token_type) const { const auto url = makeUrl(std::format("/api2/repos/{}/", repo_id)); const auto auth = makeAuthToken(token, token_type); const auto response = cpr::Get(cpr::Url{url}, cpr::Header{ {"accept", "application/json"}, {"authorization", auth} }); 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 { const auto url = cpr::Url{makeUrl(std::format("/api2/repos/{}/dir/", repo_id))}; const auto auth = makeAuthToken(token, token_type); const auto response = cpr::Get(url, cpr::Header{ {"accept", "application/json"}, {"authorization", auth} }, cpr::Parameters{ {"p", path} }); return parseResponse >(response); } tl::expected accountInfo(const std::string &token, const TokenType token_type) const { const auto url = cpr::Url{makeUrl("/api2/account/info/")}; const auto auth = makeAuthToken(token, token_type); const auto response = cpr::Get(url, cpr::Header{ {"accept", "application/json"}, {"authorization", auth} }); return parseResponse(response); } tl::expected fileDetail(const std::string &repo_id, const std::string &path, const std::string &token, const TokenType token_type) const { const auto url = cpr::Url{makeUrl(std::format("/api2/repos/{}/file/detail/", repo_id))}; const auto auth = makeAuthToken(token, token_type); const auto response = cpr::Get(url, cpr::Header{ {"accept", "application/json"}, {"authorization", auth} }, cpr::Parameters{ {"p", path} }); return parseResponse(response); } tl::expected downloadFileUrl(const std::string &repo_id, const std::string &path, const std::string &token, const TokenType token_type) const { 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, cpr::Header{ {"accept", "application/json"}, {"authorization", auth} }, cpr::Parameters{ {"p", path} }); return parseResponse(response); } tl::expected 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 { if (auto download_url = downloadFileUrl(repo_id, path, token, token_type); download_url.has_value()) { const auto auth = makeAuthToken(token, token_type); const auto url = cpr::Url{download_url.value()}; auto header = cpr::Header{ {"authorization", auth} }; const auto range = std::format( "bytes={}-{}", start ? std::to_string(*start) : "", end ? std::to_string(*end) : ""); header.emplace("range", range); if (const auto &response = cpr::Get(url, header); response.error || (response.status_code != 200 && response.status_code != 206)) { return tl::make_unexpected(SeafileException{std::format("HTTP Error:{}", response.status_code)}); } else { return response.text; } } else { return download_url; } } tl::expected dirDetail(const std::string &repo_id, const std::string &path, const std::string &token, const TokenType token_type) const { 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{ {"accept", "application/json"}, {"authorization", auth} }, cpr::Parameters{ {"path", path} }); return parseResponse(response); } }; } #endif //SEADISK_SEAFILE_API_HPP