Basic store and retrieve data
This commit is contained in:
commit
2d530ec000
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
/storage
|
2772
Cargo.lock
generated
Normal file
2772
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
13
Cargo.toml
Normal file
13
Cargo.toml
Normal 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
117
src/main.rs
Normal 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(())
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user