use axum::{ debug_handler, extract::{Extension, Query, State}, http::{HeaderMap, StatusCode}, response::{ErrorResponse, IntoResponse, Result}, }; use axum_extra::extract::cookie::{Cookie, CookieJar}; use serde::Deserialize; // use tracing::{event, Level}; use crate::{ consts, data::db, model, ron_extractor::ExtractRon, ron_utils::{ron_error, ron_response}, }; const NOT_AUTHORIZED_MESSAGE: &str = "Action not authorized"; #[derive(Deserialize)] pub struct RecipeId { #[serde(rename = "recipe_id")] id: i64, } // #[allow(dead_code)] // #[debug_handler] // pub async fn update_user( // State(connection): State, // Extension(user): Extension>, // ExtractRon(ron): ExtractRon, // ) -> Result { // if let Some(user) = user { // connection // .update_user( // user.id, // ron.email.as_deref().map(str::trim), // ron.name.as_deref(), // ron.password.as_deref(), // ) // .await?; // } else { // return Err(ErrorResponse::from(ron_error( // StatusCode::UNAUTHORIZED, // NOT_AUTHORIZED_MESSAGE, // ))); // } // Ok(StatusCode::OK) // } #[debug_handler] pub async fn set_lang( State(connection): State, Extension(user): Extension>, headers: HeaderMap, ExtractRon(ron): ExtractRon, ) -> Result<(CookieJar, StatusCode)> { let mut jar = CookieJar::from_headers(&headers); if let Some(user) = user { connection.set_user_lang(user.id, &ron.lang).await?; } else { let cookie = Cookie::build((consts::COOKIE_LANG_NAME, ron.lang)).path("/"); jar = jar.add(cookie); } Ok((jar, StatusCode::OK)) } async fn check_user_rights_recipe( connection: &db::Connection, user: &Option, recipe_id: i64, ) -> Result<()> { if user.is_none() || !connection .can_edit_recipe(user.as_ref().unwrap().id, recipe_id) .await? { Err(ErrorResponse::from(ron_error( StatusCode::UNAUTHORIZED, NOT_AUTHORIZED_MESSAGE, ))) } else { Ok(()) } } async fn check_user_rights_recipe_group( connection: &db::Connection, user: &Option, group_id: i64, ) -> Result<()> { if user.is_none() || !connection .can_edit_recipe_group(user.as_ref().unwrap().id, group_id) .await? { Err(ErrorResponse::from(ron_error( StatusCode::UNAUTHORIZED, NOT_AUTHORIZED_MESSAGE, ))) } else { Ok(()) } } async fn check_user_rights_recipe_step( connection: &db::Connection, user: &Option, step_id: i64, ) -> Result<()> { if user.is_none() || !connection .can_edit_recipe_step(user.as_ref().unwrap().id, step_id) .await? { Err(ErrorResponse::from(ron_error( StatusCode::UNAUTHORIZED, NOT_AUTHORIZED_MESSAGE, ))) } else { Ok(()) } } async fn check_user_rights_recipe_ingredient( connection: &db::Connection, user: &Option, ingredient_id: i64, ) -> Result<()> { if user.is_none() || !connection .can_edit_recipe_ingredient(user.as_ref().unwrap().id, ingredient_id) .await? { Err(ErrorResponse::from(ron_error( StatusCode::UNAUTHORIZED, NOT_AUTHORIZED_MESSAGE, ))) } else { Ok(()) } } #[debug_handler] pub async fn set_recipe_title( State(connection): State, Extension(user): Extension>, ExtractRon(ron): ExtractRon, ) -> Result { check_user_rights_recipe(&connection, &user, ron.recipe_id).await?; connection .set_recipe_title(ron.recipe_id, &ron.title) .await?; Ok(StatusCode::OK) } #[debug_handler] pub async fn set_recipe_description( State(connection): State, Extension(user): Extension>, ExtractRon(ron): ExtractRon, ) -> Result { check_user_rights_recipe(&connection, &user, ron.recipe_id).await?; connection .set_recipe_description(ron.recipe_id, &ron.description) .await?; Ok(StatusCode::OK) } #[debug_handler] pub async fn set_servings( State(connection): State, Extension(user): Extension>, ExtractRon(ron): ExtractRon, ) -> Result { check_user_rights_recipe(&connection, &user, ron.recipe_id).await?; connection .set_recipe_servings(ron.recipe_id, ron.servings) .await?; Ok(StatusCode::OK) } #[debug_handler] pub async fn set_estimated_time( State(connection): State, Extension(user): Extension>, ExtractRon(ron): ExtractRon, ) -> Result { check_user_rights_recipe(&connection, &user, ron.recipe_id).await?; connection .set_recipe_estimated_time(ron.recipe_id, ron.estimated_time) .await?; Ok(StatusCode::OK) } #[debug_handler] pub async fn get_tags( State(connection): State, recipe_id: Query, ) -> Result { Ok(ron_response( StatusCode::OK, common::ron_api::Tags { recipe_id: recipe_id.id, tags: connection.get_recipes_tags(recipe_id.id).await?, }, )) } #[debug_handler] pub async fn add_tags( State(connection): State, Extension(user): Extension>, ExtractRon(ron): ExtractRon, ) -> Result { check_user_rights_recipe(&connection, &user, ron.recipe_id).await?; connection.add_recipe_tags(ron.recipe_id, &ron.tags).await?; Ok(StatusCode::OK) } #[debug_handler] pub async fn rm_tags( State(connection): State, Extension(user): Extension>, ExtractRon(ron): ExtractRon, ) -> Result { check_user_rights_recipe(&connection, &user, ron.recipe_id).await?; connection.rm_recipe_tags(ron.recipe_id, &ron.tags).await?; Ok(StatusCode::OK) } #[debug_handler] pub async fn set_difficulty( State(connection): State, Extension(user): Extension>, ExtractRon(ron): ExtractRon, ) -> Result { check_user_rights_recipe(&connection, &user, ron.recipe_id).await?; connection .set_recipe_difficulty(ron.recipe_id, ron.difficulty) .await?; Ok(StatusCode::OK) } #[debug_handler] pub async fn set_language( State(connection): State, Extension(user): Extension>, ExtractRon(ron): ExtractRon, ) -> Result { if !crate::translation::available_codes() .iter() .any(|&l| l == ron.lang) { // TODO: log? return Ok(StatusCode::BAD_REQUEST); } check_user_rights_recipe(&connection, &user, ron.recipe_id).await?; connection .set_recipe_language(ron.recipe_id, &ron.lang) .await?; Ok(StatusCode::OK) } #[debug_handler] pub async fn set_is_published( State(connection): State, Extension(user): Extension>, ExtractRon(ron): ExtractRon, ) -> Result { check_user_rights_recipe(&connection, &user, ron.recipe_id).await?; connection .set_recipe_is_published(ron.recipe_id, ron.is_published) .await?; Ok(StatusCode::OK) } #[debug_handler] pub async fn rm( State(connection): State, Extension(user): Extension>, ExtractRon(ron): ExtractRon, ) -> Result { check_user_rights_recipe(&connection, &user, ron.recipe_id).await?; connection.rm_recipe(ron.recipe_id).await?; Ok(StatusCode::OK) } impl From for common::ron_api::Group { fn from(group: model::Group) -> Self { Self { id: group.id, name: group.name, comment: group.comment, steps: group .steps .into_iter() .map(common::ron_api::Step::from) .collect(), } } } impl From for common::ron_api::Step { fn from(step: model::Step) -> Self { Self { id: step.id, action: step.action, ingredients: step .ingredients .into_iter() .map(common::ron_api::Ingredient::from) .collect(), } } } impl From for common::ron_api::Ingredient { fn from(ingredient: model::Ingredient) -> Self { Self { id: ingredient.id, name: ingredient.name, comment: ingredient.comment, quantity_value: ingredient.quantity_value, quantity_unit: ingredient.quantity_unit, } } } #[debug_handler] pub async fn get_groups( State(connection): State, recipe_id: Query, ) -> Result { // Here we don't check user rights on purpose. Ok(ron_response( StatusCode::OK, connection .get_groups(recipe_id.id) .await? .into_iter() .map(common::ron_api::Group::from) .collect::>(), )) } #[debug_handler] pub async fn add_group( State(connection): State, Extension(user): Extension>, ExtractRon(ron): ExtractRon, ) -> Result { check_user_rights_recipe(&connection, &user, ron.recipe_id).await?; let group_id = connection.add_recipe_group(ron.recipe_id).await?; Ok(ron_response( StatusCode::OK, common::ron_api::AddRecipeGroupResult { group_id }, )) } #[debug_handler] pub async fn rm_group( State(connection): State, Extension(user): Extension>, ExtractRon(ron): ExtractRon, ) -> Result { check_user_rights_recipe_group(&connection, &user, ron.group_id).await?; connection.rm_recipe_group(ron.group_id).await?; Ok(StatusCode::OK) } #[debug_handler] pub async fn set_group_name( State(connection): State, Extension(user): Extension>, ExtractRon(ron): ExtractRon, ) -> Result { check_user_rights_recipe_group(&connection, &user, ron.group_id).await?; connection.set_group_name(ron.group_id, &ron.name).await?; Ok(StatusCode::OK) } #[debug_handler] pub async fn set_group_comment( State(connection): State, Extension(user): Extension>, ExtractRon(ron): ExtractRon, ) -> Result { check_user_rights_recipe_group(&connection, &user, ron.group_id).await?; connection .set_group_comment(ron.group_id, &ron.comment) .await?; Ok(StatusCode::OK) } #[debug_handler] pub async fn add_step( State(connection): State, Extension(user): Extension>, ExtractRon(ron): ExtractRon, ) -> Result { check_user_rights_recipe_group(&connection, &user, ron.group_id).await?; let step_id = connection.add_recipe_step(ron.group_id).await?; Ok(ron_response( StatusCode::OK, common::ron_api::AddRecipeStepResult { step_id }, )) } #[debug_handler] pub async fn rm_step( State(connection): State, Extension(user): Extension>, ExtractRon(ron): ExtractRon, ) -> Result { check_user_rights_recipe_step(&connection, &user, ron.step_id).await?; connection.rm_recipe_step(ron.step_id).await?; Ok(StatusCode::OK) } #[debug_handler] pub async fn set_step_action( State(connection): State, Extension(user): Extension>, ExtractRon(ron): ExtractRon, ) -> Result { check_user_rights_recipe_step(&connection, &user, ron.step_id).await?; connection.set_step_action(ron.step_id, &ron.action).await?; Ok(StatusCode::OK) } #[debug_handler] pub async fn add_ingredient( State(connection): State, Extension(user): Extension>, ExtractRon(ron): ExtractRon, ) -> Result { check_user_rights_recipe_step(&connection, &user, ron.step_id).await?; let ingredient_id = connection.add_recipe_ingredient(ron.step_id).await?; Ok(ron_response( StatusCode::OK, common::ron_api::AddRecipeIngredientResult { ingredient_id }, )) } #[debug_handler] pub async fn rm_ingredient( State(connection): State, Extension(user): Extension>, ExtractRon(ron): ExtractRon, ) -> Result { check_user_rights_recipe_ingredient(&connection, &user, ron.ingredient_id).await?; connection.rm_recipe_ingredient(ron.ingredient_id).await?; Ok(StatusCode::OK) } #[debug_handler] pub async fn set_ingredient_name( State(connection): State, Extension(user): Extension>, ExtractRon(ron): ExtractRon, ) -> Result { check_user_rights_recipe_ingredient(&connection, &user, ron.ingredient_id).await?; connection .set_ingredient_name(ron.ingredient_id, &ron.name) .await?; Ok(StatusCode::OK) } #[debug_handler] pub async fn set_ingredient_comment( State(connection): State, Extension(user): Extension>, ExtractRon(ron): ExtractRon, ) -> Result { check_user_rights_recipe_ingredient(&connection, &user, ron.ingredient_id).await?; connection .set_ingredient_comment(ron.ingredient_id, &ron.comment) .await?; Ok(StatusCode::OK) } #[debug_handler] pub async fn set_ingredient_quantity( State(connection): State, Extension(user): Extension>, ExtractRon(ron): ExtractRon, ) -> Result { check_user_rights_recipe_ingredient(&connection, &user, ron.ingredient_id).await?; connection .set_ingredient_quantity(ron.ingredient_id, ron.quantity) .await?; Ok(StatusCode::OK) } #[debug_handler] pub async fn set_ingredient_unit( State(connection): State, Extension(user): Extension>, ExtractRon(ron): ExtractRon, ) -> Result { check_user_rights_recipe_ingredient(&connection, &user, ron.ingredient_id).await?; connection .set_ingredient_unit(ron.ingredient_id, &ron.unit) .await?; Ok(StatusCode::OK) } ///// 404 ///// #[debug_handler] pub async fn not_found(Extension(_user): Extension>) -> impl IntoResponse { ron_error(StatusCode::NOT_FOUND, "Not found") }