From 7b795600c1bb6049583eb793e0e954c887142b66 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 22 Jun 2025 10:43:54 +1200 Subject: [PATCH] 'Generic Commit' --- getpkg/src/GetbinClient.cpp | 77 +++++++++++++++++++++++++++++++ getpkg/src/GetbinClient.hpp | 1 + getpkg/src/main.cpp | 91 +++++++++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+) diff --git a/getpkg/src/GetbinClient.cpp b/getpkg/src/GetbinClient.cpp index 0e5abac..dd60d49 100644 --- a/getpkg/src/GetbinClient.cpp +++ b/getpkg/src/GetbinClient.cpp @@ -297,6 +297,83 @@ bool GetbinClient::getHash(const std::string& toolName, const std::string& arch, cv.wait(lock, [&] { return done; }); } + worker.join(); + return success; +} + +bool GetbinClient::deleteObject(const std::string& hash, const std::string& token) { + bool success = false; + bool done = false; + std::mutex mtx; + std::condition_variable cv; + + std::thread worker([&]() { + trantor::EventLoop loop; + + auto client = drogon::HttpClient::newHttpClient( + "https://" + std::string(SERVER_HOST), + &loop + ); + + // Configure SSL certificates + std::string ca_path = find_ca_certificates(); + if (!ca_path.empty()) { + std::cerr << "[GetbinClient] Found CA certificates at: " << ca_path << std::endl; + std::vector> sslConfigs; + sslConfigs.push_back({"VerifyCAFile", ca_path}); + client->addSSLConfigs(sslConfigs); + } else { + std::cerr << "[GetbinClient] Warning: No system CA certificates found. SSL verification may fail." << std::endl; + } + + client->enableCookies(); + client->setUserAgent("getpkg/1.0"); + + std::string delete_path = "/deleteobject?hash=" + hash; + + auto req = drogon::HttpRequest::newHttpRequest(); + req->setMethod(drogon::Get); + req->setPath(delete_path); + req->addHeader("Authorization", "Bearer " + token); + + client->sendRequest(req, [&](drogon::ReqResult result, const drogon::HttpResponsePtr& response) { + std::lock_guard lock(mtx); + if (result == drogon::ReqResult::Ok && response) { + int status_code = static_cast(response->getStatusCode()); + std::string response_body(response->getBody()); + + if (status_code == 200) { + // Check if the response indicates success + try { + auto resp_json = json::parse(response_body); + if (resp_json.contains("result") && resp_json["result"] == "success") { + success = true; + } + } catch (...) { + // If not JSON, assume success if 200 OK + success = true; + } + } else { + std::cerr << "[GetbinClient::deleteObject] HTTP error: status code " << status_code << std::endl; + std::cerr << "[GetbinClient::deleteObject] Response body: " << response_body << std::endl; + } + } else { + std::cerr << "[GetbinClient::deleteObject] HTTP request failed." << std::endl; + } + done = true; + cv.notify_one(); + loop.quit(); + }, 10.0); // 10 second timeout + + loop.loop(); + }); + + // Wait for completion + { + std::unique_lock lock(mtx); + cv.wait(lock, [&] { return done; }); + } + worker.join(); return success; } \ No newline at end of file diff --git a/getpkg/src/GetbinClient.hpp b/getpkg/src/GetbinClient.hpp index cacb527..0bd6845 100644 --- a/getpkg/src/GetbinClient.hpp +++ b/getpkg/src/GetbinClient.hpp @@ -7,4 +7,5 @@ public: bool download(const std::string& toolName, const std::string& arch, const std::string& outPath); bool upload(const std::string& archivePath, std::string& outUrl, std::string& outHash, const std::string& token); bool getHash(const std::string& toolName, const std::string& arch, std::string& outHash); + bool deleteObject(const std::string& hash, const std::string& token); }; \ No newline at end of file diff --git a/getpkg/src/main.cpp b/getpkg/src/main.cpp index bc3cba9..ac3d4c3 100644 --- a/getpkg/src/main.cpp +++ b/getpkg/src/main.cpp @@ -403,6 +403,89 @@ int hash_command(int argc, char* argv[]) { return 0; } +int unpublish_tool(int argc, char* argv[]) { + if (argc < 3) { + std::cerr << "Usage: getpkg unpublish " << std::endl; + std::cerr << " getpkg unpublish " << std::endl; + return 1; + } + std::string target = argv[2]; + + // Get token + std::string token; + const char* envToken = std::getenv("SOS_WRITE_TOKEN"); + if (envToken && std::strlen(envToken) > 0) { + token = envToken; + } else { + std::string home = get_home(); + std::filesystem::path tokenPath = std::filesystem::path(home) / ".config/getpkg.xyz/write_token.txt"; + if (std::filesystem::exists(tokenPath)) { + std::ifstream tfile(tokenPath); + std::getline(tfile, token); + } else { + std::cout << "Enter getpkg.xyz write token: "; + std::getline(std::cin, token); + std::filesystem::create_directories(tokenPath.parent_path()); + std::ofstream tfile(tokenPath); + tfile << token << std::endl; + } + } + + if (token.empty()) { + std::cerr << "Error: No write token provided" << std::endl; + return 1; + } + + GetbinClient getbin; + std::string hash = target; + + // Check if target looks like a hash (all digits) or a tool name + bool isHash = true; + for (char c : target) { + if (!std::isdigit(c)) { + isHash = false; + break; + } + } + + if (!isHash) { + // Target is a tool name, need to get hash first + std::string toolName = target; + std::string arch = "x86_64"; // default + + // Check if arch is specified (contains :) + if (target.find(':') != std::string::npos) { + toolName = target.substr(0, target.find(':')); + arch = target.substr(target.find(':') + 1); + } + + if (!getbin.getHash(toolName, arch, hash)) { + std::cerr << "Failed to get hash for " << target << std::endl; + return 1; + } + + if (hash.empty()) { + std::cerr << "Tool " << target << " not found" << std::endl; + return 1; + } + + std::cout << "Found hash " << hash << " for " << target << std::endl; + } + + // Delete the object + if (getbin.deleteObject(hash, token)) { + std::cout << "Successfully unpublished " << target; + if (!isHash) { + std::cout << " (hash: " << hash << ")"; + } + std::cout << std::endl; + return 0; + } else { + std::cerr << "Failed to unpublish " << target << std::endl; + return 1; + } +} + int uninstall_tool(int argc, char* argv[]) { if (argc < 3) { std::cerr << "Usage: getpkg uninstall " << std::endl; @@ -468,6 +551,10 @@ void show_help() { std::cout << " ARCH is optional (defaults to 'universal')" << std::endl; std::cout << " Requires SOS_WRITE_TOKEN environment variable" << std::endl; std::cout << std::endl; + std::cout << " unpublish Remove a published tool" << std::endl; + std::cout << " unpublish Remove a published tool by hash" << std::endl; + std::cout << " Requires SOS_WRITE_TOKEN environment variable" << std::endl; + std::cout << std::endl; std::cout << " update Update getpkg and all installed tools" << std::endl; std::cout << std::endl; std::cout << " create Create a new tool project" << std::endl; @@ -485,6 +572,7 @@ void show_help() { std::cout << " getpkg install myapp Install myapp (legacy syntax)" << std::endl; std::cout << " getpkg publish myapp:x86_64 ./build Publish architecture-specific build" << std::endl; std::cout << " getpkg publish myapp ./build Publish universal build" << std::endl; + std::cout << " getpkg unpublish myapp:x86_64 Remove published myapp" << std::endl; std::cout << " getpkg uninstall myapp Remove myapp from system" << std::endl; std::cout << " getpkg update Update everything" << std::endl; std::cout << std::endl; @@ -511,6 +599,8 @@ int main(int argc, char* argv[]) { return uninstall_tool(argc, argv); } else if (command == "publish") { return publish_tool(argc, argv); + } else if (command == "unpublish") { + return unpublish_tool(argc, argv); } else if (command == "update") { return update_tool(argc, argv); } else if (command == "autocomplete") { @@ -518,6 +608,7 @@ int main(int argc, char* argv[]) { if (args.empty()) std::cout << R"(install uninstall publish +unpublish update version create