Skip to content

Commit

Permalink
Merge pull request #2
Browse files Browse the repository at this point in the history
update 0.1.2
  • Loading branch information
JKearnsl authored Jul 31, 2024
2 parents 0e91bc2 + 0a46620 commit 2da1253
Show file tree
Hide file tree
Showing 37 changed files with 1,160 additions and 783 deletions.
10 changes: 6 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "tobox"
version = "0.1.1"
version = "0.1.2"
authors = ["JKearnsl <[email protected]>"]
edition = "2021"

Expand All @@ -10,12 +10,14 @@ path = "src/tobox/main.rs"


[dependencies]
tokio = { version = "^1.38", features = [
tokio = { version = "^1.39", features = [
"rt",
"rt-multi-thread",
"macros"
] }
tokio-stream = "^0.1"
actix-web = { version = "^4.8", features = ["rustls-0_23"] }
actix-multipart = "^0.7"
rustls = "^0.23"
rustls-pemfile = "^2.1"
log = "^0.4"
Expand All @@ -37,10 +39,10 @@ strum_macros = "^0.26"
sha2 = "^0.10"

# Database
sqlx = { version = "^0.7", features = [
sqlx = { version = "^0.8", features = [
"runtime-tokio",
"sqlite",
"sqlx-sqlite",
"chrono"
] }
regex = "1.10.5"
regex = "^1.10"
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,21 @@ This is a young repository. Work plan:
- [ ] Node protection against interruption of some servers
- [ ] Node checking files based on the first byte pattern
- [ ] Panel


## Synchronizing objects

In order to avoid conflicts and reduce the overhead costs associated with ensuring the uniqueness of file names
in a decentralized virtual directory, it has been decided to abandon this requirement
and instead use the file's name as a unique identifier.

The new approach involves generating a unique object key in UUIDv4 format and using it to access files in the future.
This method has several advantages beyond avoiding synchronization issues:

* In some cases, such as storing large volumes of user photos or public documents, it eliminates the need to ensure file name uniqueness. This can save time and resources that would otherwise be spent on unnecessary checks.
* Clients often assign unique identifiers to files themselves (e.g., UUID) and maintain a separate table in a relational database that links files to user IDs. With the new approach, all files start with a unique UUIDv4 and optional filename/pathname, which users can choose to use or not.

Tobox approach also has a significant drawback when using cloud file storage as a local one.
Users can get confused if they see two files with the same name in the same directory.
This problem can be partially solved by checking if the filename exists in the specified pathname.
However, **tobox does not guarantee** the uniqueness of the filename and path name pair, **but rather allows it**.
86 changes: 0 additions & 86 deletions src/tobox/adapters/database/access_log_db.rs

This file was deleted.

175 changes: 175 additions & 0 deletions src/tobox/adapters/database/file_storage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
use std::collections::VecDeque;
use std::future::Future;
use std::path::Path;
use std::pin::Pin;
use std::sync::Mutex;
use std::task::{Context, Poll};

use tokio::fs::File;
use tokio::io::{self, AsyncReadExt, AsyncWriteExt};
use tokio_stream::{Stream, StreamExt};

use crate::application::common::file_storage_manager::{
FileStorageManager,
FileStorageReader,
FileStorageRemover,
FileStorageWriter
};
use crate::domain::models::file_stream::FileStream;

struct ByteStream {
file: File,
buffer: [u8; 1],
}

impl ByteStream {
fn new(file: File) -> Self {
Self {
file,
buffer: [0],
}
}
}

impl Stream for ByteStream {
type Item = io::Result<u8>;

fn poll_next(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>
) -> Poll<Option<Self::Item>> {
let buf = &mut self.buffer;
let fut = self.file.read(buf);

tokio::pin!(fut);

match fut.poll(cx) {
Poll::Ready(Ok(0)) => Poll::Ready(None), // EOF
Poll::Ready(Ok(_)) => Poll::Ready(Some(Ok(buf[0]))),
Poll::Ready(Err(e)) => Poll::Ready(Some(Err(e))),
Poll::Pending => Poll::Pending,
}
}
}

impl FileStream for ByteStream {}


struct BufferPool {
pool: Mutex<VecDeque<Vec<u8>>>,
buffer_size: usize,
}

impl BufferPool {
fn new(buffer_size: usize, pool_size: usize) -> Self {
let mut pool = VecDeque::with_capacity(pool_size);
for _ in 0..pool_size {
pool.push_back(vec![0; buffer_size]);
}
BufferPool {
pool: Mutex::new(pool),
buffer_size,
}
}

fn get_buffer(&self) -> Vec<u8> {
let mut pool = self.pool.lock().unwrap();
pool.pop_front().unwrap_or_else(|| vec![0; self.buffer_size])
}

fn return_buffer(&self, buffer: Vec<u8>) {
let mut pool = self.pool.lock().unwrap();
if pool.len() < pool.capacity() {
pool.push_back(buffer);
}
}
}


pub struct FileStorage {
path: Box<Path>
}

impl FileStorage {
pub fn new(path: &Path) -> Self {
Self {
path: path.as_ref().into(),
}
}
}

impl FileStorageReader for FileStorage {
async fn read_file<T: Into<String>>(&self, filename: &T) -> Box<dyn FileStream> {
let file = File::open(self.path.join(filename.into())).await.unwrap();
Box::new(ByteStream::new(file))
}
}

impl FileStorageWriter for FileStorage {
async fn save_file<T: Into<String>>(
&self,
filename: &T,
content_type: Option<&T>,
size_range: Option<(u64, u64)>,
bytes: &dyn FileStream
) -> Result<String, String> {
let mut file = File::create(self.path.join(filename.into())).await.unwrap();
let mut stream = bytes;
let mut buf = Vec::new(); // Todo: add buffer pool
while let Some(byte) = stream.next().await {
// todo: check size range
// todo: check content type
buf.push(byte.unwrap());
}
file.write_all(&buf).await.unwrap();
Ok("982d9e3eb996f559e633f4d194def3761d909f5a3b647d1a851fead67c32c9d1".to_string()) // Todo: calculate hash
}
}

impl FileStorageRemover for FileStorage {
async fn remove_file<T: Into<String>>(&self, filename: &T) {
tokio::fs::remove_file(self.path.join(filename.into())).await.unwrap();
}
}

impl FileStorageManager for FileStorage {}


#[cfg(test)]
mod tests {
use tokio::fs::OpenOptions;
use tokio::io::AsyncWriteExt;
use tokio_stream::StreamExt;

use super::*;

#[tokio::test]
async fn test_byte_stream() {
let file_path = "test_byte_stream.txt";
let mut file = OpenOptions::new()
.write(true)
.create(true)
.open(file_path)
.await
.unwrap();

file.write_all(b"hello world").await.unwrap();

let file = OpenOptions::new()
.read(true)
.open(file_path)
.await
.unwrap();

let mut stream = ByteStream::new(file);

let mut buf = Vec::new();
while let Some(byte) = stream.next().await {
buf.push(byte.unwrap());
}

assert_eq!(buf, b"hello world");

tokio::fs::remove_file(file_path).await.unwrap();
}
}
1 change: 1 addition & 0 deletions src/tobox/adapters/database/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ pub mod service_db;
pub mod permission_db;
pub mod init_state_db;
pub mod access_log_db;
pub mod file_storage;
Loading

0 comments on commit 2da1253

Please sign in to comment.