bug fix + format
This commit is contained in:
parent
ccb7c1353e
commit
f4a3f38096
116
src/main.rs
116
src/main.rs
@ -1,15 +1,15 @@
|
|||||||
use async_std::fs::{create_dir_all, read_dir, read_to_string, remove_file, File};
|
use async_std::fs::{create_dir_all, read_dir, read_to_string, remove_file, File};
|
||||||
use std::time::{SystemTime, UNIX_EPOCH, Duration};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde::{Serialize, Deserialize};
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
use async_std::io::WriteExt;
|
use async_std::io::WriteExt;
|
||||||
use async_std::path::Path;
|
use async_std::path::Path;
|
||||||
use async_std::stream::{IntoStream, StreamExt};
|
use async_std::stream::{IntoStream, StreamExt};
|
||||||
use async_std::task;
|
use async_std::task;
|
||||||
|
use base64;
|
||||||
use sdk_common::log;
|
use sdk_common::log;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use tide::{Request, Response, StatusCode};
|
use tide::{Request, Response, StatusCode};
|
||||||
use base64;
|
|
||||||
|
|
||||||
const STORAGE_DIR: &str = "./storage";
|
const STORAGE_DIR: &str = "./storage";
|
||||||
const MIN_TTL: u64 = 60; // 1 minute
|
const MIN_TTL: u64 = 60; // 1 minute
|
||||||
@ -68,7 +68,8 @@ struct Metadata {
|
|||||||
|
|
||||||
/// Converts a `SystemTime` to a UNIX timestamp (seconds since UNIX epoch).
|
/// Converts a `SystemTime` to a UNIX timestamp (seconds since UNIX epoch).
|
||||||
fn system_time_to_unix(system_time: SystemTime) -> u64 {
|
fn system_time_to_unix(system_time: SystemTime) -> u64 {
|
||||||
system_time.duration_since(UNIX_EPOCH)
|
system_time
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
.expect("SystemTime before UNIX_EPOCH!")
|
.expect("SystemTime before UNIX_EPOCH!")
|
||||||
.as_secs()
|
.as_secs()
|
||||||
}
|
}
|
||||||
@ -99,7 +100,7 @@ struct RetrieveResponse {
|
|||||||
async fn get_file_path(key: &str) -> String {
|
async fn get_file_path(key: &str) -> String {
|
||||||
let dir_name = format!("{}/{}", STORAGE_DIR, &key[..2]);
|
let dir_name = format!("{}/{}", STORAGE_DIR, &key[..2]);
|
||||||
let file_path = format!("{}/{}", dir_name, &key[2..]);
|
let file_path = format!("{}/{}", dir_name, &key[2..]);
|
||||||
|
|
||||||
file_path
|
file_path
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,23 +111,41 @@ async fn store_data(key: &str, value: &[u8], expires_at: SystemTime) -> Result<(
|
|||||||
|
|
||||||
// Check if key exists
|
// Check if key exists
|
||||||
if file_path.exists().await {
|
if file_path.exists().await {
|
||||||
return Err(tide::Error::from_str(StatusCode::Conflict, "Key already exists"));
|
return Err(tide::Error::from_str(
|
||||||
|
StatusCode::Conflict,
|
||||||
|
"Key already exists",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
create_dir_all(file_path).await.map_err(|e| tide::Error::new(StatusCode::InternalServerError, e))?;
|
create_dir_all(file_path.parent().ok_or(tide::Error::from_str(
|
||||||
|
StatusCode::InternalServerError,
|
||||||
|
"File path doesn't have parent",
|
||||||
|
))?)
|
||||||
|
.await
|
||||||
|
.map_err(|e| tide::Error::new(StatusCode::InternalServerError, e))?;
|
||||||
|
|
||||||
let metadata_path = format!("{}.meta", file_name);
|
let metadata_path = format!("{}.meta", file_name);
|
||||||
|
|
||||||
let mut file = File::create(&file_path).await.map_err(|e| tide::Error::new(StatusCode::InternalServerError, e))?;
|
let mut file = File::create(&file_path)
|
||||||
file.write_all(value).await.map_err(|e| tide::Error::new(StatusCode::InternalServerError, e))?;
|
.await
|
||||||
|
.map_err(|e| tide::Error::new(StatusCode::InternalServerError, e))?;
|
||||||
|
file.write_all(value)
|
||||||
|
.await
|
||||||
|
.map_err(|e| tide::Error::new(StatusCode::InternalServerError, e))?;
|
||||||
|
|
||||||
let metadata = Metadata {
|
let metadata = Metadata {
|
||||||
expires_at: system_time_to_unix(expires_at)
|
expires_at: system_time_to_unix(expires_at),
|
||||||
};
|
};
|
||||||
|
|
||||||
let metadata_json = serde_json::to_string(&metadata).map_err(|e| tide::Error::new(StatusCode::InternalServerError, e))?;
|
let metadata_json = serde_json::to_string(&metadata)
|
||||||
let mut meta_file = File::create(&metadata_path).await.map_err(|e| tide::Error::new(StatusCode::InternalServerError, e))?;
|
.map_err(|e| tide::Error::new(StatusCode::InternalServerError, e))?;
|
||||||
meta_file.write_all(metadata_json.as_bytes()).await.map_err(|e| tide::Error::new(StatusCode::InternalServerError, e))?;
|
let mut meta_file = File::create(&metadata_path)
|
||||||
|
.await
|
||||||
|
.map_err(|e| tide::Error::new(StatusCode::InternalServerError, e))?;
|
||||||
|
meta_file
|
||||||
|
.write_all(metadata_json.as_bytes())
|
||||||
|
.await
|
||||||
|
.map_err(|e| tide::Error::new(StatusCode::InternalServerError, e))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -134,9 +153,13 @@ async fn store_data(key: &str, value: &[u8], expires_at: SystemTime) -> Result<(
|
|||||||
async fn retrieve_data(key: &str) -> Result<Vec<u8>, String> {
|
async fn retrieve_data(key: &str) -> Result<Vec<u8>, String> {
|
||||||
let file_path = format!("{}/{}/{}", STORAGE_DIR, &key[..2], &key[2..]);
|
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 file = File::open(&file_path)
|
||||||
|
.await
|
||||||
|
.map_err(|_| "Key not found.".to_string())?;
|
||||||
let mut buffer = Vec::new();
|
let mut buffer = Vec::new();
|
||||||
async_std::io::ReadExt::read_to_end(&mut file, &mut buffer).await.map_err(|e| e.to_string())?;
|
async_std::io::ReadExt::read_to_end(&mut file, &mut buffer)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
Ok(buffer)
|
Ok(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,7 +186,10 @@ async fn handle_store(mut req: Request<()>) -> tide::Result<Response> {
|
|||||||
let live_for = if let Some(ttl) = data.ttl {
|
let live_for = if let Some(ttl) = data.ttl {
|
||||||
if ttl < MIN_TTL {
|
if ttl < MIN_TTL {
|
||||||
return Ok(Response::builder(StatusCode::BadRequest)
|
return Ok(Response::builder(StatusCode::BadRequest)
|
||||||
.body(format!("Invalid ttl: must be at least {} seconds.", MIN_TTL))
|
.body(format!(
|
||||||
|
"Invalid ttl: must be at least {} seconds.",
|
||||||
|
MIN_TTL
|
||||||
|
))
|
||||||
.build());
|
.build());
|
||||||
} else if ttl > MAX_TTL {
|
} else if ttl > MAX_TTL {
|
||||||
return Ok(Response::builder(StatusCode::BadRequest)
|
return Ok(Response::builder(StatusCode::BadRequest)
|
||||||
@ -176,7 +202,9 @@ async fn handle_store(mut req: Request<()>) -> tide::Result<Response> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let now = SystemTime::now();
|
let now = SystemTime::now();
|
||||||
let expires_at = now.checked_add(live_for).ok_or(tide::Error::from_str(StatusCode::BadRequest, "Invalid ttl"))?;
|
let expires_at = now
|
||||||
|
.checked_add(live_for)
|
||||||
|
.ok_or(tide::Error::from_str(StatusCode::BadRequest, "Invalid ttl"))?;
|
||||||
|
|
||||||
// Decode the value from Base64
|
// Decode the value from Base64
|
||||||
let value_bytes = match base64::decode(&data.value) {
|
let value_bytes = match base64::decode(&data.value) {
|
||||||
@ -190,20 +218,16 @@ async fn handle_store(mut req: Request<()>) -> tide::Result<Response> {
|
|||||||
|
|
||||||
// Store the data
|
// Store the data
|
||||||
match store_data(&data.key, &value_bytes, expires_at).await {
|
match store_data(&data.key, &value_bytes, expires_at).await {
|
||||||
Ok(()) => {
|
Ok(()) => Ok(Response::builder(StatusCode::Ok)
|
||||||
Ok(Response::builder(StatusCode::Ok)
|
.body(serde_json::to_value(&ApiResponse {
|
||||||
.body(serde_json::to_value(&ApiResponse {
|
message: "Data stored successfully.".to_string(),
|
||||||
message: "Data stored successfully.".to_string(),
|
})?)
|
||||||
})?)
|
.build()),
|
||||||
.build())
|
Err(e) => Ok(Response::builder(e.status())
|
||||||
}
|
.body(serde_json::to_value(&ApiResponse {
|
||||||
Err(e) => {
|
message: e.to_string(),
|
||||||
Ok(Response::builder(e.status())
|
})?)
|
||||||
.body(serde_json::to_value(&ApiResponse {
|
.build()),
|
||||||
message: e.to_string(),
|
|
||||||
})?)
|
|
||||||
.build())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,24 +244,32 @@ async fn handle_retrieve(req: Request<()>) -> tide::Result<Response> {
|
|||||||
Ok(value) => {
|
Ok(value) => {
|
||||||
let encoded_value = base64::encode(&value);
|
let encoded_value = base64::encode(&value);
|
||||||
Ok(Response::builder(StatusCode::Ok)
|
Ok(Response::builder(StatusCode::Ok)
|
||||||
.body(serde_json::to_value(&RetrieveResponse { key, value: encoded_value })?)
|
.body(serde_json::to_value(&RetrieveResponse {
|
||||||
|
key,
|
||||||
|
value: encoded_value,
|
||||||
|
})?)
|
||||||
.build())
|
.build())
|
||||||
}
|
}
|
||||||
Err(e) => Ok(Response::builder(StatusCode::NotFound)
|
Err(e) => Ok(Response::builder(StatusCode::NotFound).body(e).build()),
|
||||||
.body(e)
|
|
||||||
.build()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks a metadata file and deletes the associated data file if expired
|
/// Checks a metadata file and deletes the associated data file if expired
|
||||||
async fn handle_file_cleanup(now: u64, meta_path: &Path) -> Result<(), String> {
|
async fn handle_file_cleanup(now: u64, meta_path: &Path) -> Result<(), String> {
|
||||||
let meta_content = read_to_string(meta_path).await.map_err(|e| format!("Failed to read metadata: {}", e.to_string()))?;
|
let meta_content = read_to_string(meta_path)
|
||||||
let metadata: Metadata = serde_json::from_str(&meta_content).map_err(|e| format!("Failed to parse metadata: {}", e.to_string()))?;
|
.await
|
||||||
|
.map_err(|e| format!("Failed to read metadata: {}", e.to_string()))?;
|
||||||
|
let metadata: Metadata = serde_json::from_str(&meta_content)
|
||||||
|
.map_err(|e| format!("Failed to parse metadata: {}", e.to_string()))?;
|
||||||
|
|
||||||
if metadata.expires_at < now {
|
if metadata.expires_at < now {
|
||||||
let data_file_path = meta_path.with_extension("");
|
let data_file_path = meta_path.with_extension("");
|
||||||
remove_file(&data_file_path).await.map_err(|e| format!("Failed to remove data file: {}", e.to_string()))?;
|
remove_file(&data_file_path)
|
||||||
remove_file(meta_path).await.map_err(|e| format!("Failed to remove metadata file: {}", e.to_string()))?;
|
.await
|
||||||
|
.map_err(|e| format!("Failed to remove data file: {}", e.to_string()))?;
|
||||||
|
remove_file(meta_path)
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("Failed to remove metadata file: {}", e.to_string()))?;
|
||||||
log::debug!("Removed expired file: {:?}", data_file_path);
|
log::debug!("Removed expired file: {:?}", data_file_path);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -246,7 +278,9 @@ async fn handle_file_cleanup(now: u64, meta_path: &Path) -> Result<(), String> {
|
|||||||
#[async_std::main]
|
#[async_std::main]
|
||||||
async fn main() -> tide::Result<()> {
|
async fn main() -> tide::Result<()> {
|
||||||
sdk_common::env_logger::init();
|
sdk_common::env_logger::init();
|
||||||
create_dir_all(STORAGE_DIR).await.expect("Failed to create storage directory.");
|
create_dir_all(STORAGE_DIR)
|
||||||
|
.await
|
||||||
|
.expect("Failed to create storage directory.");
|
||||||
|
|
||||||
task::spawn(cleanup_expired_files());
|
task::spawn(cleanup_expired_files());
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user