Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wp/delete file blake3 hash http route #24

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 59 additions & 5 deletions src/backend/fs.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#![allow(unused_variables)]

use std::{
fs::OpenOptions,
io::{Read, Write},
path::PathBuf,
fs::{self, OpenOptions},
io::{self, Read, Write},
//path::Path,
path::{Path, PathBuf},
};

use anyhow::{anyhow, Result};
Expand Down Expand Up @@ -32,6 +33,21 @@ pub async fn write_file(pk: &Secp256k1PubKey, file_bytes: &[u8]) -> Result<Blake
let file_hash = Blake3Hash(blake3::keyed_hash(&x_only_pk.serialize(), file_bytes));

trace!("TODO: Check if file catalog already exists");
let path = SYS_CFG
.volumes
.get(0)
.expect("First volume present")
.path
.join(CATALOG_DIR)
.join(file_hash.to_string());

trace!("Read catalog at {}", &path.to_string_lossy());

let is_catalog = std::path::Path::new(&path).file_name();
trace!("Catalog File: file_name: {:?}", is_catalog);
if is_catalog.is_some() {
return Err(anyhow!("This file already exists for this user."));
}

trace!("Segment files");
let segments_iter = file_bytes.par_chunks_exact(SEGMENT_SIZE);
Expand Down Expand Up @@ -190,7 +206,7 @@ pub async fn read_file(pk: &Secp256k1PubKey, blake3_hash: &Blake3Hash) -> Result
.expect("Get first volume")
.path
.join(SEGMENT_DIR)
.join(format!("{}.c{}", segment_hash, NODE_FORMAT));
.join(format!("{segment_hash}.c{NODE_FORMAT}"));

let chunk_file = OpenOptions::new().read(true).open(chunk_path).unwrap();
let header = Header::try_from(chunk_file).unwrap();
Expand All @@ -202,7 +218,7 @@ pub async fn read_file(pk: &Secp256k1PubKey, blake3_hash: &Blake3Hash) -> Result
let path = volume
.path
.join(SEGMENT_DIR)
.join(format!("{}.c{}", segment_hash, NODE_FORMAT));
.join(format!("{segment_hash}.c{NODE_FORMAT}"));

let mut file = OpenOptions::new().read(true).open(path).unwrap();

Expand Down Expand Up @@ -252,3 +268,41 @@ pub fn read_catalog(file_hash: &Blake3Hash) -> Result<Vec<BaoHash>> {

Ok(bao_hashes)
}

pub fn delete_file(pk: Secp256k1PubKey, file_bytes: &[u8]) -> Result<()> {
let pk_bytes = pk.to_bytes();
let (x_only_pk, _) = pk.into_inner().x_only_public_key();

let file_hash = Blake3Hash(blake3::keyed_hash(&x_only_pk.serialize(), file_bytes));
trace!(">>>>>file_hash:: {}", file_hash);

for vol in &SYS_CFG.volumes {
let seg_path = &vol.path.join(SEGMENT_DIR).join(file_hash.to_string());
let seg = &vol.path.join(SEGMENT_DIR);
remove_dir_contents(seg).unwrap();
}

for vol in &SYS_CFG.volumes {
let cat_path = &vol.path.join(CATALOG_DIR).join(file_hash.to_string());
let cat = &vol.path.join(CATALOG_DIR);
remove_dir_catalogs(cat.to_path_buf(), cat_path.to_path_buf()).unwrap();
}
Ok(())
}

fn remove_dir_contents<P: AsRef<Path>>(path: P) -> io::Result<()> {
trace!(">>> remove_dir_contents");
for entry in fs::read_dir(path)? {
trace!("Delete Segment File at {:?}", entry);
fs::remove_file(entry?.path())?;
}
Ok(())
}

