From 224b7e52c7a7a02d36a82c070925a2e506b7b2f4 Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Tue, 20 Sep 2022 15:44:46 +0200 Subject: Delete model --- src/models/model.rs | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/models/user.rs | 2 +- src/routes/model.rs | 35 +++++++++++++++++++++++++++++++++-- 3 files changed, 84 insertions(+), 4 deletions(-) diff --git a/src/models/model.rs b/src/models/model.rs index 08bfe88..80eb5ad 100644 --- a/src/models/model.rs +++ b/src/models/model.rs @@ -144,7 +144,7 @@ impl Model { /// List all models pub async fn list(page: i64) -> Result, AppError> { let pool = unsafe { get_client() }; - let rows: Vec= sqlx::query_as( + let rows: Vec = sqlx::query_as( r#" SELECT models.*, @@ -165,6 +165,22 @@ impl Model { Ok(rows) } + /// Delete a model + pub async fn delete(model_id: i32) -> Result<(), AppError> { + let pool = unsafe { get_client() }; + + sqlx::query( + r#" + DELETE FROM models WHERE id = $1 + "#, + ) + .bind(model_id) + .execute(pool) + .await?; + + Ok(()) + } + /// Return the number of models. pub async fn count() -> Result { let pool = unsafe { get_client() }; @@ -184,6 +200,23 @@ impl ModelUser { None => json!(0), } } + + pub async fn upload_paths(&self) -> Option> { + if self.uploads.is_none() { + return None; + } + + let uploads = ModelUpload::find_by_model(self.id) + .await + .unwrap_or_default(); + + let paths = uploads + .iter() + .map(|x| x.filepath.clone()) + .collect::>(); + + return Some(paths); + } } impl ModelUpload { @@ -216,4 +249,20 @@ impl ModelUpload { Ok(rec) } + + /// Find all paths of a model + pub async fn find_by_model(model_id: i32) -> Result, AppError> { + let pool = unsafe { get_client() }; + + let rec: Vec = sqlx::query_as( + r#" + SELECT * FROM uploads WHERE model_id = $1 + "#, + ) + .bind(model_id) + .fetch_all(pool) + .await?; + + Ok(rec) + } } diff --git a/src/models/user.rs b/src/models/user.rs index 964a57c..c4debf5 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -27,7 +27,7 @@ pub struct UserList { pub id: i32, email: String, username: String, - is_staff: Option, + pub is_staff: Option, #[serde_as(as = "NoneAsEmptyString")] pub avatar: Option, } diff --git a/src/routes/model.rs b/src/routes/model.rs index 3666504..4f935a9 100644 --- a/src/routes/model.rs +++ b/src/routes/model.rs @@ -1,15 +1,17 @@ use crate::{ errors::AppError, - files::upload, + files::{delete_upload, upload}, models::{ auth::Claims, model::{Model, ModelCreate, ModelUpload, ModelUser}, + user::User, }, pagination::Pagination, routes::JsonCreate, }; use axum::{ extract::{ContentLengthLimit, Multipart, Path, Query}, + http::StatusCode, routing::{get, post}, Json, Router, }; @@ -19,7 +21,7 @@ use serde::Serialize; pub fn create_route() -> Router { Router::new() .route("/", get(list_models).post(create_model)) - .route("/:id", get(get_model)) + .route("/:id", get(get_model).delete(delete_model)) .route("/:id/upload", post(upload_model_file)) } @@ -95,3 +97,32 @@ async fn upload_model_file( Err(e) => Err(e), } } + +/// The owner or a staffer can delete a model +async fn delete_model(claims: Claims, Path(model_id): Path) -> Result { + let model = match Model::find_by_id(model_id).await { + Ok(model) => model, + Err(_) => { + return Err(AppError::NotFound("Model not found".to_string())); + } + }; + + let user = User::find_by_id(claims.user_id).await?; + + let uploads: Vec = model.upload_paths().await.unwrap(); + + if model.author_id() != user.id { + if !user.is_staff.unwrap() { + return Err(AppError::Unauthorized); + } + } + + // If the model has been deleted, remove all old uploads from the file system + if Model::delete(model_id).await.is_ok() { + uploads + .iter() + .for_each(|path: &String| delete_upload(path).unwrap_or_default()); + } + + Ok(StatusCode::NO_CONTENT) +} -- cgit v1.2.3-71-g8e6c