safesight/plugins/alarm/uploaders/minio_uploader.cpp

140 lines
4.6 KiB
C++

#include "minio_uploader.h"
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <sstream>
#if __has_include(<curl/curl.h>)
#include <curl/curl.h>
#define HAS_CURL 1
#else
#define HAS_CURL 0
#endif
namespace rk3588 {
namespace {
std::string GetEnvOrDefault(const std::string& env_name, const std::string& default_val) {
(void)env_name;
// Handle ${ENV_VAR} syntax
if (default_val.size() > 3 && default_val.substr(0, 2) == "${" &&
default_val.back() == '}') {
std::string var_name = default_val.substr(2, default_val.size() - 3);
const char* val = std::getenv(var_name.c_str());
return val ? std::string(val) : "";
}
return default_val;
}
} // namespace
bool MinioUploader::Init(const SimpleJson& config) {
endpoint_ = config.ValueOr<std::string>("endpoint", "http://localhost:9000");
bucket_ = config.ValueOr<std::string>("bucket", "alarms");
region_ = config.ValueOr<std::string>("region", "us-east-1");
std::string ak = config.ValueOr<std::string>("access_key", "");
std::string sk = config.ValueOr<std::string>("secret_key", "");
access_key_ = GetEnvOrDefault("MINIO_ACCESS_KEY", ak);
secret_key_ = GetEnvOrDefault("MINIO_SECRET_KEY", sk);
use_ssl_ = endpoint_.find("https://") == 0;
if (access_key_.empty() || secret_key_.empty()) {
std::cerr << "[MinioUploader] warning: access_key or secret_key not set\n";
}
std::cout << "[MinioUploader] initialized, endpoint=" << endpoint_
<< " bucket=" << bucket_ << "\n";
return true;
}
UploadResult MinioUploader::Upload(const std::string& key,
const uint8_t* data,
size_t size,
const std::string& content_type) {
UploadResult result;
#if HAS_CURL
// Simple S3 PUT request (works with MinIO)
// For production, use proper AWS4 signing
std::string url = endpoint_;
if (url.back() != '/') url += '/';
url += bucket_ + "/" + key;
CURL* curl = curl_easy_init();
if (!curl) {
result.error = "curl_easy_init failed";
return result;
}
struct curl_slist* headers = nullptr;
std::string ct = content_type.empty() ? "application/octet-stream" : content_type;
headers = curl_slist_append(headers, ("Content-Type: " + ct).c_str());
// Simple auth (MinIO supports this for internal use)
// For proper S3 compatibility, implement AWS4 signing
if (!access_key_.empty()) {
// This is a simplified approach - real implementation should use AWS4 signing
std::string auth = "Authorization: AWS " + access_key_ + ":" + secret_key_;
headers = curl_slist_append(headers, auth.c_str());
}
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, static_cast<curl_off_t>(size));
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
// Set up data to send
struct ReadData {
const uint8_t* data;
size_t remaining;
} read_data{data, size};
curl_easy_setopt(curl, CURLOPT_READFUNCTION,
+[](char* buffer, size_t s, size_t n, void* userp) -> size_t {
auto* rd = static_cast<ReadData*>(userp);
size_t to_copy = std::min(s * n, rd->remaining);
if (to_copy > 0) {
std::memcpy(buffer, rd->data, to_copy);
rd->data += to_copy;
rd->remaining -= to_copy;
}
return to_copy;
});
curl_easy_setopt(curl, CURLOPT_READDATA, &read_data);
CURLcode res = curl_easy_perform(curl);
if (res != CURLE_OK) {
result.error = curl_easy_strerror(res);
} else {
long http_code = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
if (http_code >= 200 && http_code < 300) {
result.success = true;
result.url = url;
} else {
result.error = "HTTP " + std::to_string(http_code);
}
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
#else
// Stub for Windows
std::cout << "[MinioUploader] would upload " << size << " bytes to "
<< endpoint_ << "/" << bucket_ << "/" << key << "\n";
result.success = true;
result.url = endpoint_ + "/" + bucket_ + "/" + key;
#endif
return result;
}
} // namespace rk3588