diff options
| -rw-r--r-- | src/models/warning.rs | 105 | ||||
| -rw-r--r-- | src/routes/warning.rs | 33 |
2 files changed, 118 insertions, 20 deletions
diff --git a/src/models/warning.rs b/src/models/warning.rs index 68c2773..2cb051d 100644 --- a/src/models/warning.rs +++ b/src/models/warning.rs @@ -3,34 +3,51 @@ use chrono::{Local, NaiveDateTime}; use serde::{Deserialize, Serialize}; use sqlx::types::JsonValue; use sqlx::Row; +use std::convert::From; /// Model for warnings. #[derive(Deserialize, Serialize, sqlx::FromRow)] pub struct Warning { - id: i32, - user_id: Option<i32>, - model_id: Option<i32>, - resolved_by: Option<i32>, - note: String, - admin_note: String, - created: NaiveDateTime, - updated: NaiveDateTime, + pub id: i32, + pub user_id: Option<i32>, + pub model_id: Option<i32>, + pub resolved_by: Option<i32>, + pub note: String, + pub admin_note: String, + pub created: NaiveDateTime, + pub updated: NaiveDateTime, } #[derive(Serialize, sqlx::FromRow)] pub struct WarningUser { - id: i32, - user_id: Option<i32>, - model_id: Option<i32>, - resolved_by: Option<i32>, - note: String, - admin_note: String, - created: NaiveDateTime, - updated: NaiveDateTime, + pub id: i32, + pub user_id: Option<i32>, + pub model_id: Option<i32>, + pub resolved_by: Option<i32>, + pub note: String, + pub admin_note: String, + pub created: NaiveDateTime, + pub updated: NaiveDateTime, user: Option<JsonValue>, resolved: Option<JsonValue>, } +/// Impl conversion from `WarningUser` to `Warning` +impl From<WarningUser> for Warning { + fn from(item: WarningUser) -> Self { + Self { + id: item.id, + user_id: item.user_id, + model_id: item.model_id, + resolved_by: item.resolved_by, + note: item.note, + admin_note: item.admin_note, + created: item.created, + updated: item.created, + } + } +} + /// Payload used to create a new warning #[derive(Deserialize)] pub struct WarningCreate { @@ -38,6 +55,12 @@ pub struct WarningCreate { pub note: String, } +/// Payload used to edit a warning +#[derive(Deserialize)] +pub struct WarningEdit { + pub admin_note: String, +} + /// Payload used for warning filtering #[derive(Deserialize)] pub struct WarningFilterPayload { @@ -107,6 +130,31 @@ impl Warning { Ok(rows) } + /// Returns the warning with id = `warning_id` + pub async fn find_by_id(warning_id: i32) -> Result<WarningUser, AppError> { + let pool = unsafe { get_client() }; + + let rec: WarningUser = sqlx::query_as( + r#" + SELECT + warnings.*, + json_build_object('id', users.id, 'name', users.name, 'email', users.email, 'username', users.username, 'is_staff', users.is_staff, 'avatar', users.avatar) as user, + coalesce(r.data, '{}'::json) as resolved + FROM warnings + JOIN users ON users.id = warnings.user_id + LEFT JOIN ( + SELECT id, json_build_object('id', r.id, 'name', r.name, 'email', r.email, 'username', r.username, 'is_staff', r.is_staff, 'avatar', r.avatar) as data + FROM users r + ) r ON r.id = warnings.resolved_by + WHERE warnings.id = $1 + "#) + .bind(warning_id) + .fetch_one(pool) + .await?; + + Ok(rec) + } + /// Return the number of warnings. pub async fn count(user_id: Option<i32>) -> Result<i64, AppError> { let pool = unsafe { get_client() }; @@ -221,4 +269,29 @@ impl Warning { let count: i64 = cursor.try_get(0).unwrap(); Ok(count) } + + /// Edit a warning + pub async fn edit(&mut self, resolver: i32, payload: WarningEdit) -> Result<(), AppError> { + let pool = unsafe { get_client() }; + + let now = Local::now().naive_utc(); + + sqlx::query( + r#" + UPDATE warnings SET admin_note = $1, resolved_by = $2, updated = $3 WHERE id = $4 + "#, + ) + .bind(&payload.admin_note) + .bind(resolver) + .bind(now) + .bind(self.id) + .execute(pool) + .await?; + + self.admin_note = payload.admin_note; + self.resolved_by = Some(resolver); + self.updated = now; + + Ok(()) + } } diff --git a/src/routes/warning.rs b/src/routes/warning.rs index 3a8573e..bb1306e 100644 --- a/src/routes/warning.rs +++ b/src/routes/warning.rs @@ -4,14 +4,14 @@ use crate::{ auth::Claims, model::Model, user::User, - warning::{Warning, WarningCreate, WarningFilter, WarningFilterPayload}, + warning::{Warning, WarningCreate, WarningEdit, WarningFilter, WarningFilterPayload}, }, pagination::{Pagination, WarningPagination}, routes::JsonCreate, }; use axum::{ - extract::Query, - routing::{get, post}, + extract::{Path, Query}, + routing::{get, post, put}, Json, Router, }; @@ -19,6 +19,7 @@ use axum::{ pub fn create_route() -> Router { Router::new() .route("/", get(list_warnings).post(create_warning)) + .route("/:id", put(edit_warning)) .route("/filter", post(filter_warnings)) } @@ -52,7 +53,7 @@ async fn create_warning( ) -> Result<JsonCreate<Warning>, AppError> { let model = match Model::find_by_id(payload.model_id).await { Ok(model) => model, - Err(_) => return Err(AppError::NotFound("Model not found".to_string())), + Err(_) => return Err(AppError::NotFound("Report not found".to_string())), }; let warning = Warning::new(claims.user_id, model.id, payload.note); @@ -62,6 +63,30 @@ async fn create_warning( Ok(JsonCreate(warning_new)) } +/// Staffers can edit a warning +async fn edit_warning( + Json(payload): Json<WarningEdit>, + claims: Claims, + Path(warning_id): Path<i32>, +) -> Result<Json<Warning>, AppError> { + let mut warning: Warning = match Warning::find_by_id(warning_id).await { + Ok(warning) => warning.into(), + Err(_) => { + return Err(AppError::NotFound("Report not found".to_string())); + } + }; + + let user = User::find_by_id(claims.user_id).await?; + + if !(user.is_staff.unwrap()) { + return Err(AppError::Unauthorized); + } + + warning.edit(user.id, payload).await?; + + Ok(Json(warning)) +} + /// Apply a filter to warnings list async fn filter_warnings( Json(payload): Json<WarningFilterPayload>, |
