diff options
| author | Santo Cariotti <santo@dcariotti.me> | 2022-10-17 20:08:09 +0000 |
|---|---|---|
| committer | Santo Cariotti <santo@dcariotti.me> | 2022-10-17 20:08:09 +0000 |
| commit | 74976dab57887a4d7e29b426cdf7422722fa58ee (patch) | |
| tree | 5f1bbed3dbcf3ba520866cb4eb060aaf5a771915 /src/routes | |
| parent | 611293122213f83e82d851cd8dc83fd1e4f79dcd (diff) | |
Refactoring of mods
Diffstat (limited to 'src/routes')
| -rw-r--r-- | src/routes/auth.rs | 68 | ||||
| -rw-r--r-- | src/routes/mod.rs | 28 | ||||
| -rw-r--r-- | src/routes/model.rs | 267 | ||||
| -rw-r--r-- | src/routes/user.rs | 210 | ||||
| -rw-r--r-- | src/routes/warning.rs | 177 |
5 files changed, 0 insertions, 750 deletions
diff --git a/src/routes/auth.rs b/src/routes/auth.rs deleted file mode 100644 index 0c459f5..0000000 --- a/src/routes/auth.rs +++ /dev/null @@ -1,68 +0,0 @@ -use crate::{ - errors::AppError, - models::{ - auth::{AuthBody, Claims, LoginCredentials, SignUpForm}, - user::User, - }, - routes::JsonCreate, -}; -use axum::{routing::post, Json, Router}; - -/// Create routes for `/v1/auth/` namespace -pub fn create_route() -> Router { - Router::new() - .route("/login", post(make_login)) - .route("/signup", post(signup)) -} - -/// Make login. Check if a user with the email and password passed in request body exists into the -/// database -async fn make_login(Json(payload): Json<LoginCredentials>) -> Result<Json<AuthBody>, AppError> { - let user = User::new( - String::new(), - String::new(), - payload.username, - payload.password, - ); - match User::find(user).await { - Ok(user) => { - let claims = Claims::new(user.id); - let token = claims.get_token()?; - Ok(Json(AuthBody::new(token))) - } - Err(_) => Err(AppError::NotFound("User not found".to_string())), - } -} - -/// Create a new user -async fn signup(Json(payload): Json<SignUpForm>) -> Result<JsonCreate<AuthBody>, AppError> { - if payload.password1 != payload.password2 { - return Err(AppError::BadRequest( - "The inserted passwords do not match".to_string(), - )); - } - - if User::email_has_taken(&payload.email).await? { - return Err(AppError::BadRequest( - "An user with this email already exists".to_string(), - )); - } - - if User::username_has_taken(&payload.username).await? { - return Err(AppError::BadRequest( - "An user with this username already exists".to_string(), - )); - } - - let user = User::new( - payload.name, - payload.email, - payload.username, - payload.password1, - ); - let user = User::create(user).await?; - - let claims = Claims::new(user.id); - let token = claims.get_token()?; - Ok(JsonCreate(AuthBody::new(token))) -} diff --git a/src/routes/mod.rs b/src/routes/mod.rs deleted file mode 100644 index a0e8031..0000000 --- a/src/routes/mod.rs +++ /dev/null @@ -1,28 +0,0 @@ -pub mod auth; -pub mod model; -pub mod user; -pub mod warning; - -use crate::errors::AppError; -use axum::{ - http::StatusCode, - response::{IntoResponse, Response}, - Json, -}; -use serde::Serialize; - -pub async fn page_404() -> impl IntoResponse { - AppError::NotFound("Route not found".to_string()) -} - -/// Extension of `Json` which returns the CREATED status code -pub struct JsonCreate<T>(pub T); - -impl<T> IntoResponse for JsonCreate<T> -where - T: Serialize, -{ - fn into_response(self) -> Response { - (StatusCode::CREATED, Json(self.0)).into_response() - } -} diff --git a/src/routes/model.rs b/src/routes/model.rs deleted file mode 100644 index 58ec732..0000000 --- a/src/routes/model.rs +++ /dev/null @@ -1,267 +0,0 @@ -use crate::{ - errors::AppError, - files::{delete_upload, upload}, - models::{ - auth::Claims, - likes::Like, - model::{Model, ModelCreate, ModelFilter, ModelUpload, ModelUser}, - user::User, - }, - pagination::{ModelPagination, Pagination}, - routes::JsonCreate, -}; -use axum::{ - extract::{ContentLengthLimit, Multipart, Path, Query}, - http::StatusCode, - routing::{delete, get, post}, - Json, Router, -}; - -/// Create routes for `/v1/models/` namespace -pub fn create_route() -> Router { - Router::new() - .route("/", get(list_models).post(create_model)) - .route("/filter", post(filter_models)) - .route("/:id", get(get_model).delete(delete_model).put(edit_model)) - .route("/:id/like", post(add_like).delete(delete_like)) - .route("/:id/upload", post(upload_model_file)) - .route("/:id/upload/:uid", delete(delete_model_file)) -} - -/// List models. -async fn list_models(pagination: Query<Pagination>) -> Result<Json<ModelPagination>, AppError> { - let page = pagination.0.page.unwrap_or_default(); - let results = Model::list(page).await?; - let count = Model::count().await?; - - Ok(Json(ModelPagination { count, results })) -} - -/// Create a model. Checks Authorization token -async fn create_model( - Json(payload): Json<ModelCreate>, - claims: Claims, -) -> Result<JsonCreate<Model>, AppError> { - let model = Model::new( - payload.name, - payload.description, - payload.duration, - payload.height, - payload.weight, - payload.printer, - payload.material, - claims.user_id, - ); - - let model_new = Model::create(model).await?; - - Ok(JsonCreate(model_new)) -} - -/// Get a model with id = `model_id` -async fn get_model(Path(model_id): Path<i32>) -> Result<Json<ModelUser>, AppError> { - match Model::find_by_id(model_id).await { - Ok(model) => Ok(Json(model)), - Err(_) => Err(AppError::NotFound("Model not found".to_string())), - } -} - -/// The owner or a staffer can delete a model -async fn delete_model(claims: Claims, Path(model_id): Path<i32>) -> Result<StatusCode, AppError> { - 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<String> = model.list_upload_filepaths().await.unwrap_or_default(); - - if !(model.author_id() == user.id || 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) -} - -/// The owner or a staffer can edit a model -async fn edit_model( - Json(payload): Json<ModelCreate>, - claims: Claims, - Path(model_id): Path<i32>, -) -> Result<Json<ModelUser>, AppError> { - 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?; - - if !(model.author_id() == user.id || user.is_staff.unwrap()) { - return Err(AppError::Unauthorized); - } - - let model_body = Model::new( - payload.name, - payload.description, - payload.duration, - payload.height, - payload.weight, - payload.printer, - payload.material, - claims.user_id, - ); - - // NOTE: can we edit this as same as `user.edit_avatar()`? - Model::edit(model.id, model_body).await?; - Ok(Json(model)) -} - -/// Upload a file for a model -async fn upload_model_file( - claims: Claims, - Path(model_id): Path<i32>, - ContentLengthLimit(multipart): ContentLengthLimit<Multipart, { 1024 * 1024 * 40 }>, -) -> Result<Json<ModelUpload>, AppError> { - 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?; - - if !(model.author_id() == user.id || user.is_staff.unwrap()) { - return Err(AppError::Unauthorized); - } - - let allowed_extensions = vec![ - "stl", - "obj", - "png", - "jpg", - "jpeg", - "gif", - "webp", - "blend", - "octet-stream", - "sla", - ]; - - match upload(multipart, allowed_extensions, None).await { - Ok(saved_file) => { - let model_file = ModelUpload::create(ModelUpload::new(saved_file, model_id)).await?; - - Ok(Json(model_file)) - } - Err(e) => Err(e), - } -} - -/// The owner or a staffer can delete a model upload -async fn delete_model_file( - claims: Claims, - Path((model_id, upload_id)): Path<(i32, i32)>, -) -> Result<StatusCode, AppError> { - 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?; - - if !(model.author_id() == user.id || user.is_staff.unwrap()) { - return Err(AppError::Unauthorized); - } - - let upload = match ModelUpload::find_by_id(upload_id).await { - Ok(upload) => upload, - Err(_) => { - return Err(AppError::NotFound("Upload not found".to_string())); - } - }; - - if upload.model_id != model.id { - return Err(AppError::NotFound("Upload not found".to_string())); - } - - let filepath = upload.filepath.clone(); - - match ModelUpload::delete(upload_id).await { - Ok(_) => { - delete_upload(&filepath)?; - - Ok(StatusCode::NO_CONTENT) - } - Err(e) => Err(e), - } -} - -/// Assign a like to a model from the Authorization user -async fn add_like(claims: Claims, Path(model_id): Path<i32>) -> Result<StatusCode, AppError> { - 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 like = Like::new(user.id, model.id); - - match like.save().await { - Ok(_) => Ok(StatusCode::CREATED), - Err(e) => { - return Err(e); - } - } -} - -/// Remove a like from a model and an Authorization user -async fn delete_like(claims: Claims, Path(model_id): Path<i32>) -> Result<StatusCode, AppError> { - 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 like = Like::new(user.id, model.id); - - match like.remove().await { - Ok(_) => Ok(StatusCode::NO_CONTENT), - Err(e) => { - return Err(e); - } - } -} - -/// Filter models -async fn filter_models( - pagination: Query<Pagination>, - Json(payload): Json<ModelFilter>, -) -> Result<Json<ModelPagination>, AppError> { - let page = pagination.0.page.unwrap_or_default(); - - let results = Model::filter(page, payload.q.clone()).await?; - let count = Model::count_filter(payload.q).await?; - - Ok(Json(ModelPagination { count, results })) -} diff --git a/src/routes/user.rs b/src/routes/user.rs deleted file mode 100644 index 31366a0..0000000 --- a/src/routes/user.rs +++ /dev/null @@ -1,210 +0,0 @@ -use crate::{ - errors::AppError, - files::{delete_upload, upload}, - models::{ - auth::Claims, - user::{User, UserEdit, UserList}, - }, - pagination::{ModelPagination, Pagination, UserPagination}, -}; -use axum::{ - extract::{ContentLengthLimit, Multipart, Path, Query}, - routing::{delete, get, put}, - Json, Router, -}; - -/// Create routes for `/v1/users/` namespace -pub fn create_route() -> Router { - Router::new() - .route("/", get(list_users)) - .route("/me", get(get_me)) - .route("/me/avatar", put(edit_my_avatar).delete(delete_my_avatar)) - .route("/:id", get(get_user).put(edit_user)) - .route("/:id/avatar", delete(delete_avatar)) - .route("/:id/models", get(get_user_models)) -} - -/// List users. Checks Authorization token -async fn list_users( - _: Claims, - pagination: Query<Pagination>, -) -> Result<Json<UserPagination>, AppError> { - let page = pagination.0.page.unwrap_or_default(); - let results = User::list(page).await?; - let count = User::count().await?; - - Ok(Json(UserPagination { count, results })) -} - -/// Get info about me -async fn get_me(claims: Claims) -> Result<Json<UserList>, AppError> { - match User::find_by_id(claims.user_id).await { - Ok(user) => Ok(Json(user)), - Err(_) => Err(AppError::NotFound("User not found".to_string())), - } -} - -/// Edit the avatar of the user linked to the claims -async fn edit_my_avatar( - claims: Claims, - ContentLengthLimit(multipart): ContentLengthLimit<Multipart, { 1024 * 1024 * 5 }>, -) -> Result<Json<UserList>, AppError> { - let mut user = match User::find_by_id(claims.user_id).await { - Ok(user) => user, - Err(_) => { - return Err(AppError::NotFound("User not found".to_string())); - } - }; - - if user.avatar.is_some() { - let avatar_url = user.avatar.as_ref().unwrap(); - delete_upload(avatar_url)?; - } - - match upload( - multipart, - vec!["jpg", "jpeg", "png", "webp"], - Some(format!("avatar-{}", user.id)), - ) - .await - { - Ok(saved_file) => { - user.edit_avatar(Some(saved_file)).await?; - - Ok(Json(user)) - } - Err(e) => Err(e), - } -} - -/// A staffer can delete an user `id`'s avatar -async fn delete_avatar( - Path(user_id): Path<i32>, - claims: Claims, -) -> Result<Json<UserList>, AppError> { - let mut user = match User::find_by_id(user_id).await { - Ok(user) => user, - Err(_) => { - return Err(AppError::NotFound("User not found".to_string())); - } - }; - - // If the user of the access token is different than the user they want to edit, checks if the - // first user is an admin - if claims.user_id != user.id { - match User::find_by_id(claims.user_id).await { - Ok(user) => { - if !(user.is_staff.unwrap()) { - return Err(AppError::Unauthorized); - } - } - Err(_) => { - return Err(AppError::NotFound("User not found".to_string())); - } - }; - } - - if user.avatar.is_some() { - let avatar_url = user.avatar.as_ref().unwrap(); - delete_upload(avatar_url)?; - } - - user.edit_avatar(None).await?; - - Ok(Json(user)) -} - -/// Delete the avatar of the user linked to the claims -async fn delete_my_avatar(claims: Claims) -> Result<Json<UserList>, AppError> { - let mut user = match User::find_by_id(claims.user_id).await { - Ok(user) => user, - Err(_) => { - return Err(AppError::NotFound("User not found".to_string())); - } - }; - - if user.avatar.is_some() { - let avatar_url = user.avatar.as_ref().unwrap(); - delete_upload(avatar_url)?; - } - - user.edit_avatar(None).await?; - - Ok(Json(user)) -} - -/// Get an user with id = `user_id` -async fn get_user(Path(user_id): Path<i32>) -> Result<Json<UserList>, AppError> { - match User::find_by_id(user_id).await { - Ok(user) => Ok(Json(user)), - Err(_) => Err(AppError::NotFound("User not found".to_string())), - } -} - -/// Edit an user with id = `user_id`. Only staffers and owner of that account can perform this -/// action. -/// Only staffers can update the user `is_staff` value -async fn edit_user( - Path(user_id): Path<i32>, - Json(mut payload): Json<UserEdit>, - claims: Claims, -) -> Result<Json<UserList>, AppError> { - let mut user = match User::find_by_id(user_id).await { - Ok(user) => user, - Err(_) => { - return Err(AppError::NotFound("User not found".to_string())); - } - }; - - let claimed = match User::find_by_id(claims.user_id).await { - Ok(user) => user, - Err(_) => { - return Err(AppError::NotFound("User not found".to_string())); - } - }; - - if user.id != claimed.id { - if !(claimed.is_staff.unwrap()) { - return Err(AppError::Unauthorized); - } - } - - if !claimed.is_staff.unwrap() && user.is_staff != payload.is_staff { - payload.is_staff = user.is_staff; - } - - if user.email != payload.email && User::email_has_taken(&payload.email).await? { - return Err(AppError::BadRequest( - "An user with this email already exists".to_string(), - )); - } - - if user.username != payload.username && User::username_has_taken(&payload.username).await? { - return Err(AppError::BadRequest( - "An user with this username already exists".to_string(), - )); - } - - user.edit(payload).await?; - - Ok(Json(user)) -} - -/// Get user models list -async fn get_user_models( - Path(user_id): Path<i32>, - pagination: Query<Pagination>, -) -> Result<Json<ModelPagination>, AppError> { - let user = match User::find_by_id(user_id).await { - Ok(user) => user, - Err(_) => { - return Err(AppError::NotFound("User not found".to_string())); - } - }; - - let page = pagination.0.page.unwrap_or_default(); - let results = user.get_models(page).await?; - let count = user.count_models().await?; - - Ok(Json(ModelPagination { count, results })) -} diff --git a/src/routes/warning.rs b/src/routes/warning.rs deleted file mode 100644 index 384218a..0000000 --- a/src/routes/warning.rs +++ /dev/null @@ -1,177 +0,0 @@ -use crate::{ - errors::AppError, - models::{ - auth::Claims, - model::Model, - user::User, - warning::{Warning, WarningCreate, WarningEdit, WarningFilter, WarningFilterPayload}, - }, - pagination::{Pagination, WarningPagination}, - routes::JsonCreate, -}; -use axum::{ - extract::{Path, Query}, - http::StatusCode, - routing::{get, post}, - Json, Router, -}; - -/// Create routes for `/v1/warnings/` namespace -pub fn create_route() -> Router { - Router::new() - .route("/", get(list_warnings).post(create_warning)) - .route( - "/:id", - get(get_warning).put(edit_warning).delete(delete_warning), - ) - .route("/filter", post(filter_warnings)) -} - -/// List warnings. A staffer can see everything. -async fn list_warnings( - pagination: Query<Pagination>, - claims: Claims, -) -> Result<Json<WarningPagination>, 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 })) -} - -/// Get a warning with id = `model_id` -async fn get_warning( - Path(warning_id): Path<i32>, - claims: Claims, -) -> Result<Json<Warning>, AppError> { - let user = User::find_by_id(claims.user_id).await?; - - if !(user.is_staff.unwrap()) { - return Err(AppError::Unauthorized); - } - - match Warning::find_by_id(warning_id).await { - Ok(warning) => Ok(Json(warning.into())), - Err(_) => Err(AppError::NotFound("Warning not found".to_string())), - } -} - -/// Create a warning. Checks Authorization token -async fn create_warning( - Json(payload): Json<WarningCreate>, - claims: Claims, -) -> Result<JsonCreate<Warning>, AppError> { - let model = match Model::find_by_id(payload.model_id).await { - Ok(model) => model, - Err(_) => return Err(AppError::NotFound("Report 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)) -} - -/// 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)) -} - -/// A staffer can delete a warning -async fn delete_warning( - claims: Claims, - Path(warning_id): Path<i32>, -) -> Result<StatusCode, AppError> { - let user = User::find_by_id(claims.user_id).await?; - - if !user.is_staff.unwrap() { - return Err(AppError::Unauthorized); - } - - if Warning::delete(warning_id).await.is_ok() { - Ok(StatusCode::NO_CONTENT) - } else { - Ok(StatusCode::BAD_REQUEST) - } -} - -/// Apply a filter to warnings list -async fn filter_warnings( - Json(payload): Json<WarningFilterPayload>, - pagination: Query<Pagination>, - claims: Claims, -) -> Result<Json<WarningPagination>, 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::filter( - page, - WarningFilter { - model_id: payload.model_id, - resolved_by: payload.resolved_by, - user_id: None, - }, - ) - .await?, - Warning::count_by_model_id(WarningFilter { - model_id: payload.model_id, - resolved_by: payload.resolved_by, - user_id: None, - }) - .await?, - ), - false => ( - Warning::filter( - page, - WarningFilter { - model_id: payload.model_id, - resolved_by: payload.resolved_by, - user_id: Some(user.id), - }, - ) - .await?, - Warning::count_by_model_id(WarningFilter { - model_id: payload.model_id, - resolved_by: payload.resolved_by, - user_id: Some(user.id), - }) - .await?, - ), - }; - - Ok(Json(WarningPagination { count, results })) -} |
