'Generic Commit'
This commit is contained in:
parent
ab87fecb39
commit
c4001b0efc
@ -15,6 +15,8 @@
|
|||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
@ -377,3 +379,123 @@ bool GetbinClient::deleteObject(const std::string& hash, const std::string& toke
|
|||||||
worker.join();
|
worker.join();
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
bool GetbinClient::listPackages(std::vector<std::string>& outPackages) {
|
||||||
|
outPackages.clear();
|
||||||
|
|
||||||
|
// Set up SSL configuration
|
||||||
|
std::string ca_path = find_ca_certificates();
|
||||||
|
if (!ca_path.empty()) {
|
||||||
|
std::cout << "[GetbinClient] Found CA certificates at: " << ca_path << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
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, true, false);
|
||||||
|
if (!ca_path.empty()) {
|
||||||
|
std::vector<std::pair<std::string, std::string>> sslConfigs;
|
||||||
|
sslConfigs.push_back({"VerifyCAFile", ca_path});
|
||||||
|
client->addSSLConfigs(sslConfigs);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto req = drogon::HttpRequest::newHttpRequest();
|
||||||
|
req->setMethod(drogon::Get);
|
||||||
|
req->setPath("/dir");
|
||||||
|
|
||||||
|
client->sendRequest(req, [&](drogon::ReqResult result, const drogon::HttpResponsePtr& response) {
|
||||||
|
if (result == drogon::ReqResult::Ok) {
|
||||||
|
int status_code = response->getStatusCode();
|
||||||
|
std::string response_body = std::string(response->getBody());
|
||||||
|
|
||||||
|
if (status_code == 200) {
|
||||||
|
try {
|
||||||
|
json json_response = json::parse(response_body);
|
||||||
|
|
||||||
|
if (json_response.contains("entries") && json_response["entries"].is_array()) {
|
||||||
|
for (const auto& entry : json_response["entries"]) {
|
||||||
|
if (entry.contains("labeltags") && entry["labeltags"].is_array()) {
|
||||||
|
for (const auto& labeltag : entry["labeltags"]) {
|
||||||
|
if (labeltag.is_string()) {
|
||||||
|
std::string name = labeltag.get<std::string>();
|
||||||
|
// Extract tool name (remove architecture suffix if present)
|
||||||
|
size_t colon_pos = name.find(":");
|
||||||
|
if (colon_pos != std::string::npos) {
|
||||||
|
name = name.substr(0, colon_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip empty names
|
||||||
|
if (name.empty()) continue;
|
||||||
|
|
||||||
|
// Add to list if not already present
|
||||||
|
if (std::find(outPackages.begin(), outPackages.end(), name) == outPackages.end()) {
|
||||||
|
outPackages.push_back(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "[GetbinClient::listPackages] JSON parse error: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::cerr << "[GetbinClient::listPackages] HTTP error: status code " << status_code << std::endl;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::cerr << "[GetbinClient::listPackages] HTTP request failed." << std::endl;
|
||||||
|
}
|
||||||
|
done = true;
|
||||||
|
cv.notify_one();
|
||||||
|
loop.quit();
|
||||||
|
}, 10.0);
|
||||||
|
|
||||||
|
loop.loop();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for completion
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(mtx);
|
||||||
|
cv.wait(lock, [&] { return done; });
|
||||||
|
}
|
||||||
|
|
||||||
|
worker.join();
|
||||||
|
|
||||||
|
// Filter out duplicates where we have both toolname and toolname-noarch
|
||||||
|
// Keep the base name and remove the -noarch variant
|
||||||
|
std::vector<std::string> filteredPackages;
|
||||||
|
std::set<std::string> baseNames;
|
||||||
|
|
||||||
|
// First pass: collect all base names (without -noarch)
|
||||||
|
for (const auto& pkg : outPackages) {
|
||||||
|
const std::string suffix = "-noarch";
|
||||||
|
if (pkg.length() < suffix.length() || pkg.substr(pkg.length() - suffix.length()) != suffix) {
|
||||||
|
baseNames.insert(pkg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second pass: add packages, skipping -noarch variants if base exists
|
||||||
|
for (const auto& pkg : outPackages) {
|
||||||
|
const std::string suffix = "-noarch";
|
||||||
|
if (pkg.length() >= suffix.length() && pkg.substr(pkg.length() - suffix.length()) == suffix) {
|
||||||
|
std::string baseName = pkg.substr(0, pkg.length() - suffix.length());
|
||||||
|
if (baseNames.find(baseName) == baseNames.end()) {
|
||||||
|
filteredPackages.push_back(pkg); // Keep -noarch only if no base version
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
filteredPackages.push_back(pkg); // Always keep base versions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outPackages = std::move(filteredPackages);
|
||||||
|
|
||||||
|
// Sort the packages for better display
|
||||||
|
std::sort(outPackages.begin(), outPackages.end());
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
class GetbinClient {
|
class GetbinClient {
|
||||||
public:
|
public:
|
||||||
@ -8,4 +9,5 @@ public:
|
|||||||
bool upload(const std::string& archivePath, std::string& outUrl, std::string& outHash, const std::string& token);
|
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 getHash(const std::string& toolName, const std::string& arch, std::string& outHash);
|
||||||
bool deleteObject(const std::string& hash, const std::string& token);
|
bool deleteObject(const std::string& hash, const std::string& token);
|
||||||
|
bool listPackages(std::vector<std::string>& outPackages);
|
||||||
};
|
};
|
@ -63,9 +63,11 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <map>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <iomanip>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -486,6 +488,109 @@ int unpublish_tool(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int list_packages(int argc, char* argv[]) {
|
||||||
|
GetbinClient getbin;
|
||||||
|
std::vector<std::string> availablePackages;
|
||||||
|
|
||||||
|
std::cout << "Fetching package list from getpkg.xyz..." << std::endl;
|
||||||
|
if (!getbin.listPackages(availablePackages)) {
|
||||||
|
std::cerr << "Failed to fetch package list from server." << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (availablePackages.empty()) {
|
||||||
|
std::cout << "No packages found on server." << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get local installation status
|
||||||
|
std::string home = get_home();
|
||||||
|
std::filesystem::path configDir = std::filesystem::path(home) / ".config/getpkg";
|
||||||
|
std::filesystem::path getpkgDir = std::filesystem::path(home) / ".getpkg";
|
||||||
|
|
||||||
|
// Build list of installed packages with their info
|
||||||
|
std::map<std::string, json> installedPackages;
|
||||||
|
if (std::filesystem::exists(configDir)) {
|
||||||
|
for (const auto& entry : std::filesystem::directory_iterator(configDir)) {
|
||||||
|
if (entry.path().extension() == ".json") {
|
||||||
|
std::string toolName = entry.path().stem().string();
|
||||||
|
try {
|
||||||
|
std::ifstream configFile(entry.path());
|
||||||
|
json toolConfig;
|
||||||
|
configFile >> toolConfig;
|
||||||
|
installedPackages[toolName] = toolConfig;
|
||||||
|
} catch (...) {
|
||||||
|
// Skip malformed config files
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print header
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::cout << "+" << std::string(25, '-') << "+" << std::string(12, '-') << "+" << std::string(18, '-') << "+" << std::string(10, '-') << "+" << std::endl;
|
||||||
|
std::cout << "|" << std::setw(25) << std::left << " Package"
|
||||||
|
<< "|" << std::setw(12) << std::left << " Status"
|
||||||
|
<< "|" << std::setw(18) << std::left << " Local Version"
|
||||||
|
<< "|" << std::setw(10) << std::left << " Latest"
|
||||||
|
<< "|" << std::endl;
|
||||||
|
std::cout << "+" << std::string(25, '-') << "+" << std::string(12, '-') << "+" << std::string(18, '-') << "+" << std::string(10, '-') << "+" << std::endl;
|
||||||
|
|
||||||
|
// Process each package
|
||||||
|
for (const auto& packageName : availablePackages) {
|
||||||
|
std::string status = "Available";
|
||||||
|
std::string localVersion = "-";
|
||||||
|
std::string remoteStatus = "✓";
|
||||||
|
|
||||||
|
auto it = installedPackages.find(packageName);
|
||||||
|
if (it != installedPackages.end()) {
|
||||||
|
// Package is installed
|
||||||
|
json& config = it->second;
|
||||||
|
localVersion = config.value("version", "unknown");
|
||||||
|
std::string localHash = config.value("hash", "");
|
||||||
|
std::string arch = config.value("arch", get_arch());
|
||||||
|
|
||||||
|
// Check if it's up to date by comparing hashes
|
||||||
|
std::string remoteHash;
|
||||||
|
if (getbin.getHash(packageName, arch, remoteHash) && !remoteHash.empty()) {
|
||||||
|
if (localHash == remoteHash) {
|
||||||
|
status = "✓ Latest";
|
||||||
|
remoteStatus = "✓";
|
||||||
|
} else {
|
||||||
|
status = "⚡ Update";
|
||||||
|
remoteStatus = "⚡";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
status = "✓ Local";
|
||||||
|
remoteStatus = "?";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print row
|
||||||
|
std::cout << "|" << std::setw(25) << std::left << (" " + packageName).substr(0, 24)
|
||||||
|
<< "|" << std::setw(12) << std::left << (" " + status).substr(0, 11)
|
||||||
|
<< "|" << std::setw(18) << std::left << (" " + localVersion).substr(0, 17)
|
||||||
|
<< "|" << std::setw(10) << std::left << (" " + remoteStatus).substr(0, 9)
|
||||||
|
<< "|" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print footer
|
||||||
|
std::cout << "+" << std::string(25, '-') << "+" << std::string(12, '-') << "+" << std::string(18, '-') << "+" << std::string(10, '-') << "+" << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
// Print legend
|
||||||
|
std::cout << "Legend:" << std::endl;
|
||||||
|
std::cout << " Available - Package available for installation" << std::endl;
|
||||||
|
std::cout << " ✓ Latest - Installed and up to date" << std::endl;
|
||||||
|
std::cout << " ⚡ Update - Installed but update available" << std::endl;
|
||||||
|
std::cout << " ✓ Local - Installed (remote status unknown)" << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::cout << "Total packages: " << availablePackages.size()
|
||||||
|
<< ", Installed: " << installedPackages.size() << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int clean_tool(int argc, char* argv[]) {
|
int clean_tool(int argc, char* argv[]) {
|
||||||
std::string home = get_home();
|
std::string home = get_home();
|
||||||
std::filesystem::path getpkgDir = std::filesystem::path(home) / ".getpkg";
|
std::filesystem::path getpkgDir = std::filesystem::path(home) / ".getpkg";
|
||||||
@ -699,6 +804,9 @@ void show_help() {
|
|||||||
std::cout << " hash <file_or_directory> Calculate hash of file or directory" << std::endl;
|
std::cout << " hash <file_or_directory> Calculate hash of file or directory" << std::endl;
|
||||||
std::cout << " Outputs raw hash value to stdout" << std::endl;
|
std::cout << " Outputs raw hash value to stdout" << std::endl;
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
|
std::cout << " list List all available packages" << std::endl;
|
||||||
|
std::cout << " Shows installation status and version information" << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
std::cout << " clean Clean up orphaned configs and symlinks" << std::endl;
|
std::cout << " clean Clean up orphaned configs and symlinks" << std::endl;
|
||||||
std::cout << " Removes unused config files and dangling symlinks" << std::endl;
|
std::cout << " Removes unused config files and dangling symlinks" << std::endl;
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
@ -752,6 +860,7 @@ update
|
|||||||
version
|
version
|
||||||
create
|
create
|
||||||
hash
|
hash
|
||||||
|
list
|
||||||
clean
|
clean
|
||||||
help
|
help
|
||||||
)";
|
)";
|
||||||
@ -761,6 +870,8 @@ help
|
|||||||
return create_tool(argc, argv);
|
return create_tool(argc, argv);
|
||||||
} else if (command == "hash") {
|
} else if (command == "hash") {
|
||||||
return hash_command(argc, argv);
|
return hash_command(argc, argv);
|
||||||
|
} else if (command == "list") {
|
||||||
|
return list_packages(argc, argv);
|
||||||
} else if (command == "clean") {
|
} else if (command == "clean") {
|
||||||
return clean_tool(argc, argv);
|
return clean_tool(argc, argv);
|
||||||
} else if (command == "help") {
|
} else if (command == "help") {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user