From e010223224b28df4b53e763b2a4f2a4fb37cc339 Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Tue, 20 Sep 2022 16:43:18 +0200 Subject: Create warning app --- src/main.rs | 3 +- src/models/mod.rs | 1 + src/models/model.rs | 2 +- src/models/warning.rs | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/routes/mod.rs | 1 + src/routes/warning.rs | 64 ++++++++++++++++++++++++++ 6 files changed, 191 insertions(+), 2 deletions(-) create mode 100644 src/models/warning.rs create mode 100644 src/routes/warning.rs (limited to 'src') diff --git a/src/main.rs b/src/main.rs index 13f8054..66caecd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -56,7 +56,8 @@ async fn create_app() -> Router { let api_routes = Router::new() .nest("/users", routes::user::create_route()) .nest("/auth", routes::auth::create_route()) - .nest("/models", routes::model::create_route()); + .nest("/models", routes::model::create_route()) + .nest("/warnings", routes::warning::create_route()); Router::new() .route( diff --git a/src/models/mod.rs b/src/models/mod.rs index 7e61662..45a11f3 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,3 +1,4 @@ pub mod auth; pub mod model; pub mod user; +pub mod warning; diff --git a/src/models/model.rs b/src/models/model.rs index 5aa3ddf..071f515 100644 --- a/src/models/model.rs +++ b/src/models/model.rs @@ -38,7 +38,7 @@ pub struct ModelCreate { #[derive(Serialize, sqlx::FromRow)] pub struct ModelUser { - id: i32, + pub id: i32, name: String, description: Option, duration: i32, diff --git a/src/models/warning.rs b/src/models/warning.rs new file mode 100644 index 0000000..0725d53 --- /dev/null +++ b/src/models/warning.rs @@ -0,0 +1,122 @@ +use crate::{config::CONFIG, db::get_client, errors::AppError}; +use chrono::{Local, NaiveDateTime}; +use serde::{Deserialize, Serialize}; +use sqlx::Row; + +/// Model for models. +#[derive(Deserialize, Serialize, sqlx::FromRow)] +pub struct Warning { + id: i32, + user_id: Option, + model_id: Option, + resolved_by: Option, + note: String, + admin_note: String, + created: NaiveDateTime, + updated: NaiveDateTime, +} + +/// Payload used to create a new warning +#[derive(Deserialize)] +pub struct WarningCreate { + pub model_id: i32, + pub note: String, +} + +impl Warning { + /// Create a warning means create an object which has an `user_id` (creator of the warning), a + /// `model_id` (suspect model) and a `note` + pub fn new(user_id: i32, model_id: i32, note: String) -> Self { + let now = Local::now().naive_utc(); + Self { + id: 0, + user_id: Some(user_id), + model_id: Some(model_id), + resolved_by: None, + note, + admin_note: String::new(), + created: now, + updated: now, + } + } + + /// List all warnings. A staffer can see all the warnings, a user cannot + pub async fn list(page: i64, user_id: Option) -> Result, AppError> { + let pool = unsafe { get_client() }; + let rows: Vec = match user_id { + Some(id) => { + sqlx::query_as( + r#" + SELECT * FROM warnings WHERE user_id = $1 + LIMIT $2 OFFSET $3 + "#, + ) + .bind(id) + .bind(CONFIG.page_limit) + .bind(CONFIG.page_limit * page) + .fetch_all(pool) + .await? + } + None => { + sqlx::query_as( + r#" + SELECT * FROM warnings + LIMIT $1 OFFSET $2 + "#, + ) + .bind(CONFIG.page_limit) + .bind(CONFIG.page_limit * page) + .fetch_all(pool) + .await? + } + }; + + Ok(rows) + } + + /// Return the number of warnings. + pub async fn count(user_id: Option) -> Result { + let pool = unsafe { get_client() }; + + let cursor = match user_id { + Some(id) => { + sqlx::query(r#"SELECT COUNT(id) as count FROM warnings WHERE user_id = $1"#) + .bind(id) + .fetch_one(pool) + .await? + } + None => { + sqlx::query(r#"SELECT COUNT(id) as count FROM warnings"#) + .fetch_one(pool) + .await? + } + }; + + let count: i64 = cursor.try_get(0).unwrap(); + Ok(count) + } + + /// Create a new upload for model + pub async fn create(warning: Warning) -> Result { + let pool = unsafe { get_client() }; + + let rec: Warning = sqlx::query_as( + r#" + INSERT INTO warnings (user_id, model_id, resolved_by, note, admin_note, created, updated) + VALUES ( $1, $2, $3, $4, $5, $6, $7) + RETURNING * + "#, + ) + .bind(warning.user_id) + .bind(warning.model_id) + .bind(warning.resolved_by) + .bind(warning.note) + .bind(warning.admin_note) + .bind(warning.created) + .bind(warning.updated) + .fetch_one(pool) + .await?; + + Ok(rec) + } +} diff --git a/src/routes/mod.rs b/src/routes/mod.rs index c69f18e..a0e8031 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -1,6 +1,7 @@ pub mod auth; pub mod model; pub mod user; +pub mod warning; use crate::errors::AppError; use axum::{ diff --git a/src/routes/warning.rs b/src/routes/warning.rs new file mode 100644 index 0000000..87ca514 --- /dev/null +++ b/src/routes/warning.rs @@ -0,0 +1,64 @@ +use crate::{ + errors::AppError, + models::{ + auth::Claims, + model::Model, + user::User, + warning::{Warning, WarningCreate}, + }, + pagination::Pagination, + routes::JsonCreate, +}; +use axum::{extract::Query, routing::get, Json, Router}; +use serde::Serialize; + +/// Create routes for `/v1/warnings/` namespace +pub fn create_route() -> Router { + Router::new().route("/", get(list_warnings).post(create_warning)) +} + +#[derive(Serialize)] +struct WarningPagination { + count: i64, + results: Vec, +} + +/// List warnings. A staffer can see everything. +async fn list_warnings( + pagination: Query, + claims: Claims, +) -> Result, AppError> { + let page = pagination.0.page.unwrap_or_default(); + + let user = User::find_by_id(claims.user_id).await?; + + let (results, count) = match user.is_staff.unwrap() { + true => ( + Warning::list(page, None).await?, + Warning::count(None).await?, + ), + false => ( + Warning::list(page, Some(user.id)).await?, + Warning::count(Some(user.id)).await?, + ), + }; + + Ok(Json(WarningPagination { count, results })) +} + +/// Create a warning. Checks Authorization token +async fn create_warning( + Json(payload): Json, + claims: Claims, +) -> Result, 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())), + }; + + let warning = Warning::new(claims.user_id, model.id, payload.note); + + let warning_new = Warning::create(warning).await?; + + Ok(JsonCreate(warning_new)) +} -- cgit v1.2.3-71-g8e6c