diff --git a/README.md b/README.md index 59a4b0b..6d20572 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Public read actions: - `wget http://localhost:8123/object/squashkiwi:latest` - `wget http://localhost:8123/object/4528400792837739857` - - `curl http://loclahost:8123/exists/squashkiwi:latest` + - `curl http://localhost:8123/exists/squashkiwi:latest` - `curl http://localhost:8123/exists/4528400792837739857` - You can retrieve the hash for a given label and tag with, e.g.: diff --git a/src/server.cpp b/src/server.cpp index 7cf71d9..5a414a8 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -143,6 +143,11 @@ void Server::setup_routes() { handle_get_hash(req, res); }); + // Check if object exists by hash or label:tag + server_.Get("/exists/(.*)", [this](const httplib::Request& req, httplib::Response& res) { + handle_exists(req, res); + }); + // Get directory listing server_.Get("/dir", [this](const httplib::Request& req, httplib::Response& res) { handle_get_directory(req, res); @@ -529,4 +534,48 @@ void Server::handle_append_tag(const httplib::Request& req, httplib::Response& r res.set_content(response.dump(), "application/json"); } +void Server::handle_exists(const httplib::Request& req, httplib::Response& res) { + const auto& key = req.matches[1].str(); + std::string hash_str = key; + + // Check if the key is a label:tag format + dbEntry entry; + if (db_->get(key, entry)) { + // Got it from label:tag, use the hash + hash_str = entry.hash; + } + + // If we couldn't determine a hash + if (hash_str.empty()) { + nlohmann::json response = {{"result", "success"}, {"exists", false}}; + res.set_content(response.dump(), "application/json"); + return; + } + + // Try to interpret the key as a hash directly + uint64_t hash_value; + try { + hash_value = std::stoull(hash_str); + } catch (const std::invalid_argument& e) { + nlohmann::json response = {{"result", "success"}, {"exists", false}}; + res.set_content(response.dump(), "application/json"); + return; + } + + std::stringstream oss; + oss << hash_value; + if (oss.str() != hash_str) { + nlohmann::json response = {{"result", "success"}, {"exists", false}}; + res.set_content(response.dump(), "application/json"); + return; + } + + // Hash is valid, check if the file exists + std::filesystem::path file_path = config_.object_store_path / oss.str(); + bool exists = std::filesystem::exists(file_path) && std::filesystem::is_regular_file(file_path); + + nlohmann::json response = {{"result", "success"}, {"exists", exists}}; + res.set_content(response.dump(), "application/json"); +} + } // namespace simple_object_storage \ No newline at end of file diff --git a/src/server.hpp b/src/server.hpp index ada71f3..57ab08c 100644 --- a/src/server.hpp +++ b/src/server.hpp @@ -31,6 +31,7 @@ private: void handle_delete_tag(const httplib::Request& req, httplib::Response& res); void handle_delete_object(const httplib::Request& req, httplib::Response& res); void handle_append_tag(const httplib::Request& req, httplib::Response& res); + void handle_exists(const httplib::Request& req, httplib::Response& res); std::pair parse_label_tag(const std::string& label_tag) const; void add_file_metadata(const std::string &file_path, nlohmann::json &metadata) const; diff --git a/test.sh b/test.sh index 243dfee..c328612 100755 --- a/test.sh +++ b/test.sh @@ -3,9 +3,21 @@ SCRIPT_DIR=$(dirname $0) SCRIPT_NAME=$(basename $0) +# FUNCTIONS +function title() { + echo "----------------------------------------" + # Center the text + local text="$1" + local line_length=40 + local text_length=${#text} + local padding=$(( (line_length - text_length) / 2 )) + printf "%*s%s%*s\n" $padding "" "$text" $padding "" + echo "----------------------------------------" +} + function die() { - echo "error: $1" + title "error: $1" exit 1 } @@ -54,11 +66,15 @@ echo "md5sum of ${SCRIPT_DIR}/${SCRIPT_NAME} is ${MD5SUM}" # download the object echo "downloading ${OBJECT_HASH} to ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded1" -curl -s "${BASE_URL}/object/${OBJECT_HASH}" -o ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded1 +if ! curl -s "${BASE_URL}/object/${OBJECT_HASH}" -o ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded1; then + die "failed to download ${OBJECT_HASH}" +fi # download the object again via the label:tag echo "downloading ${BASE_TAG}:test1 to ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded2" -curl -s "${BASE_URL}/object/${BASE_TAG}:test1" -o ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded2 +if ! curl -s "${BASE_URL}/object/${BASE_TAG}:test1" -o ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded2; then + die "failed to download ${BASE_TAG}:test1" +fi # get md5sum of the downloaded file MD5SUM_DOWNLOADED1=$(md5sum ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded1 | awk '{print $1}') @@ -84,15 +100,22 @@ fi # testing we can still download via the hash echo "testing we can still download via the hash" -if ! curl -s "${BASE_URL}/object/${OBJECT_HASH}" -o ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded3 | jq -r '.result' | grep -q 'success'; then - die "failed to download via the hash" +if ! curl -s "${BASE_URL}/exists/${OBJECT_HASH}" | jq -r '.result' | grep -q 'success'; then + die "failed to check exists via the hash" fi -MD5SUM_DOWNLOADED3=$(md5sum ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded3 | awk '{print $1}') -[ "${MD5SUM}" != "${MD5SUM_DOWNLOADED3}" ] && die "md5sums do not match" +# download the object +echo "downloading ${OBJECT_HASH} to ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded4" +if ! curl -s "${BASE_URL}/object/${OBJECT_HASH}" -o ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded4; then + die "failed to download ${OBJECT_HASH}" +fi + +MD5SUM_DOWNLOADED4=$(md5sum ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded4 | awk '{print $1}') +MD5SUM=$(md5sum ${SCRIPT_DIR}/${SCRIPT_NAME} | awk '{print $1}') +[ "${MD5SUM}" != "${MD5SUM_DOWNLOADED4}" ] && die "md5sums do not match" # delete the downloaded file -rm ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded3 +rm ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded4 # delete the object echo "deleting ${OBJECT_HASH}" @@ -103,9 +126,8 @@ fi # verify the object is deleted echo "verifying ${OBJECT_HASH} is deleted" DELETE_RESPONSE=$(curl -s "${BASE_URL}/object/${OBJECT_HASH}") -echo "delete response: ${DELETE_RESPONSE}" -if ! echo "${DELETE_RESPONSE}" | jq -r '.result' | grep -q 'success'; then +if ! echo "${DELETE_RESPONSE}" | jq -r '.result' | grep -q 'error'; then die "failed to verify ${OBJECT_HASH} is deleted" fi - +title "ALL TESTS PASSED" diff --git a/test.sh.downloaded b/test.sh.downloaded deleted file mode 100644 index 14ac425..0000000 --- a/test.sh.downloaded +++ /dev/null @@ -1,107 +0,0 @@ -#! /bin/bash - -SCRIPT_DIR=$(dirname $0) -SCRIPT_NAME=$(basename $0) - - -function die() { - echo "error: $1" - exit 1 -} - - -# test jq is installed -if ! command -v jq &> /dev/null; then - echo "jq could not be found" - echo "sudo apt-get install jq" - exit 1 -fi - -# read ~/.config/simple_object_storage/config.json -CONFIG_PATH="${HOME}/.config/simple_object_storage/config.json" -if [ ! -f "${CONFIG_PATH}" ]; then - echo "config file not found at ${CONFIG_PATH}" - exit 1 -fi -CONFIG=$(cat "${CONFIG_PATH}") - -# get the host and port from the config -HOST=$(echo $CONFIG | jq -r '.host') -PORT=$(echo $CONFIG | jq -r '.port') - -# extract the first write token from the config -WRITE_TOKEN=$(echo $CONFIG | jq -r '.write_tokens[0]') - -BASE_URL="http://${HOST}:${PORT}" - -BASE_TAG="autotest" - - # test every action in the README.md file, leaving the system in the same state it was found - # and print the output of each action - -# upload this script as an object -echo "uploading ${SCRIPT_DIR}/${SCRIPT_NAME} to ${BASE_TAG}:test1" -OBJECT_HASH=$(curl -s "${BASE_URL}/upload?token=${WRITE_TOKEN}&labeltag=${BASE_TAG}:test1&filename=${SCRIPT_NAME}" -T ${SCRIPT_DIR}/${SCRIPT_NAME} | jq -r '.hash') -echo "received hash ${OBJECT_HASH}" - -# get md5sum of this file -MD5SUM=$(md5sum ${SCRIPT_DIR}/${SCRIPT_NAME} | awk '{print $1}') -echo "md5sum of ${SCRIPT_DIR}/${SCRIPT_NAME} is ${MD5SUM}" - -# download the object -echo "downloading ${OBJECT_HASH} to ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded" -curl -s "${BASE_URL}/object/${OBJECT_HASH}" -o ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded - -# download the object again via the label:tag -echo "downloading ${BASE_TAG}:test1 to ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded2" -curl -s "${BASE_URL}/object/${BASE_TAG}:test1" -o ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded2 - -# get md5sum of the downloaded file -MD5SUM_DOWNLOADED=$(md5sum ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded | awk '{print $1}') -echo "md5sum of ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded is ${MD5SUM_DOWNLOADED}" -[ "${MD5SUM}" != "${MD5SUM_DOWNLOADED}" ] && die "md5sums do not match" -MD5SUM_DOWNLOADED2=$(md5sum ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded2 | awk '{print $1}') -[ "${MD5SUM}" != "${MD5SUM_DOWNLOADED2}" ] && die "md5sums do not match" - -rm ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded1 -rm ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded2 - -# delete the object tags -echo "deleting tag ${BASE_TAG}:test1" -if ! curl -s "${BASE_URL}/deletetag?token=${WRITE_TOKEN}&labeltag=${BASE_TAG}:test1" | jq -r '.result' | grep -q 'success'; then - die "failed to delete tag ${BASE_TAG}:test1" -fi - -# testing we CANT download via the label:tag -echo "testing we CANT download via the label:tag" -if curl -s "${BASE_URL}/object/${BASE_TAG}:test1" | jq -r '.result' | grep -q 'success'; then - die "downloaded via the label:tag" -fi - -# testing we can still download via the hash -echo "testing we can still download via the hash" -if ! curl -s "${BASE_URL}/object/${OBJECT_HASH}" -o ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded3 | jq -r '.result' | grep -q 'success'; then - die "failed to download via the hash" -fi - -MD5SUM_DOWNLOADED3=$(md5sum ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded3 | awk '{print $1}') -[ "${MD5SUM}" != "${MD5SUM_DOWNLOADED3}" ] && die "md5sums do not match" - -# delete the downloaded file -rm ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded3 - -# delete the object -echo "deleting ${OBJECT_HASH}" -if ! curl -s "${BASE_URL}/deleteobject?token=${WRITE_TOKEN}&hash=${OBJECT_HASH}" | jq -r '.result' | grep -q 'success'; then - die "failed to delete ${OBJECT_HASH}" -fi - -# verify the object is deleted -echo "verifying ${OBJECT_HASH} is deleted" -DELETE_RESPONSE=$(curl -s "${BASE_URL}/object/${OBJECT_HASH}") -echo "delete response: ${DELETE_RESPONSE}" -if ! echo "${DELETE_RESPONSE}" | jq -r '.result' | grep -q 'success'; then - die "failed to verify ${OBJECT_HASH} is deleted" -fi - - diff --git a/test.sh.downloaded3 b/test.sh.downloaded3 deleted file mode 100644 index 243dfee..0000000 --- a/test.sh.downloaded3 +++ /dev/null @@ -1,111 +0,0 @@ -#! /bin/bash - -SCRIPT_DIR=$(dirname $0) -SCRIPT_NAME=$(basename $0) - - -function die() { - echo "error: $1" - exit 1 -} - - -# test jq is installed -if ! command -v jq &> /dev/null; then - echo "jq could not be found" - echo "sudo apt-get install jq" - exit 1 -fi - -# read ~/.config/simple_object_storage/config.json -CONFIG_PATH="${HOME}/.config/simple_object_storage/config.json" -if [ ! -f "${CONFIG_PATH}" ]; then - echo "config file not found at ${CONFIG_PATH}" - exit 1 -fi -CONFIG=$(cat "${CONFIG_PATH}") - -# get the host and port from the config -HOST=$(echo $CONFIG | jq -r '.host') -PORT=$(echo $CONFIG | jq -r '.port') - -# extract the first write token from the config -WRITE_TOKEN=$(echo $CONFIG | jq -r '.write_tokens[0]') - -BASE_URL="http://${HOST}:${PORT}" - -BASE_TAG="autotest" - - # test every action in the README.md file, leaving the system in the same state it was found - # and print the output of each action - -# upload this script as an object -echo "uploading ${SCRIPT_DIR}/${SCRIPT_NAME} to ${BASE_TAG}:test1" -OBJECT_HASH=$(curl -s "${BASE_URL}/upload?token=${WRITE_TOKEN}&labeltag=${BASE_TAG}:test1&filename=${SCRIPT_NAME}" -T ${SCRIPT_DIR}/${SCRIPT_NAME} | jq -r '.hash') -echo "received hash ${OBJECT_HASH}" - -# check the hash matches. -CHECK_HASH=$(curl -s "${BASE_URL}/hash/${BASE_TAG}:test1" | jq -r '.hash') -[ "${OBJECT_HASH}" != "${CHECK_HASH}" ] && die "hash does not match" - -# get md5sum of this file -MD5SUM=$(md5sum ${SCRIPT_DIR}/${SCRIPT_NAME} | awk '{print $1}') -echo "md5sum of ${SCRIPT_DIR}/${SCRIPT_NAME} is ${MD5SUM}" - -# download the object -echo "downloading ${OBJECT_HASH} to ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded1" -curl -s "${BASE_URL}/object/${OBJECT_HASH}" -o ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded1 - -# download the object again via the label:tag -echo "downloading ${BASE_TAG}:test1 to ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded2" -curl -s "${BASE_URL}/object/${BASE_TAG}:test1" -o ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded2 - -# get md5sum of the downloaded file -MD5SUM_DOWNLOADED1=$(md5sum ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded1 | awk '{print $1}') -echo "md5sum of ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded1 is ${MD5SUM_DOWNLOADED1}" -[ "${MD5SUM}" != "${MD5SUM_DOWNLOADED1}" ] && die "md5sums do not match" -MD5SUM_DOWNLOADED2=$(md5sum ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded2 | awk '{print $1}') -[ "${MD5SUM}" != "${MD5SUM_DOWNLOADED2}" ] && die "md5sums do not match" - -rm ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded1 -rm ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded2 - -# delete the object tags -echo "deleting tag ${BASE_TAG}:test1" -if ! curl -s "${BASE_URL}/deletetag?token=${WRITE_TOKEN}&labeltag=${BASE_TAG}:test1" | jq -r '.result' | grep -q 'success'; then - die "failed to delete tag ${BASE_TAG}:test1" -fi - -# testing we CANT download via the label:tag -echo "testing we CANT download via the label:tag" -if curl -s "${BASE_URL}/object/${BASE_TAG}:test1" | jq -r '.result' | grep -q 'success'; then - die "downloaded via the label:tag" -fi - -# testing we can still download via the hash -echo "testing we can still download via the hash" -if ! curl -s "${BASE_URL}/object/${OBJECT_HASH}" -o ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded3 | jq -r '.result' | grep -q 'success'; then - die "failed to download via the hash" -fi - -MD5SUM_DOWNLOADED3=$(md5sum ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded3 | awk '{print $1}') -[ "${MD5SUM}" != "${MD5SUM_DOWNLOADED3}" ] && die "md5sums do not match" - -# delete the downloaded file -rm ${SCRIPT_DIR}/${SCRIPT_NAME}.downloaded3 - -# delete the object -echo "deleting ${OBJECT_HASH}" -if ! curl -s "${BASE_URL}/deleteobject?token=${WRITE_TOKEN}&hash=${OBJECT_HASH}" | jq -r '.result' | grep -q 'success'; then - die "failed to delete ${OBJECT_HASH}" -fi - -# verify the object is deleted -echo "verifying ${OBJECT_HASH} is deleted" -DELETE_RESPONSE=$(curl -s "${BASE_URL}/object/${OBJECT_HASH}") -echo "delete response: ${DELETE_RESPONSE}" -if ! echo "${DELETE_RESPONSE}" | jq -r '.result' | grep -q 'success'; then - die "failed to verify ${OBJECT_HASH} is deleted" -fi - -