Basic store and retrieve data

This commit is contained in:
Sosthene 2024-11-18 17:15:46 +01:00
commit 2d530ec000
4 changed files with 2904 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
/storage

2772
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

13
Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
[package]
name = "sdk_storage"
version = "0.1.0"
edition = "2021"
[dependencies]
sdk_common = { git = "https://git.4nkweb.com/4nk/sdk_common.git", branch = "dev"}
tide = "0.16.0"
async-std = { version = "1.8.0", features = ["attributes"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
base64 = "0.13"

117
src/main.rs Normal file
View File

@ -0,0 +1,117 @@
use async_std::fs::{create_dir_all, File};
use async_std::io::WriteExt;
use serde::{Deserialize, Serialize};
use tide::{Request, Response, StatusCode};
use base64;
const STORAGE_DIR: &str = "./storage";
#[derive(Deserialize)]
struct StoreRequest {
key: String,
value: String,
}
#[derive(Serialize)]
struct ApiResponse {
message: String,
}
#[derive(Serialize)]
struct RetrieveResponse {
key: String,
value: String,
}
/// Store data on the filesystem
async fn store_data(key: &str, value: &[u8]) -> Result<(), String> {
// Create directory based on the first 2 characters of the key
let dir_name = format!("{}/{}", STORAGE_DIR, &key[..2]);
let file_path = format!("{}/{}", dir_name, &key[2..]);
// Create directory if it doesn't exist
create_dir_all(&dir_name).await.map_err(|e| e.to_string())?;
// Write value to file
let mut file = File::create(&file_path).await.map_err(|e| e.to_string())?;
file.write_all(value).await.map_err(|e| e.to_string())?;
Ok(())
}
async fn retrieve_data(key: &str) -> Result<Vec<u8>, String> {
let file_path = format!("{}/{}/{}", STORAGE_DIR, &key[..2], &key[2..]);
let mut file = File::open(&file_path).await.map_err(|_| "Key not found.".to_string())?;
let mut buffer = Vec::new();
async_std::io::ReadExt::read_to_end(&mut file, &mut buffer).await.map_err(|e| e.to_string())?;
Ok(buffer)
}
/// Handler for the /store endpoint
async fn handle_store(mut req: Request<()>) -> tide::Result<Response> {
// Parse the JSON body
let data: StoreRequest = req.body_json().await.map_err(|_| {
tide::Error::from_str(StatusCode::BadRequest, "Invalid JSON payload")
})?;
// Validate the key
if data.key.len() != 64 || !data.key.chars().all(|c| c.is_ascii_hexdigit()) {
return Ok(Response::builder(StatusCode::BadRequest)
.body("Invalid key: must be a 32 bytes hex string.".to_string())
.build());
}
// Decode the value from Base64
let value_bytes = base64::decode(&data.value).map_err(|_| {
tide::Error::from_str(StatusCode::BadRequest, "Invalid Base64 value")
})?;
// Store the data
store_data(&data.key, &value_bytes).await.map_err(|e| {
tide::Error::from_str(StatusCode::InternalServerError, e)
})?;
// Respond with success
Ok(Response::builder(StatusCode::Ok)
.body(serde_json::to_value(&ApiResponse {
message: "Data stored successfully.".to_string(),
})?)
.build())
}
async fn handle_retrieve(req: Request<()>) -> tide::Result<Response> {
let key: String = req.param("key")?.to_string();
if key.len() != 64 || !key.chars().all(|c| c.is_ascii_hexdigit()) {
return Ok(Response::builder(StatusCode::BadRequest)
.body("Invalid key: must be a 32 bytes hex string.".to_string())
.build());
}
match retrieve_data(&key).await {
Ok(value) => {
let encoded_value = base64::encode(&value);
Ok(Response::builder(StatusCode::Ok)
.body(serde_json::to_value(&RetrieveResponse { key, value: encoded_value })?)
.build())
}
Err(e) => Ok(Response::builder(StatusCode::NotFound)
.body(e)
.build()),
}
}
#[async_std::main]
async fn main() -> tide::Result<()> {
create_dir_all(STORAGE_DIR).await.expect("Failed to create storage directory.");
let mut app = tide::new();
app.at("/store").post(handle_store);
app.at("/retrieve/:key").get(handle_retrieve);
app.listen("0.0.0.0:8080").await?;
println!("Server running at http://0.0.0.0:8080");
Ok(())
}