diff --git a/src/config.rs b/src/config.rs index 8f77ffa..f3b07e1 100644 --- a/src/config.rs +++ b/src/config.rs @@ -140,14 +140,6 @@ pub(crate) enum Format { } impl Format { - pub(crate) fn to_mime(&self) -> mime::Mime { - match self { - Format::Jpeg => mime::IMAGE_JPEG, - Format::Png => mime::IMAGE_PNG, - Format::Webp => "image/webp".parse().unwrap(), - } - } - pub(crate) fn to_magick_format(&self) -> &'static str { match self { Format::Jpeg => "JPEG", diff --git a/src/magick.rs b/src/magick.rs index d804d8e..e368644 100644 --- a/src/magick.rs +++ b/src/magick.rs @@ -19,7 +19,15 @@ pub(crate) fn details_hint(filename: &str) -> Option { } } -#[derive(Debug)] +pub(crate) fn image_webp() -> mime::Mime { + "image/webp".parse().unwrap() +} + +pub(crate) fn video_mp4() -> mime::Mime { + "video/mp4".parse().unwrap() +} + +#[derive(Copy, Clone, Debug)] pub(crate) enum ValidInputType { Mp4, Gif, @@ -38,6 +46,24 @@ impl ValidInputType { Self::Webp => "WEBP", } } + + pub(crate) fn to_ext(&self) -> &'static str { + match self { + Self::Mp4 => ".mp4", + Self::Gif => ".gif", + Self::Png => ".png", + Self::Jpeg => ".jpeg", + Self::Webp => ".webp", + } + } + + pub(crate) fn from_format(format: Format) -> Self { + match format { + Format::Jpeg => ValidInputType::Jpeg, + Format::Png => ValidInputType::Png, + Format::Webp => ValidInputType::Webp, + } + } } #[derive(Debug)] @@ -157,11 +183,11 @@ fn parse_details(s: std::borrow::Cow<'_, str>) -> Result { } let mime_type = match format { - "MP4" => crate::validate::video_mp4(), + "MP4" => video_mp4(), "GIF" => mime::IMAGE_GIF, "PNG" => mime::IMAGE_PNG, "JPEG" => mime::IMAGE_JPEG, - "WEBP" => crate::validate::image_webp(), + "WEBP" => image_webp(), _ => return Err(UploadError::UnsupportedFormat.into()), }; diff --git a/src/main.rs b/src/main.rs index a335286..875a485 100644 --- a/src/main.rs +++ b/src/main.rs @@ -52,7 +52,6 @@ use self::{ migrate::LatestDb, store::Store, upload_manager::{Details, UploadManager, UploadManagerSession}, - validate::{image_webp, video_mp4}, }; const MEGABYTES: usize = 1024 * 1024; @@ -740,7 +739,7 @@ where .transform_error(transform_error) .field( "images", - Field::array(Field::file(move |filename, content_type, stream| { + Field::array(Field::file(move |filename, _, stream| { let manager = manager2.clone(); let span = tracing::info_span!("file-import", ?filename); @@ -752,7 +751,6 @@ where .session() .import( filename, - content_type, validate_imports, map_error::map_crate_error(stream), ) @@ -837,7 +835,6 @@ async fn main() -> anyhow::Result<()> { let manager = UploadManager::new(store, db, CONFIG.format()).await?; - // TODO: move restructure to FileStore manager.restructure().await?; launch(manager).await } diff --git a/src/upload_manager/session.rs b/src/upload_manager/session.rs index 69dd910..1f368a5 100644 --- a/src/upload_manager/session.rs +++ b/src/upload_manager/session.rs @@ -1,5 +1,6 @@ use crate::{ error::{Error, UploadError}, + magick::ValidInputType, migrate::{alias_id_key, alias_key}, store::Store, upload_manager::{ @@ -142,7 +143,6 @@ where pub(crate) async fn import( mut self, alias: String, - content_type: mime::Mime, validate: bool, mut stream: impl Stream> + Unpin, ) -> Result { @@ -197,7 +197,7 @@ where } debug!("Validating bytes"); - let (content_type, validated_reader) = crate::validate::validate_image_bytes( + let (input_type, validated_reader) = crate::validate::validate_image_bytes( bytes_mut.freeze(), self.manager.inner.format.clone(), true, @@ -214,10 +214,10 @@ where let hash = hasher_reader.finalize_reset().await?; debug!("Adding alias"); - self.add_alias(&hash, content_type.clone()).await?; + self.add_alias(&hash, input_type).await?; debug!("Saving file"); - self.save_upload(&identifier, hash, content_type).await?; + self.save_upload(&identifier, hash, input_type).await?; // Return alias to file Ok(self) @@ -228,9 +228,9 @@ where &self, identifier: &S::Identifier, hash: Hash, - content_type: mime::Mime, + input_type: ValidInputType, ) -> Result<(), Error> { - let (dup, name) = self.check_duplicate(hash, content_type).await?; + let (dup, name) = self.check_duplicate(hash, input_type).await?; // bail early with alias to existing file if this is a duplicate if dup.exists() { @@ -246,15 +246,15 @@ where } // check for an already-uploaded image with this hash, returning the path to the target file - #[instrument(skip(self, hash, content_type))] + #[instrument(skip(self, hash, input_type))] async fn check_duplicate( &self, hash: Hash, - content_type: mime::Mime, + input_type: ValidInputType, ) -> Result<(Dup, String), Error> { let main_tree = self.manager.inner.main_tree.clone(); - let filename = self.next_file(content_type).await?; + let filename = self.next_file(input_type).await?; let filename2 = filename.clone(); let hash2 = hash.as_slice().to_vec(); @@ -287,11 +287,11 @@ where } // generate a short filename that isn't already in-use - #[instrument(skip(self, content_type))] - async fn next_file(&self, content_type: mime::Mime) -> Result { + #[instrument(skip(self, input_type))] + async fn next_file(&self, input_type: ValidInputType) -> Result { loop { debug!("Filename generation loop"); - let filename = file_name(Uuid::new_v4(), content_type.clone())?; + let filename = file_name(Uuid::new_v4(), input_type); let identifier_tree = self.manager.inner.identifier_tree.clone(); let filename2 = filename.clone(); @@ -319,9 +319,9 @@ where // Add an alias to an existing file // // This will help if multiple 'users' upload the same file, and one of them wants to delete it - #[instrument(skip(self, hash, content_type))] - async fn add_alias(&mut self, hash: &Hash, content_type: mime::Mime) -> Result<(), Error> { - let alias = self.next_alias(hash, content_type).await?; + #[instrument(skip(self, hash, input_type))] + async fn add_alias(&mut self, hash: &Hash, input_type: ValidInputType) -> Result<(), Error> { + let alias = self.next_alias(hash, input_type).await?; self.store_hash_id_alias_mapping(hash, &alias).await?; @@ -365,11 +365,15 @@ where } // Generate an alias to the file - #[instrument(skip(self, hash, content_type))] - async fn next_alias(&mut self, hash: &Hash, content_type: mime::Mime) -> Result { + #[instrument(skip(self, hash, input_type))] + async fn next_alias( + &mut self, + hash: &Hash, + input_type: ValidInputType, + ) -> Result { loop { debug!("Alias gen loop"); - let alias = file_name(Uuid::new_v4(), content_type.clone())?; + let alias = file_name(Uuid::new_v4(), input_type); self.alias = Some(alias.clone()); let res = self.save_alias_hash_mapping(hash, &alias).await?; @@ -407,20 +411,6 @@ where } } -fn file_name(name: Uuid, content_type: mime::Mime) -> Result { - Ok(format!("{}{}", name, to_ext(content_type)?)) -} - -fn to_ext(mime: mime::Mime) -> Result<&'static str, Error> { - if mime == mime::IMAGE_PNG { - Ok(".png") - } else if mime == mime::IMAGE_JPEG { - Ok(".jpg") - } else if mime == crate::video_mp4() { - Ok(".mp4") - } else if mime == crate::image_webp() { - Ok(".webp") - } else { - Err(UploadError::UnsupportedFormat.into()) - } +fn file_name(name: Uuid, input_type: ValidInputType) -> String { + format!("{}{}", name, input_type.to_ext()) } diff --git a/src/validate.rs b/src/validate.rs index 7cbc6af..fe77aad 100644 --- a/src/validate.rs +++ b/src/validate.rs @@ -5,14 +5,6 @@ use actix_web::web::Bytes; use tokio::io::AsyncRead; use tracing::instrument; -pub(crate) fn image_webp() -> mime::Mime { - "image/webp".parse().unwrap() -} - -pub(crate) fn video_mp4() -> mime::Mime { - "video/mp4".parse().unwrap() -} - struct UnvalidatedBytes { bytes: Bytes, written: usize, @@ -45,56 +37,48 @@ pub(crate) async fn validate_image_bytes( bytes: Bytes, prescribed_format: Option, validate: bool, -) -> Result<(mime::Mime, impl AsyncRead + Unpin), Error> { +) -> Result<(ValidInputType, impl AsyncRead + Unpin), Error> { let input_type = crate::magick::input_type_bytes(bytes.clone()).await?; if !validate { - let mime_type = match input_type { - ValidInputType::Gif => video_mp4(), - ValidInputType::Mp4 => mime::IMAGE_GIF, - ValidInputType::Jpeg => mime::IMAGE_JPEG, - ValidInputType::Png => mime::IMAGE_PNG, - ValidInputType::Webp => image_webp(), - }; - - return Ok((mime_type, Either::left(UnvalidatedBytes::new(bytes)))); + return Ok((input_type, Either::left(UnvalidatedBytes::new(bytes)))); } match (prescribed_format, input_type) { (_, ValidInputType::Gif) => Ok(( - video_mp4(), + ValidInputType::Mp4, Either::right(Either::left(crate::ffmpeg::to_mp4_bytes( bytes, InputFormat::Gif, )?)), )), (_, ValidInputType::Mp4) => Ok(( - video_mp4(), + ValidInputType::Mp4, Either::right(Either::left(crate::ffmpeg::to_mp4_bytes( bytes, InputFormat::Mp4, )?)), )), (Some(Format::Jpeg) | None, ValidInputType::Jpeg) => Ok(( - mime::IMAGE_JPEG, + ValidInputType::Jpeg, Either::right(Either::right(Either::left( crate::exiftool::clear_metadata_bytes_read(bytes)?, ))), )), (Some(Format::Png) | None, ValidInputType::Png) => Ok(( - mime::IMAGE_PNG, + ValidInputType::Png, Either::right(Either::right(Either::left( crate::exiftool::clear_metadata_bytes_read(bytes)?, ))), )), (Some(Format::Webp) | None, ValidInputType::Webp) => Ok(( - image_webp(), + ValidInputType::Webp, Either::right(Either::right(Either::right(Either::left( crate::magick::clear_metadata_bytes_read(bytes)?, )))), )), (Some(format), _) => Ok(( - format.to_mime(), + ValidInputType::from_format(format), Either::right(Either::right(Either::right(Either::right( crate::magick::convert_bytes_read(bytes, format)?, )))),