From ef30e38246cd8521efcc50ce28ad17214ee631cb Mon Sep 17 00:00:00 2001 From: trivernis Date: Sat, 15 Jan 2022 20:14:14 +0100 Subject: [PATCH] Implement api to change the status of a file Signed-off-by: trivernis --- mediarepo-daemon/Cargo.lock | 4 +- mediarepo-daemon/mediarepo-core/Cargo.toml | 2 +- .../mediarepo-core/src/fs/file_hash_store.rs | 11 +++++ .../mediarepo-model/src/file/mod.rs | 47 ++++++++++++++++++- mediarepo-daemon/mediarepo-model/src/repo.rs | 10 ++++ .../src/namespaces/files/mod.rs | 36 ++++++++++++-- 6 files changed, 103 insertions(+), 7 deletions(-) diff --git a/mediarepo-daemon/Cargo.lock b/mediarepo-daemon/Cargo.lock index fe5ae42..76c9a5a 100644 --- a/mediarepo-daemon/Cargo.lock +++ b/mediarepo-daemon/Cargo.lock @@ -1182,8 +1182,8 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] name = "mediarepo-api" -version = "0.26.0" -source = "git+https://github.com/Trivernis/mediarepo-api.git?rev=06d51ed147c4f65361f8351c30e3417ffd457f0c#06d51ed147c4f65361f8351c30e3417ffd457f0c" +version = "0.27.0" +source = "git+https://github.com/Trivernis/mediarepo-api.git?rev=9fd25e4696cdd68886fa98579aef4fa2ca561dc0#9fd25e4696cdd68886fa98579aef4fa2ca561dc0" dependencies = [ "bromine", "chrono", diff --git a/mediarepo-daemon/mediarepo-core/Cargo.toml b/mediarepo-daemon/mediarepo-core/Cargo.toml index a070b1e..682eb77 100644 --- a/mediarepo-daemon/mediarepo-core/Cargo.toml +++ b/mediarepo-daemon/mediarepo-core/Cargo.toml @@ -44,7 +44,7 @@ features = ["toml"] [dependencies.mediarepo-api] git = "https://github.com/Trivernis/mediarepo-api.git" -rev = "06d51ed147c4f65361f8351c30e3417ffd457f0c" +rev = "9fd25e4696cdd68886fa98579aef4fa2ca561dc0" features = ["bromine"] [features] diff --git a/mediarepo-daemon/mediarepo-core/src/fs/file_hash_store.rs b/mediarepo-daemon/mediarepo-core/src/fs/file_hash_store.rs index 9f6c88e..b23fcf7 100644 --- a/mediarepo-daemon/mediarepo-core/src/fs/file_hash_store.rs +++ b/mediarepo-daemon/mediarepo-core/src/fs/file_hash_store.rs @@ -78,6 +78,17 @@ impl FileHashStore { Ok(()) } + pub async fn delete_file(&self, descriptor: &[u8]) -> RepoResult<()> { + let path = self.descriptor_to_file_path(descriptor); + if !path.exists() { + tracing::warn!("file {:?} doesn't exist", path); + return Ok(()); + } + fs::remove_file(path).await?; + + Ok(()) + } + /// Scans the size of the folder #[inline] pub async fn get_size(&self) -> RepoResult { diff --git a/mediarepo-daemon/mediarepo-model/src/file/mod.rs b/mediarepo-daemon/mediarepo-model/src/file/mod.rs index dc36e5c..dc7dfa8 100644 --- a/mediarepo-daemon/mediarepo-model/src/file/mod.rs +++ b/mediarepo-daemon/mediarepo-model/src/file/mod.rs @@ -6,7 +6,7 @@ use std::str::FromStr; use mediarepo_core::content_descriptor::encode_content_descriptor; use sea_orm::prelude::*; -use sea_orm::{DatabaseConnection, Set}; +use sea_orm::{ConnectionTrait, DatabaseConnection, Set}; use sea_orm::{JoinType, QuerySelect}; use tokio::io::{AsyncReadExt, BufReader}; @@ -14,10 +14,12 @@ use crate::file::filter::FilterProperty; use crate::file_metadata::FileMetadata; use mediarepo_core::error::{RepoError, RepoResult}; use mediarepo_core::fs::file_hash_store::FileHashStore; +use mediarepo_core::mediarepo_api::types::files::FileStatus as ApiFileStatus; use mediarepo_core::thumbnailer::{self, Thumbnail as ThumbnailerThumb, ThumbnailSize}; use mediarepo_database::entities::content_descriptor; use mediarepo_database::entities::content_descriptor_tag; use mediarepo_database::entities::file; +use mediarepo_database::entities::file_metadata; use mediarepo_database::entities::namespace; use mediarepo_database::entities::tag; @@ -29,6 +31,16 @@ pub enum FileStatus { Deleted = 30, } +impl From for FileStatus { + fn from(s: ApiFileStatus) -> Self { + match s { + ApiFileStatus::Imported => Self::Imported, + ApiFileStatus::Archived => Self::Archived, + ApiFileStatus::Deleted => Self::Deleted, + } + } +} + #[derive(Clone)] pub struct File { db: DatabaseConnection, @@ -177,6 +189,17 @@ impl File { } } + pub async fn set_status(&mut self, status: FileStatus) -> RepoResult<()> { + let active_model = file::ActiveModel { + id: Set(self.model.id), + status: Set(status as i32), + ..Default::default() + }; + self.model = active_model.update(&self.db).await?; + + Ok(()) + } + /// Returns the metadata associated with this file /// A file MUST always have metadata associated pub async fn metadata(&self) -> RepoResult { @@ -286,4 +309,26 @@ impl File { Ok(thumbs) } + + /// Deletes the file as well as the content descriptor, tag mappings and metadata about the file + #[tracing::instrument(level = "debug", skip(self))] + pub async fn delete(self) -> RepoResult<()> { + let trx = self.db.begin().await?; + file_metadata::Entity::delete_many() + .filter(file_metadata::Column::FileId.eq(self.model.id)) + .exec(&trx) + .await?; + self.model.delete(&trx).await?; + content_descriptor_tag::Entity::delete_many() + .filter(content_descriptor_tag::Column::CdId.eq(self.content_descriptor.id)) + .exec(&trx) + .await?; + content_descriptor::Entity::delete_many() + .filter(content_descriptor::Column::Id.eq(self.content_descriptor.id)) + .exec(&trx) + .await?; + trx.commit().await?; + + Ok(()) + } } diff --git a/mediarepo-daemon/mediarepo-model/src/repo.rs b/mediarepo-daemon/mediarepo-model/src/repo.rs index 7700ade..18eca9a 100644 --- a/mediarepo-daemon/mediarepo-model/src/repo.rs +++ b/mediarepo-daemon/mediarepo-model/src/repo.rs @@ -144,6 +144,16 @@ impl Repo { .await } + /// Deletes a file from the database and disk + #[tracing::instrument(level = "debug", skip(self, file))] + pub async fn delete_file(&self, file: File) -> RepoResult<()> { + let cd = file.cd().to_owned(); + file.delete().await?; + self.main_storage.delete_file(&cd).await?; + + Ok(()) + } + /// Returns all thumbnails of a file pub async fn get_file_thumbnails(&self, file_cd: &[u8]) -> RepoResult> { let file_cd = encode_content_descriptor(file_cd); diff --git a/mediarepo-daemon/mediarepo-socket/src/namespaces/files/mod.rs b/mediarepo-daemon/mediarepo-socket/src/namespaces/files/mod.rs index 4dce144..f0d07a6 100644 --- a/mediarepo-daemon/mediarepo-socket/src/namespaces/files/mod.rs +++ b/mediarepo-daemon/mediarepo-socket/src/namespaces/files/mod.rs @@ -11,7 +11,7 @@ use mediarepo_core::itertools::Itertools; use mediarepo_core::mediarepo_api::types::files::{ AddFileRequestHeader, FileBasicDataResponse, FileMetadataResponse, GetFileThumbnailOfSizeRequest, GetFileThumbnailsRequest, ReadFileRequest, - ThumbnailMetadataResponse, UpdateFileNameRequest, + ThumbnailMetadataResponse, UpdateFileNameRequest, UpdateFileStatusRequest, }; use mediarepo_core::mediarepo_api::types::filtering::FindFilesRequest; use mediarepo_core::mediarepo_api::types::identifier::FileIdentifier; @@ -38,7 +38,9 @@ impl NamespaceProvider for FilesNamespace { "get_thumbnails" => Self::thumbnails, "get_thumbnail_of_size" => Self::get_thumbnail_of_size, "update_file_name" => Self::update_file_name, - "delete_thumbnails" => Self::delete_thumbnails + "delete_thumbnails" => Self::delete_thumbnails, + "update_file_status" => Self::update_status, + "delete_file" => Self::delete_file ); } } @@ -160,11 +162,26 @@ impl FilesNamespace { Ok(()) } + #[tracing::instrument(skip_all)] + async fn update_status(ctx: &Context, event: Event) -> IPCResult<()> { + let request = event.payload::()?; + let repo = get_repo_from_context(ctx).await; + let mut file = file_by_identifier(request.file_id, &repo).await?; + file.set_status(request.status.into()).await?; + ctx.emit_to( + Self::name(), + "update_file_status", + FileBasicDataResponse::from_model(file), + ) + .await?; + + Ok(()) + } + /// Reads the binary contents of a file #[tracing::instrument(skip_all)] async fn read_file(ctx: &Context, event: Event) -> IPCResult<()> { let request = event.payload::()?; - let repo = get_repo_from_context(ctx).await; let file = file_by_identifier(request.id, &repo).await?; let bytes = repo.get_file_bytes(&file).await?; @@ -175,6 +192,19 @@ impl FilesNamespace { Ok(()) } + /// Deletes a file + #[tracing::instrument(skip_all)] + async fn delete_file(ctx: &Context, event: Event) -> IPCResult<()> { + let id = event.payload::()?; + let repo = get_repo_from_context(ctx).await; + let file = file_by_identifier(id, &repo).await?; + repo.delete_file(file).await?; + + ctx.emit_to(Self::name(), "delete_file", ()).await?; + + Ok(()) + } + /// Returns a list of available thumbnails of a file #[tracing::instrument(skip_all)] async fn thumbnails(ctx: &Context, event: Event) -> IPCResult<()> {