From 73359f353406ca940a4a0cbc1908f6e5eb5df285 Mon Sep 17 00:00:00 2001 From: orhun Date: Thu, 26 Aug 2021 22:57:46 +0300 Subject: [PATCH] feat(paste): support disappearing (oneshot) files --- src/main.rs | 4 +++- src/paste.rs | 46 +++++++++++++++++++++++++++++++++++++--------- src/server.rs | 50 ++++++++++++++++++++++++++++++++++---------------- 3 files changed, 74 insertions(+), 26 deletions(-) diff --git a/src/main.rs b/src/main.rs index 8af2a9a..08972e6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,7 +15,9 @@ async fn main() -> IoResult<()> { .expect("failed to parse config"); let server_config = config.server.clone(); fs::create_dir_all(&server_config.upload_path)?; - fs::create_dir_all(PasteType::Url.get_path(&server_config.upload_path))?; + for paste_type in &[PasteType::Url, PasteType::Oneshot, PasteType::Trash] { + fs::create_dir_all(paste_type.get_path(&server_config.upload_path))?; + } let mut http_server = HttpServer::new(move || { App::new() .data(config.clone()) diff --git a/src/paste.rs b/src/paste.rs index 401bce8..7838686 100644 --- a/src/paste.rs +++ b/src/paste.rs @@ -8,12 +8,16 @@ use std::str; use url::Url; /// Type of the data to store. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum PasteType { /// Any type of file. File, + /// A file that allowed to be accessed once. + Oneshot, /// A file that only contains an URL. Url, + /// A file that is expired or deleted. + Trash, } impl<'a> TryFrom<&'a ContentDisposition> for PasteType { @@ -21,6 +25,8 @@ impl<'a> TryFrom<&'a ContentDisposition> for PasteType { fn try_from(content_disposition: &'a ContentDisposition) -> Result { if content_disposition.has_form_field("file") { Ok(Self::File) + } else if content_disposition.has_form_field("oneshot") { + Ok(Self::Oneshot) } else if content_disposition.has_form_field("url") { Ok(Self::Url) } else { @@ -34,13 +40,25 @@ impl PasteType { pub fn get_dir(&self) -> String { match self { Self::File => String::new(), + Self::Oneshot => String::from("oneshot"), Self::Url => String::from("url"), + Self::Trash => String::from("trash"), } } /// Returns the given path with [`directory`](Self::get_dir) adjoined. pub fn get_path(&self, path: &Path) -> PathBuf { - path.join(self.get_dir()) + let dir = self.get_dir(); + if dir.is_empty() { + path.to_path_buf() + } else { + path.join(dir) + } + } + + /// Returns `true` if the variant is [`Oneshot`](Self::Oneshot). + pub fn is_oneshot(&self) -> bool { + self == &Self::Oneshot } } @@ -83,7 +101,10 @@ impl Paste { Some(v) => v.to_string(), None => String::from("file"), }; - let mut path = config.server.upload_path.join(file_name); + let mut path = self + .type_ + .get_path(&config.server.upload_path) + .join(file_name); match path.clone().extension() { Some(extension) => { if let Some(file_name) = config.paste.random_url.generate() { @@ -189,17 +210,22 @@ mod tests { ); fs::remove_file(file_name)?; + for paste_type in &[PasteType::Url, PasteType::Oneshot] { + fs::create_dir_all(paste_type.get_path(&config.server.upload_path))?; + } + config.paste.random_url.enabled = false; let paste = Paste { data: vec![116, 101, 115, 116], - type_: PasteType::File, + type_: PasteType::Oneshot, }; let file_name = paste.store_file("test.file", &config)?; + let file_path = PasteType::Oneshot + .get_path(&config.server.upload_path) + .join(&file_name); assert_eq!("test.file", &file_name); - assert_eq!("test", fs::read_to_string(&file_name)?); - fs::remove_file(file_name)?; - - fs::create_dir_all(PasteType::Url.get_path(&config.server.upload_path))?; + assert_eq!("test", fs::read_to_string(&file_path)?); + fs::remove_file(file_path)?; config.paste.random_url.enabled = true; let url = String::from("https://orhun.dev/"); @@ -221,7 +247,9 @@ mod tests { }; assert!(paste.store_url(&config).is_err()); - fs::remove_dir(PasteType::Url.get_path(&config.server.upload_path))?; + for paste_type in &[PasteType::Url, PasteType::Oneshot] { + fs::remove_dir(paste_type.get_path(&config.server.upload_path))?; + } Ok(()) } diff --git a/src/server.rs b/src/server.rs index badee3c..47dcb1f 100644 --- a/src/server.rs +++ b/src/server.rs @@ -29,27 +29,42 @@ async fn serve( ) -> Result { let mut path = config.server.upload_path.join(&*file); let mut paste_type = PasteType::File; - for type_ in &[PasteType::Url] { - if !path.exists() - || path.file_name().map(|v| v.to_str()).flatten() == Some(&type_.get_dir()) - { - path = config.server.upload_path.join(type_.get_dir()).join(&*file); - paste_type = *type_; - break; + if !path.exists() || path.is_dir() { + for type_ in &[PasteType::Url, PasteType::Oneshot] { + let alt_path = type_.get_path(&config.server.upload_path).join(&*file); + if alt_path.exists() + || path.file_name().map(|v| v.to_str()).flatten() == Some(&type_.get_dir()) + { + path = alt_path; + paste_type = *type_; + break; + } } } match paste_type { - PasteType::File => Ok(NamedFile::open(&path)? - .disable_content_disposition() - .set_content_type( - mime::get_mime_type(&config.paste.mime_override, file.to_string()) - .map_err(error::ErrorInternalServerError)?, - ) - .prefer_utf8(true) - .into_response(&request)?), + PasteType::File | PasteType::Oneshot => { + let response = NamedFile::open(&path)? + .disable_content_disposition() + .set_content_type( + mime::get_mime_type(&config.paste.mime_override, file.to_string()) + .map_err(error::ErrorInternalServerError)?, + ) + .prefer_utf8(true) + .into_response(&request)?; + if paste_type.is_oneshot() { + fs::rename( + path, + PasteType::Trash + .get_path(&config.server.upload_path) + .join(&*file), + )?; + } + Ok(response) + } PasteType::Url => Ok(HttpResponse::Found() .header("Location", fs::read_to_string(&path)?) .finish()), + PasteType::Trash => unreachable!(), } } @@ -86,8 +101,11 @@ async fn upload( type_: paste_type, }; let file_name = match paste_type { - PasteType::File => paste.store_file(content.get_file_name()?, &config)?, + PasteType::File | PasteType::Oneshot => { + paste.store_file(content.get_file_name()?, &config)? + } PasteType::Url => paste.store_url(&config)?, + PasteType::Trash => unreachable!(), }; log::info!("{} ({}) is uploaded from {}", file_name, bytes_unit, host); urls.push(format!(