use crate::connections::db_connection::db; use crate::connections::s3_connection; use crate::models::files::{File, FileID, NewFile}; use crate::models::users::UserID; use crate::schema::files; use crate::utils::crypt_utils::sha512; use crate::utils::time_utils::time; use diesel::prelude::*; use mime_guess::Mime; #[derive(thiserror::Error, Debug)] enum FilesServiceError { #[error("UnknownMimeType!")] UnknownMimeType, } pub async fn create_file_with_file_name( user_id: UserID, file_name: &str, bytes: &[u8], ) -> anyhow::Result { let mime = mime_guess::from_path(file_name) .first() .ok_or(FilesServiceError::UnknownMimeType)?; create_file_with_mimetype(user_id, file_name, &mime, bytes).await } pub async fn create_file_with_mimetype( user_id: UserID, file_name: &str, mime_type: &Mime, bytes: &[u8], ) -> anyhow::Result { let sha512 = sha512(bytes); if let Ok(f) = get_file_with_hash(user_id, &sha512) { return Ok(f); } let file = NewFile { time_create: time() as i64, mime_type: mime_type.as_ref(), sha512: &sha512, file_size: bytes.len() as i32, file_name, user_id: user_id.0, }; s3_connection::upload_file(&file.file_path(), bytes).await?; let res = diesel::insert_into(files::table) .values(&file) .get_result(&mut db()?)?; Ok(res) } pub fn get_file_with_hash(user_id: UserID, sha512: &str) -> anyhow::Result { Ok(files::table .filter( files::dsl::sha512 .eq(sha512) .and(files::dsl::user_id.eq(user_id.0)), ) .first(&mut db()?)?) } pub fn get_file_with_id(id: FileID) -> anyhow::Result { Ok(files::table .filter(files::dsl::id.eq(id.0)) .first(&mut db()?)?) } pub async fn get_file_content_by_id(id: FileID) -> anyhow::Result> { let file = get_file_with_id(id)?; get_file_content(&file).await } pub async fn get_file_content(file: &File) -> anyhow::Result> { s3_connection::get_file(&file.file_path()).await } /// Delete the file if it is not referenced anymore in the database. Returns true /// if the file was actually deleted, false otherwise pub async fn delete_file_if_unused(id: FileID) -> anyhow::Result { let file = get_file_with_id(id)?; let res = diesel::delete(files::dsl::files.filter(files::dsl::id.eq(file.id().0))) .execute(&mut db()?); match res { Ok(_) => { s3_connection::delete_file_if_exists(&file.file_path()).await?; log::info!("File {:?} was deleted", file.id()); Ok(true) } Err(e) => { log::info!( "File {:?} could not be deleted, it must be used somewhere: {e}", file.id() ); Ok(false) } } } /// Get the entire list of file pub async fn get_entire_list() -> anyhow::Result> { Ok(files::table.get_results(&mut db()?)?) } /// Remove unused files pub async fn run_garbage_collector() -> anyhow::Result { let mut count_deleted = 0; for file in get_entire_list().await? { if delete_file_if_unused(file.id()).await? { count_deleted += 1; } } Ok(count_deleted) }