Yay
This commit is contained in:
@@ -4,14 +4,20 @@ project(dropshell_template_registry)
|
|||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
# Find required packages
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
find_package(SQLite3 REQUIRED)
|
||||||
|
|
||||||
# Find all source files in src directory
|
# Find all source files in src directory
|
||||||
file(GLOB_RECURSE SOURCES
|
file(GLOB_RECURSE SOURCES
|
||||||
"src/*.cpp"
|
"src/*.cpp"
|
||||||
|
"hash.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Find all header files in src directory
|
# Find all header files in src directory
|
||||||
file(GLOB_RECURSE HEADERS
|
file(GLOB_RECURSE HEADERS
|
||||||
"src/*.hpp"
|
"src/*.hpp"
|
||||||
|
"hash.hpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add include directories
|
# Add include directories
|
||||||
@@ -25,16 +31,19 @@ add_executable(dropshell_template_registry ${SOURCES})
|
|||||||
|
|
||||||
# Link libraries
|
# Link libraries
|
||||||
target_link_libraries(dropshell_template_registry
|
target_link_libraries(dropshell_template_registry
|
||||||
pthread
|
Threads::Threads # Use modern Threads target
|
||||||
ssl
|
SQLite::SQLite3 # Use SQLite3 target
|
||||||
crypto
|
# ssl and crypto might still be needed by httplib if used
|
||||||
|
# ssl
|
||||||
|
# crypto
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set compile options for static linking
|
# Set compile options for static linking
|
||||||
set_target_properties(dropshell_template_registry PROPERTIES
|
# Note: Static linking SQLite might require additional setup depending on the system
|
||||||
LINK_SEARCH_START_STATIC ON
|
# set_target_properties(dropshell_template_registry PROPERTIES
|
||||||
LINK_SEARCH_END_STATIC ON
|
# LINK_SEARCH_START_STATIC ON
|
||||||
)
|
# LINK_SEARCH_END_STATIC ON
|
||||||
|
# )
|
||||||
|
|
||||||
# Install target
|
# Install target
|
||||||
install(TARGETS dropshell_template_registry
|
install(TARGETS dropshell_template_registry
|
||||||
|
26
Dockerfile
26
Dockerfile
@@ -6,7 +6,7 @@ RUN apk add --no-cache \
|
|||||||
cmake \
|
cmake \
|
||||||
git \
|
git \
|
||||||
musl-dev \
|
musl-dev \
|
||||||
nlohmann-json-dev
|
sqlite-dev
|
||||||
|
|
||||||
# Copy source code
|
# Copy source code
|
||||||
COPY . /src
|
COPY . /src
|
||||||
@@ -14,28 +14,24 @@ WORKDIR /src
|
|||||||
|
|
||||||
# Build
|
# Build
|
||||||
RUN mkdir build && cd build && \
|
RUN mkdir build && cd build && \
|
||||||
cmake .. && \
|
cmake .. -DCMAKE_EXE_LINKER_FLAGS="-static" && \
|
||||||
make -j$(nproc)
|
make -j$(nproc)
|
||||||
|
|
||||||
# Create final image
|
# Create final image
|
||||||
FROM alpine:latest
|
FROM scratch
|
||||||
|
|
||||||
# Install runtime dependencies
|
|
||||||
RUN apk add --no-cache \
|
|
||||||
libstdc++
|
|
||||||
|
|
||||||
# Copy binary from builder
|
# Copy binary from builder
|
||||||
COPY --from=builder /src/build/dropshell_template_registry /usr/local/bin/
|
COPY --from=builder /src/build/dropshell_template_registry /dropshell_template_registry
|
||||||
|
|
||||||
# Create data directory
|
# Create data directory (though mounting is preferred)
|
||||||
RUN mkdir -p /data
|
# RUN mkdir -p /data
|
||||||
VOLUME /data
|
# VOLUME /data
|
||||||
|
|
||||||
# Set working directory
|
# Set working directory (optional for scratch)
|
||||||
WORKDIR /data
|
# WORKDIR /data
|
||||||
|
|
||||||
# Expose port
|
# Expose port
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
# Run server
|
# Run server (assuming config is mounted at /data/config.json)
|
||||||
CMD ["dropshell_template_registry", "/data/config.json"]
|
ENTRYPOINT ["/dropshell_template_registry", "/data/config.json"]
|
4928
src/litecask.hpp
4928
src/litecask.hpp
File diff suppressed because it is too large
Load Diff
247
src/server.cpp
247
src/server.cpp
@@ -8,6 +8,8 @@
|
|||||||
#include <chrono> // For seeding random number generator
|
#include <chrono> // For seeding random number generator
|
||||||
#include <vector> // For getAllKeys
|
#include <vector> // For getAllKeys
|
||||||
#include <string_view> // For litecask values
|
#include <string_view> // For litecask values
|
||||||
|
#include <sqlite3.h> // Include SQLite
|
||||||
|
#include <stdexcept> // For std::runtime_error
|
||||||
|
|
||||||
namespace dropshell {
|
namespace dropshell {
|
||||||
|
|
||||||
@@ -18,9 +20,11 @@ public:
|
|||||||
~ScopeFileDeleter() {
|
~ScopeFileDeleter() {
|
||||||
if (!released_) {
|
if (!released_) {
|
||||||
try {
|
try {
|
||||||
std::filesystem::remove(path_);
|
if (std::filesystem::exists(path_)) {
|
||||||
|
std::filesystem::remove(path_);
|
||||||
|
}
|
||||||
} catch (const std::filesystem::filesystem_error& e) {
|
} catch (const std::filesystem::filesystem_error& e) {
|
||||||
std::cerr << "Error deleting temp file: " << e.what() << std::endl;
|
std::cerr << "Error deleting temp file: " << path_ << " - " << e.what() << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -30,8 +34,48 @@ private:
|
|||||||
bool released_;
|
bool released_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Helper to execute SQL and check for errors
|
||||||
|
void execute_sql(sqlite3* db, const char* sql, const std::string& error_msg_prefix) {
|
||||||
|
char* err_msg = nullptr;
|
||||||
|
int rc = sqlite3_exec(db, sql, 0, 0, &err_msg);
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
std::string error_details = error_msg_prefix + ": " + (err_msg ? err_msg : "Unknown error");
|
||||||
|
sqlite3_free(err_msg);
|
||||||
|
throw std::runtime_error(error_details);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Server::init_db() {
|
||||||
|
db_path_ = config_.object_store_path / "index.db";
|
||||||
|
int rc = sqlite3_open(db_path_.c_str(), &db_);
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
std::cerr << "Failed to open/create SQLite database '" << db_path_ << "': " << sqlite3_errmsg(db_) << std::endl;
|
||||||
|
db_ = nullptr; // Ensure db_ is null if open failed
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Enable WAL mode for better concurrency
|
||||||
|
execute_sql(db_, "PRAGMA journal_mode=WAL;", "Failed to set WAL mode");
|
||||||
|
|
||||||
|
// Create table if it doesn't exist
|
||||||
|
const char* create_table_sql =
|
||||||
|
"CREATE TABLE IF NOT EXISTS objects ("
|
||||||
|
"label_tag TEXT PRIMARY KEY UNIQUE NOT NULL, "
|
||||||
|
"hash TEXT NOT NULL);";
|
||||||
|
execute_sql(db_, create_table_sql, "Failed to create objects table");
|
||||||
|
|
||||||
|
} catch (const std::runtime_error& e) {
|
||||||
|
std::cerr << "Database initialization error: " << e.what() << std::endl;
|
||||||
|
sqlite3_close(db_);
|
||||||
|
db_ = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Server::Server(const ServerConfig& config)
|
Server::Server(const ServerConfig& config)
|
||||||
: config_(config), running_(false), _isInitialized(false) {
|
: config_(config), running_(false), db_(nullptr) {
|
||||||
// Ensure object store directory exists
|
// Ensure object store directory exists
|
||||||
try {
|
try {
|
||||||
std::filesystem::create_directories(config_.object_store_path);
|
std::filesystem::create_directories(config_.object_store_path);
|
||||||
@@ -40,33 +84,24 @@ Server::Server(const ServerConfig& config)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up and open the litecask datastore
|
// Initialize the database
|
||||||
datastore_path_ = config_.object_store_path / "index";
|
if (!init_db()) {
|
||||||
try {
|
// Error already printed in init_db
|
||||||
std::filesystem::create_directories(datastore_path_);
|
// Consider throwing or setting an error state
|
||||||
} catch (const std::filesystem::filesystem_error& e) {
|
|
||||||
std::cerr << "Failed to create datastore directory: " << datastore_path_ << " - " << e.what() << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open datastore
|
|
||||||
if (datastore_.open(datastore_path_.string()) != litecask::Status::Ok) {
|
|
||||||
std::cerr << "Failed to open litecask datastore at " << datastore_path_ << std::endl;
|
|
||||||
} else {
|
|
||||||
_isInitialized = true; // Mark as initialized if open succeeded
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Server::~Server() {
|
Server::~Server() {
|
||||||
stop();
|
stop();
|
||||||
if (_isInitialized) {
|
if (db_) {
|
||||||
datastore_.close(); // Close the datastore only if initialized
|
sqlite3_close(db_); // Close the database connection
|
||||||
|
db_ = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Server::start() {
|
bool Server::start() {
|
||||||
if (!_isInitialized) { // Check initialization flag
|
if (!db_) { // Check if DB initialization failed
|
||||||
std::cerr << "Datastore is not initialized. Cannot start server." << std::endl;
|
std::cerr << "Database is not initialized. Cannot start server." << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
setup_routes();
|
setup_routes();
|
||||||
@@ -90,9 +125,14 @@ void Server::stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Server::setup_routes() {
|
void Server::setup_routes() {
|
||||||
|
const std::string welcome_page = "<html><body><h1>Dropshell Template Registry</h1></body></html>";
|
||||||
// Welcome page
|
// Welcome page
|
||||||
server_.Get("/index.html", [](const httplib::Request&, httplib::Response& res) {
|
server_.Get("/", [welcome_page](const httplib::Request&, httplib::Response& res) {
|
||||||
res.set_content("<html><body><h1>Dropshell Template Registry</h1></body></html>", "text/html");
|
res.set_content(welcome_page, "text/html");
|
||||||
|
});
|
||||||
|
|
||||||
|
server_.Get("/index.html", [welcome_page](const httplib::Request&, httplib::Response& res) {
|
||||||
|
res.set_content(welcome_page, "text/html");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get object by hash or label:tag
|
// Get object by hash or label:tag
|
||||||
@@ -106,9 +146,9 @@ void Server::setup_routes() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Get directory listing
|
// Get directory listing
|
||||||
// server_.Get("/dir", [this](const httplib::Request& req, httplib::Response& res) {
|
server_.Get("/dir", [this](const httplib::Request& req, httplib::Response& res) {
|
||||||
// handle_get_directory(req, res);
|
handle_get_directory(req, res);
|
||||||
// });
|
});
|
||||||
|
|
||||||
// Upload object
|
// Upload object
|
||||||
server_.Put("/([^/]+)/(.*)", [this](const httplib::Request& req, httplib::Response& res) { // Adjusted regex slightly for label:tag
|
server_.Put("/([^/]+)/(.*)", [this](const httplib::Request& req, httplib::Response& res) { // Adjusted regex slightly for label:tag
|
||||||
@@ -119,7 +159,6 @@ void Server::setup_routes() {
|
|||||||
void Server::handle_get_object(const httplib::Request& req, httplib::Response& res) {
|
void Server::handle_get_object(const httplib::Request& req, httplib::Response& res) {
|
||||||
const auto& key = req.matches[1].str();
|
const auto& key = req.matches[1].str();
|
||||||
std::string hash_str;
|
std::string hash_str;
|
||||||
lcVector<uint8_t> value_vec; // Use lcVector for litecask::get
|
|
||||||
|
|
||||||
// Check if the key looks like a hash (numeric)
|
// Check if the key looks like a hash (numeric)
|
||||||
bool is_hash_lookup = true;
|
bool is_hash_lookup = true;
|
||||||
@@ -131,30 +170,58 @@ void Server::handle_get_object(const httplib::Request& req, httplib::Response& r
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!is_hash_lookup) {
|
if (!is_hash_lookup) {
|
||||||
// Lookup by label:tag in the datastore
|
// Lookup by label:tag in the SQLite database
|
||||||
auto rc = datastore_.get(key.data(), key.size(), value_vec); // Use .data() and .size()
|
sqlite3_stmt* stmt = nullptr;
|
||||||
if (rc == litecask::Status::Ok) {
|
const char* sql = "SELECT hash FROM objects WHERE label_tag = ?;";
|
||||||
hash_str.assign(value_vec.begin(), value_vec.end()); // Convert vector to string
|
int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
|
||||||
} else if (rc == litecask::Status::EntryNotFound) {
|
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
std::cerr << "Failed to prepare statement (get hash): " << sqlite3_errmsg(db_) << std::endl;
|
||||||
|
res.status = 500;
|
||||||
|
res.set_content("Database error preparing statement", "text/plain");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_bind_text(stmt, 1, key.c_str(), -1, SQLITE_STATIC);
|
||||||
|
|
||||||
|
rc = sqlite3_step(stmt);
|
||||||
|
if (rc == SQLITE_ROW) {
|
||||||
|
const unsigned char* text = sqlite3_column_text(stmt, 0);
|
||||||
|
if (text) {
|
||||||
|
hash_str = reinterpret_cast<const char*>(text);
|
||||||
|
}
|
||||||
|
} else if (rc == SQLITE_DONE) {
|
||||||
|
// Not found
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
res.status = 404;
|
res.status = 404;
|
||||||
res.set_content("Object not found (label:tag)", "text/plain");
|
res.set_content("Object not found (label:tag)", "text/plain");
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "Datastore get error: " << static_cast<int>(rc) << std::endl;
|
std::cerr << "Failed to execute statement (get hash): " << sqlite3_errmsg(db_) << std::endl;
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
res.status = 500;
|
res.status = 500;
|
||||||
res.set_content("Datastore error on get", "text/plain");
|
res.set_content("Database error executing statement", "text/plain");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Lookup directly by hash
|
// Lookup directly by hash
|
||||||
hash_str = key;
|
hash_str = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hash_str.empty()) {
|
||||||
|
// Should have been caught earlier if not found, but as a safeguard
|
||||||
|
res.status = 404;
|
||||||
|
res.set_content("Object hash could not be determined", "text/plain");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Construct the file path using the hash string
|
// Construct the file path using the hash string
|
||||||
std::filesystem::path file_path = config_.object_store_path / hash_str;
|
std::filesystem::path file_path = config_.object_store_path / hash_str;
|
||||||
if (!std::filesystem::exists(file_path) || !std::filesystem::is_regular_file(file_path)) {
|
if (!std::filesystem::exists(file_path) || !std::filesystem::is_regular_file(file_path)) {
|
||||||
res.status = 404;
|
res.status = 404;
|
||||||
res.set_content("Object file not found", "text/plain");
|
res.set_content("Object file not found for hash: " + hash_str, "text/plain");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,22 +232,71 @@ void Server::handle_get_object(const httplib::Request& req, httplib::Response& r
|
|||||||
|
|
||||||
void Server::handle_get_hash(const httplib::Request& req, httplib::Response& res) {
|
void Server::handle_get_hash(const httplib::Request& req, httplib::Response& res) {
|
||||||
const auto& label_tag = req.matches[1].str();
|
const auto& label_tag = req.matches[1].str();
|
||||||
lcVector<uint8_t> value_vec; // Use lcVector for litecask::get
|
|
||||||
auto rc = datastore_.get(label_tag.data(), label_tag.size(), value_vec); // Use .data() and .size()
|
|
||||||
|
|
||||||
if (rc == litecask::Status::Ok) {
|
sqlite3_stmt* stmt = nullptr;
|
||||||
res.set_content(std::string(value_vec.begin(), value_vec.end()), "text/plain"); // Convert vector to string
|
const char* sql = "SELECT hash FROM objects WHERE label_tag = ?;";
|
||||||
} else if (rc == litecask::Status::EntryNotFound) {
|
int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
|
||||||
|
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
std::cerr << "Failed to prepare statement (get hash direct): " << sqlite3_errmsg(db_) << std::endl;
|
||||||
|
res.status = 500;
|
||||||
|
res.set_content("Database error preparing statement", "text/plain");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_bind_text(stmt, 1, label_tag.c_str(), -1, SQLITE_STATIC);
|
||||||
|
|
||||||
|
rc = sqlite3_step(stmt);
|
||||||
|
if (rc == SQLITE_ROW) {
|
||||||
|
const unsigned char* text = sqlite3_column_text(stmt, 0);
|
||||||
|
if (text) {
|
||||||
|
res.set_content(reinterpret_cast<const char*>(text), "text/plain");
|
||||||
|
}
|
||||||
|
} else if (rc == SQLITE_DONE) {
|
||||||
res.status = 404;
|
res.status = 404;
|
||||||
res.set_content("Label:tag not found", "text/plain");
|
res.set_content("Label:tag not found", "text/plain");
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "Datastore get error: " << static_cast<int>(rc) << std::endl;
|
std::cerr << "Failed to execute statement (get hash direct): " << sqlite3_errmsg(db_) << std::endl;
|
||||||
res.status = 500;
|
res.status = 500;
|
||||||
res.set_content("Datastore error on get", "text/plain");
|
res.set_content("Database error executing statement", "text/plain");
|
||||||
}
|
}
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle_get_directory function removed
|
void Server::handle_get_directory(const httplib::Request& /*req*/, httplib::Response& res) {
|
||||||
|
std::stringstream ss;
|
||||||
|
sqlite3_stmt* stmt = nullptr;
|
||||||
|
const char* sql = "SELECT label_tag, hash FROM objects;";
|
||||||
|
int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
|
||||||
|
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
std::cerr << "Failed to prepare statement (get dir): " << sqlite3_errmsg(db_) << std::endl;
|
||||||
|
res.status = 500;
|
||||||
|
res.set_content("Database error preparing statement", "text/plain");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
|
||||||
|
const unsigned char* label_tag_text = sqlite3_column_text(stmt, 0);
|
||||||
|
const unsigned char* hash_text = sqlite3_column_text(stmt, 1);
|
||||||
|
if (label_tag_text && hash_text) {
|
||||||
|
ss << reinterpret_cast<const char*>(label_tag_text) << ","
|
||||||
|
<< reinterpret_cast<const char*>(hash_text) << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc != SQLITE_DONE) {
|
||||||
|
std::cerr << "Failed to execute/iterate statement (get dir): " << sqlite3_errmsg(db_) << std::endl;
|
||||||
|
// Don't overwrite potential results, but log error
|
||||||
|
if (ss.str().empty()) { // Only send error if no data was retrieved
|
||||||
|
res.status = 500;
|
||||||
|
res.set_content("Database error executing statement", "text/plain");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
res.set_content(ss.str(), "text/plain");
|
||||||
|
}
|
||||||
|
|
||||||
void Server::handle_put_object(const httplib::Request& req, httplib::Response& res) {
|
void Server::handle_put_object(const httplib::Request& req, httplib::Response& res) {
|
||||||
const auto& token = req.matches[1].str();
|
const auto& token = req.matches[1].str();
|
||||||
@@ -226,7 +342,7 @@ void Server::handle_put_object(const httplib::Request& req, httplib::Response& r
|
|||||||
if (hash == 0) {
|
if (hash == 0) {
|
||||||
res.status = 500;
|
res.status = 500;
|
||||||
res.set_content("Failed to calculate hash", "text/plain");
|
res.set_content("Failed to calculate hash", "text/plain");
|
||||||
return; // Deleter will remove temp_file
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move file to final location
|
// Move file to final location
|
||||||
@@ -235,31 +351,40 @@ void Server::handle_put_object(const httplib::Request& req, httplib::Response& r
|
|||||||
if (!std::filesystem::exists(final_path)) {
|
if (!std::filesystem::exists(final_path)) {
|
||||||
try {
|
try {
|
||||||
std::filesystem::rename(temp_path, final_path);
|
std::filesystem::rename(temp_path, final_path);
|
||||||
temp_file_deleter.release(); // Don't delete if rename succeeded
|
temp_file_deleter.release();
|
||||||
} catch (const std::filesystem::filesystem_error& e) {
|
} catch (const std::filesystem::filesystem_error& e) {
|
||||||
std::cerr << "Error renaming temp file: " << e.what() << std::endl;
|
std::cerr << "Error renaming temp file: " << e.what() << std::endl;
|
||||||
res.status = 500;
|
res.status = 500;
|
||||||
res.set_content("Failed to store object file", "text/plain");
|
res.set_content("Failed to store object file", "text/plain");
|
||||||
return; // Deleter will remove temp_file
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// File with the same hash already exists, no need to move
|
|
||||||
// temp_file_deleter will automatically remove the temp file
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update datastore index
|
// Update SQLite index (INSERT OR REPLACE)
|
||||||
auto rc = datastore_.put(label_tag.data(), label_tag.size(), hash_str.data(), hash_str.size()); // Use .data() and .size()
|
sqlite3_stmt* stmt = nullptr;
|
||||||
if (rc != litecask::Status::Ok) {
|
const char* sql = "INSERT OR REPLACE INTO objects (label_tag, hash) VALUES (?, ?);";
|
||||||
std::cerr << "Datastore put error: " << static_cast<int>(rc) << std::endl;
|
int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
std::cerr << "Failed to prepare statement (put object): " << sqlite3_errmsg(db_) << std::endl;
|
||||||
res.status = 500;
|
res.status = 500;
|
||||||
res.set_content("Failed to update datastore index", "text/plain");
|
res.set_content("Database error preparing statement", "text/plain");
|
||||||
try {
|
// Attempt to clean up the moved file if index fails
|
||||||
if (std::filesystem::exists(final_path) && !std::filesystem::remove(final_path)) {
|
try { if (std::filesystem::exists(final_path)) std::filesystem::remove(final_path); } catch(...) {};
|
||||||
std::cerr << "Failed to remove object file after index failure: " << final_path << std::endl;
|
return;
|
||||||
}
|
}
|
||||||
} catch (const std::filesystem::filesystem_error& e) {
|
|
||||||
std::cerr << "Error removing object file after index failure: " << e.what() << std::endl;
|
sqlite3_bind_text(stmt, 1, label_tag.c_str(), -1, SQLITE_STATIC);
|
||||||
}
|
sqlite3_bind_text(stmt, 2, hash_str.c_str(), -1, SQLITE_STATIC);
|
||||||
|
|
||||||
|
rc = sqlite3_step(stmt);
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
if (rc != SQLITE_DONE) {
|
||||||
|
std::cerr << "Failed to execute statement (put object): " << sqlite3_errmsg(db_) << std::endl;
|
||||||
|
res.status = 500;
|
||||||
|
res.set_content("Failed to update database index", "text/plain");
|
||||||
|
// Attempt to clean up the moved file if index fails
|
||||||
|
try { if (std::filesystem::exists(final_path)) std::filesystem::remove(final_path); } catch(...) {};
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,12 +3,13 @@
|
|||||||
|
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
#include "httplib.hpp"
|
#include "httplib.hpp"
|
||||||
#include "litecask.hpp"
|
// #include "litecask.hpp" // Removed litecask
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <sqlite3.h> // Include SQLite header
|
||||||
|
|
||||||
namespace dropshell {
|
namespace dropshell {
|
||||||
|
|
||||||
@@ -24,17 +25,20 @@ private:
|
|||||||
void setup_routes();
|
void setup_routes();
|
||||||
void handle_get_object(const httplib::Request& req, httplib::Response& res);
|
void handle_get_object(const httplib::Request& req, httplib::Response& res);
|
||||||
void handle_get_hash(const httplib::Request& req, httplib::Response& res);
|
void handle_get_hash(const httplib::Request& req, httplib::Response& res);
|
||||||
void handle_get_directory(const httplib::Request& req, httplib::Response& res);
|
void handle_get_directory(const httplib::Request& req, httplib::Response& res); // Re-add directory handler
|
||||||
void handle_put_object(const httplib::Request& req, httplib::Response& res);
|
void handle_put_object(const httplib::Request& req, httplib::Response& res);
|
||||||
bool validate_write_token(const std::string& token) const;
|
bool validate_write_token(const std::string& token) const;
|
||||||
std::pair<std::string, std::string> parse_label_tag(const std::string& label_tag) const;
|
std::pair<std::string, std::string> parse_label_tag(const std::string& label_tag) const;
|
||||||
|
|
||||||
|
bool init_db(); // Helper for DB initialization
|
||||||
|
|
||||||
const ServerConfig& config_;
|
const ServerConfig& config_;
|
||||||
httplib::Server server_;
|
httplib::Server server_;
|
||||||
litecask::Datastore datastore_;
|
// Removed litecask members
|
||||||
std::filesystem::path datastore_path_;
|
sqlite3* db_ = nullptr; // SQLite database connection
|
||||||
|
std::filesystem::path db_path_;
|
||||||
std::atomic<bool> running_;
|
std::atomic<bool> running_;
|
||||||
bool _isInitialized;
|
// Removed _isInitialized - will rely on db_ pointer
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dropshell
|
} // namespace dropshell
|
||||||
|
Reference in New Issue
Block a user