2025-04-09 21:13:25 +02:00

126 lines
3.3 KiB
Rust

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<File> {
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<File> {
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<File> {
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<File> {
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<Vec<u8>> {
let file = get_file_with_id(id)?;
get_file_content(&file).await
}
pub async fn get_file_content(file: &File) -> anyhow::Result<Vec<u8>> {
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<bool> {
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<Vec<File>> {
Ok(files::table.get_results(&mut db()?)?)
}
/// Remove unused files
pub async fn run_garbage_collector() -> anyhow::Result<usize> {
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)
}