fn remove_dir_catalogs(path: PathBuf, file: PathBuf) -> io::Result<()> {
for entry in fs::read_dir(path)? {
trace!("Delete CATALOG File at {:?}", entry);
fs::remove_file(entry?.path())?;
}
Ok(())
}
2 changes: 1 addition & 1 deletion src/bin/carbonadod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ async fn main() -> Result<()> {
error!("{}", err);
err.chain()
.skip(1)
.for_each(|cause| eprintln!("Error: {}", cause));
.for_each(|cause| eprintln!("Error: {cause}"));

handle.flush();
process::exit(1);
Expand Down
17 changes: 14 additions & 3 deletions src/frontend/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ use axum::{
extract::Path,
http::StatusCode,
response::{IntoResponse, Response},
routing::{get, post},
routing::{delete, get, post},
Router,
};
use log::info;

use tower_http::cors::CorsLayer;

use crate::{
backend::fs::{read_file, write_file},
backend::fs::{delete_file, read_file, write_file},
prelude::*,
};

Expand All @@ -35,15 +36,25 @@ async fn get_file(
Ok((StatusCode::OK, file_bytes))
}

#[axum_macros::debug_handler]
async fn remove_file(
Path((pk, blake3_hash)): Path<(String, String)>,
) -> Result<impl IntoResponse, AppError> {
let pk = Secp256k1PubKey::try_from(pk.as_str())?;
delete_file(pk, blake3_hash.as_bytes())?;
Ok(())
}

pub async fn start() -> Result<()> {
let app = Router::new()
.route("/remove/:pk/:blake3_hash", delete(remove_file))
.route("/store/:pk", post(post_file))
.route("/retrieve/:pk/:blake3_hash", get(get_file))
// .route("/catalog/:blake3_hash", get(get_catalog))
// .route("/raw/:bao_hash", get(get_raw))
.layer(CorsLayer::permissive());

let addr = SocketAddr::from(([0, 0, 0, 0], 7000));
let addr = SocketAddr::from(([127, 0, 0, 1], 7000));

info!("carbonado-node HTTP frontend successfully running at {addr}");

Expand Down
Binary file added src/temp/cat.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/.DS_Store
Binary file not shown.
66 changes: 57 additions & 9 deletions tests/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::fs;

use anyhow::Result;
use carbonado_node::{
backend::fs::{read_file, write_file},
backend::fs::{delete_file, write_file},
structs::Secp256k1PubKey,
};
use log::{debug, info};
Expand All @@ -25,16 +25,64 @@ async fn write_read() -> Result<()> {
let blake3_hash = write_file(&Secp256k1PubKey(pk), &file_bytes).await?;
debug!("File hash: {blake3_hash}");

info!("Reading file by hash");
let new_file_bytes = read_file(&Secp256k1PubKey(pk), &blake3_hash).await?;
debug!("{} new bytes read", new_file_bytes.len());
// info!("Reading file by hash");
// let new_file_bytes = read_file(&blake3_hash).await?;
// debug!("{} new bytes read", new_file_bytes.len());

assert_eq!(
file_bytes, new_file_bytes,
"Written and read file matches bytes"
);
// assert_eq!(
// file_bytes, new_file_bytes,
// "Written and read file matches bytes"
// );

info!("File write/read test finished successfully!");
// info!("File write/read test finished successfully!");

Ok(())
}

#[tokio::test]
// #[should_panic]
async fn check_catalog_exists() -> Result<()> {
carbonado::utils::init_logging(RUST_LOG);

let (_sk, pk) = generate_keypair(&mut thread_rng());

info!("Reading file bytes");
let file_bytes = fs::read("tests/samples/cat.gif")?;
debug!("{} bytes read", file_bytes.len());

info!("Writing file if not exists");
let blake3_hash = write_file(&Secp256k1PubKey(pk), &file_bytes).await.is_err();
debug!("Skip writing file as File hash exists: {blake3_hash}");
assert!(blake3_hash);

Ok(())
}

#[tokio::test]
async fn write_delete_file() -> Result<()> {
carbonado::utils::init_logging(RUST_LOG);

let (_sk, pk) = generate_keypair(&mut thread_rng());

info!("Write Delete:: Reading file bytes");
let file_bytes = fs::read("tests/samples/cat.gif")?;
debug!("{} Write Delete:: bytes read", file_bytes.len());

// info!("Write Delete:: Writing file if not exists in order to test delete");
let blake3_hash = write_file(&Secp256k1PubKey(pk), &file_bytes).await.is_ok();

if blake3_hash {
info!(
"Write File in order to Test Delete File as blake3_hash:: {} ",
blake3_hash.to_string()
);
}

let new_file_bytes = delete_file(Secp256k1PubKey(pk), &file_bytes).is_err();
debug!("Write Delete:: deleted file:: {:?}", new_file_bytes);

debug!(" >>>> Public Key Generated :: {:?} :: {}", _sk, pk);
info!("Write/Delete test finished successfully!");

Ok(())
}