126 lines
3.3 KiB
Rust
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)
|
|
}
|