Shopping list items can now be checked/unchecked
This commit is contained in:
parent
3a3288bc93
commit
a1fd63ad08
14 changed files with 940 additions and 790 deletions
20
Cargo.lock
generated
20
Cargo.lock
generated
|
|
@ -377,9 +377,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.28"
|
version = "4.5.29"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3e77c3243bd94243c03672cb5154667347c457ca271254724f9f393aee1c05ff"
|
checksum = "8acebd8ad879283633b343856142139f2da2317c96b05b4dd6181c61e2480184"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
|
|
@ -387,9 +387,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.27"
|
version = "4.5.29"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7"
|
checksum = "f6ba32cbda51c7e1dfd49acc1457ba1a7dec5b64fe360e828acb13ca8dc9c2f9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
|
|
@ -1579,9 +1579,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.8.3"
|
version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924"
|
checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"adler2",
|
"adler2",
|
||||||
]
|
]
|
||||||
|
|
@ -2138,9 +2138,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls"
|
name = "rustls"
|
||||||
version = "0.23.22"
|
version = "0.23.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9fb9263ab4eb695e42321db096e3b8fbd715a59b154d5c88d82db2175b681ba7"
|
checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
|
@ -2584,9 +2584,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stacker"
|
name = "stacker"
|
||||||
version = "0.1.17"
|
version = "0.1.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b"
|
checksum = "1d08feb8f695b465baed819b03c128dc23f57a694510ab1f06c77f763975685e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
|
|
||||||
|
|
@ -200,7 +200,7 @@ WHERE [Step].[id] IN ({}) AND ([user_id] = $1 OR (SELECT [is_admin] FROM [User]
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
sqlx::query_scalar(
|
sqlx::query_scalar(
|
||||||
r#"
|
r#"
|
||||||
SELECT COUNT(*)
|
SELECT COUNT(*) = 1
|
||||||
FROM [Recipe]
|
FROM [Recipe]
|
||||||
INNER JOIN [User] ON [User].[id] = [Recipe].[user_id]
|
INNER JOIN [User] ON [User].[id] = [Recipe].[user_id]
|
||||||
INNER JOIN [Group] ON [Group].[recipe_id] = [Recipe].[id]
|
INNER JOIN [Group] ON [Group].[recipe_id] = [Recipe].[id]
|
||||||
|
|
@ -244,6 +244,21 @@ WHERE [Ingredient].[id] IN ({}) AND
|
||||||
Ok(query.fetch_one(&self.pool).await? == ingredients_ids.len() as u64)
|
Ok(query.fetch_one(&self.pool).await? == ingredients_ids.len() as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn can_edit_shopping_list_entry(&self, user_id: i64, entry_id: i64) -> Result<bool> {
|
||||||
|
sqlx::query_scalar(
|
||||||
|
r#"
|
||||||
|
SELECT COUNT(*) = 1
|
||||||
|
FROM [ShoppingEntry]
|
||||||
|
WHERE [id] = $1 AND ([user_id] = $2 OR (SELECT [is_admin] FROM [User] WHERE [id] = $2))
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(entry_id)
|
||||||
|
.bind(user_id)
|
||||||
|
.fetch_one(&self.pool)
|
||||||
|
.await
|
||||||
|
.map_err(DBError::from)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_recipe(&self, id: i64, complete: bool) -> Result<Option<model::Recipe>> {
|
pub async fn get_recipe(&self, id: i64, complete: bool) -> Result<Option<model::Recipe>> {
|
||||||
match sqlx::query_as::<_, model::Recipe>(
|
match sqlx::query_as::<_, model::Recipe>(
|
||||||
r#"
|
r#"
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ FROM [ShoppingEntry]
|
||||||
LEFT JOIN [RecipeScheduled] ON [RecipeScheduled].[id] = [ShoppingEntry].[recipe_scheduled_id]
|
LEFT JOIN [RecipeScheduled] ON [RecipeScheduled].[id] = [ShoppingEntry].[recipe_scheduled_id]
|
||||||
LEFT JOIN [Recipe] ON [Recipe].[id] = [RecipeScheduled].[recipe_id]
|
LEFT JOIN [Recipe] ON [Recipe].[id] = [RecipeScheduled].[recipe_id]
|
||||||
WHERE [ShoppingEntry].[user_id] = $1
|
WHERE [ShoppingEntry].[user_id] = $1
|
||||||
ORDER BY [is_checked], [recipe_id], [name]
|
ORDER BY [is_checked], [recipe_id], [name], [ShoppingEntry].[id]
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(user_id)
|
.bind(user_id)
|
||||||
|
|
@ -35,4 +35,14 @@ ORDER BY [is_checked], [recipe_id], [name]
|
||||||
.await
|
.await
|
||||||
.map_err(DBError::from)
|
.map_err(DBError::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn set_entry_checked(&self, entry_id: i64, is_checked: bool) -> Result<()> {
|
||||||
|
sqlx::query("UPDATE [ShoppingEntry] SET [is_checked] = $2 WHERE [id] = $1")
|
||||||
|
.bind(entry_id)
|
||||||
|
.bind(is_checked)
|
||||||
|
.execute(&self.pool)
|
||||||
|
.await
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(DBError::from)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -107,92 +107,114 @@ async fn main() {
|
||||||
// Disabled: update user profile is now made with a post data ('edit_user_post').
|
// Disabled: update user profile is now made with a post data ('edit_user_post').
|
||||||
// .route("/user/update", put(services::ron::update_user))
|
// .route("/user/update", put(services::ron::update_user))
|
||||||
.route("/set_lang", put(services::ron::set_lang))
|
.route("/set_lang", put(services::ron::set_lang))
|
||||||
.route("/recipe/get_titles", get(services::ron::get_titles))
|
.route("/recipe/get_titles", get(services::ron::recipe::get_titles))
|
||||||
.route("/recipe/set_title", put(services::ron::set_recipe_title))
|
.route("/recipe/set_title", put(services::ron::recipe::set_title))
|
||||||
.route(
|
.route(
|
||||||
"/recipe/set_description",
|
"/recipe/set_description",
|
||||||
put(services::ron::set_recipe_description),
|
put(services::ron::recipe::set_description),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/recipe/set_servings",
|
||||||
|
put(services::ron::recipe::set_servings),
|
||||||
)
|
)
|
||||||
.route("/recipe/set_servings", put(services::ron::set_servings))
|
|
||||||
.route(
|
.route(
|
||||||
"/recipe/set_estimated_time",
|
"/recipe/set_estimated_time",
|
||||||
put(services::ron::set_estimated_time),
|
put(services::ron::recipe::set_estimated_time),
|
||||||
|
)
|
||||||
|
.route("/recipe/get_tags", get(services::ron::recipe::get_tags))
|
||||||
|
.route("/recipe/add_tags", post(services::ron::recipe::add_tags))
|
||||||
|
.route("/recipe/rm_tags", delete(services::ron::recipe::rm_tags))
|
||||||
|
.route(
|
||||||
|
"/recipe/set_difficulty",
|
||||||
|
put(services::ron::recipe::set_difficulty),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/recipe/set_language",
|
||||||
|
put(services::ron::recipe::set_language),
|
||||||
)
|
)
|
||||||
.route("/recipe/get_tags", get(services::ron::get_tags))
|
|
||||||
.route("/recipe/add_tags", post(services::ron::add_tags))
|
|
||||||
.route("/recipe/rm_tags", delete(services::ron::rm_tags))
|
|
||||||
.route("/recipe/set_difficulty", put(services::ron::set_difficulty))
|
|
||||||
.route("/recipe/set_language", put(services::ron::set_language))
|
|
||||||
.route(
|
.route(
|
||||||
"/recipe/set_is_published",
|
"/recipe/set_is_published",
|
||||||
put(services::ron::set_is_published),
|
put(services::ron::recipe::set_is_published),
|
||||||
|
)
|
||||||
|
.route("/recipe/remove", delete(services::ron::recipe::rm))
|
||||||
|
.route("/recipe/get_groups", get(services::ron::recipe::get_groups))
|
||||||
|
.route("/recipe/add_group", post(services::ron::recipe::add_group))
|
||||||
|
.route(
|
||||||
|
"/recipe/remove_group",
|
||||||
|
delete(services::ron::recipe::rm_group),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/recipe/set_group_name",
|
||||||
|
put(services::ron::recipe::set_group_name),
|
||||||
)
|
)
|
||||||
.route("/recipe/remove", delete(services::ron::rm))
|
|
||||||
.route("/recipe/get_groups", get(services::ron::get_groups))
|
|
||||||
.route("/recipe/add_group", post(services::ron::add_group))
|
|
||||||
.route("/recipe/remove_group", delete(services::ron::rm_group))
|
|
||||||
.route("/recipe/set_group_name", put(services::ron::set_group_name))
|
|
||||||
.route(
|
.route(
|
||||||
"/recipe/set_group_comment",
|
"/recipe/set_group_comment",
|
||||||
put(services::ron::set_group_comment),
|
put(services::ron::recipe::set_group_comment),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/recipe/set_groups_order",
|
"/recipe/set_groups_order",
|
||||||
put(services::ron::set_groups_order),
|
put(services::ron::recipe::set_groups_order),
|
||||||
|
)
|
||||||
|
.route("/recipe/add_step", post(services::ron::recipe::add_step))
|
||||||
|
.route(
|
||||||
|
"/recipe/remove_step",
|
||||||
|
delete(services::ron::recipe::rm_step),
|
||||||
)
|
)
|
||||||
.route("/recipe/add_step", post(services::ron::add_step))
|
|
||||||
.route("/recipe/remove_step", delete(services::ron::rm_step))
|
|
||||||
.route(
|
.route(
|
||||||
"/recipe/set_step_action",
|
"/recipe/set_step_action",
|
||||||
put(services::ron::set_step_action),
|
put(services::ron::recipe::set_step_action),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/recipe/set_steps_order",
|
"/recipe/set_steps_order",
|
||||||
put(services::ron::set_steps_order),
|
put(services::ron::recipe::set_steps_order),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/recipe/add_ingredient",
|
"/recipe/add_ingredient",
|
||||||
post(services::ron::add_ingredient),
|
post(services::ron::recipe::add_ingredient),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/recipe/remove_ingredient",
|
"/recipe/remove_ingredient",
|
||||||
delete(services::ron::rm_ingredient),
|
delete(services::ron::recipe::rm_ingredient),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/recipe/set_ingredient_name",
|
"/recipe/set_ingredient_name",
|
||||||
put(services::ron::set_ingredient_name),
|
put(services::ron::recipe::set_ingredient_name),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/recipe/set_ingredient_comment",
|
"/recipe/set_ingredient_comment",
|
||||||
put(services::ron::set_ingredient_comment),
|
put(services::ron::recipe::set_ingredient_comment),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/recipe/set_ingredient_quantity",
|
"/recipe/set_ingredient_quantity",
|
||||||
put(services::ron::set_ingredient_quantity),
|
put(services::ron::recipe::set_ingredient_quantity),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/recipe/set_ingredient_unit",
|
"/recipe/set_ingredient_unit",
|
||||||
put(services::ron::set_ingredient_unit),
|
put(services::ron::recipe::set_ingredient_unit),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/recipe/set_ingredients_order",
|
"/recipe/set_ingredients_order",
|
||||||
put(services::ron::set_ingredients_order),
|
put(services::ron::recipe::set_ingredients_order),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/calendar/get_scheduled_recipes",
|
"/calendar/get_scheduled_recipes",
|
||||||
get(services::ron::get_scheduled_recipes),
|
get(services::ron::calendar::get_scheduled_recipes),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/calendar/schedule_recipe",
|
"/calendar/schedule_recipe",
|
||||||
post(services::ron::schedule_recipe),
|
post(services::ron::calendar::schedule_recipe),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/calendar/remove_scheduled_recipe",
|
"/calendar/remove_scheduled_recipe",
|
||||||
delete(services::ron::rm_scheduled_recipe),
|
delete(services::ron::calendar::rm_scheduled_recipe),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/shopping_list/get_list",
|
"/shopping_list/get_list",
|
||||||
get(services::ron::get_shopping_list),
|
get(services::ron::shopping_list::get),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/shopping_list/set_checked",
|
||||||
|
put(services::ron::shopping_list::set_entry_checked),
|
||||||
)
|
)
|
||||||
.fallback(services::ron::not_found);
|
.fallback(services::ron::not_found);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,744 +0,0 @@
|
||||||
use axum::{
|
|
||||||
debug_handler,
|
|
||||||
extract::{Extension, State},
|
|
||||||
http::{HeaderMap, StatusCode},
|
|
||||||
response::{ErrorResponse, IntoResponse, Response, Result},
|
|
||||||
};
|
|
||||||
use axum_extra::extract::{
|
|
||||||
cookie::{Cookie, CookieJar},
|
|
||||||
Query,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
// use tracing::{event, Level};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
consts,
|
|
||||||
data::{self, db},
|
|
||||||
model,
|
|
||||||
ron_extractor::ExtractRon,
|
|
||||||
ron_utils::{ron_error, ron_response_ok},
|
|
||||||
};
|
|
||||||
|
|
||||||
const NOT_AUTHORIZED_MESSAGE: &str = "Action not authorized";
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct Id {
|
|
||||||
id: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
pub struct Ids {
|
|
||||||
ids: Vec<i64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// #[allow(dead_code)]
|
|
||||||
// #[debug_handler]
|
|
||||||
// pub async fn update_user(
|
|
||||||
// State(connection): State<db::Connection>,
|
|
||||||
// Extension(user): Extension<Option<model::User>>,
|
|
||||||
// ExtractRon(ron): ExtractRon<common::ron_api::UpdateProfile>,
|
|
||||||
// ) -> Result<StatusCode> {
|
|
||||||
// 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<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
headers: HeaderMap,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::SetLang>,
|
|
||||||
) -> 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))
|
|
||||||
}
|
|
||||||
|
|
||||||
/*** Rights ***/
|
|
||||||
|
|
||||||
async fn check_user_rights_recipe(
|
|
||||||
connection: &db::Connection,
|
|
||||||
user: &Option<model::User>,
|
|
||||||
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<model::User>,
|
|
||||||
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_groups(
|
|
||||||
connection: &db::Connection,
|
|
||||||
user: &Option<model::User>,
|
|
||||||
group_ids: &[i64],
|
|
||||||
) -> Result<()> {
|
|
||||||
if user.is_none()
|
|
||||||
|| !connection
|
|
||||||
.can_edit_recipe_all_groups(user.as_ref().unwrap().id, group_ids)
|
|
||||||
.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<model::User>,
|
|
||||||
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_steps(
|
|
||||||
connection: &db::Connection,
|
|
||||||
user: &Option<model::User>,
|
|
||||||
step_ids: &[i64],
|
|
||||||
) -> Result<()> {
|
|
||||||
if user.is_none()
|
|
||||||
|| !connection
|
|
||||||
.can_edit_recipe_all_steps(user.as_ref().unwrap().id, step_ids)
|
|
||||||
.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<model::User>,
|
|
||||||
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(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn check_user_rights_recipe_ingredients(
|
|
||||||
connection: &db::Connection,
|
|
||||||
user: &Option<model::User>,
|
|
||||||
ingredient_ids: &[i64],
|
|
||||||
) -> Result<()> {
|
|
||||||
if user.is_none()
|
|
||||||
|| !connection
|
|
||||||
.can_edit_recipe_all_ingredients(user.as_ref().unwrap().id, ingredient_ids)
|
|
||||||
.await?
|
|
||||||
{
|
|
||||||
Err(ErrorResponse::from(ron_error(
|
|
||||||
StatusCode::UNAUTHORIZED,
|
|
||||||
NOT_AUTHORIZED_MESSAGE,
|
|
||||||
)))
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*** Recipe ***/
|
|
||||||
|
|
||||||
/// Ask recipe titles associated with each given id. The returned titles are in the same order
|
|
||||||
/// as the given ids.
|
|
||||||
#[debug_handler]
|
|
||||||
pub async fn get_titles(
|
|
||||||
State(connection): State<db::Connection>,
|
|
||||||
recipe_ids: Query<Ids>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
Ok(ron_response_ok(common::ron_api::Strings {
|
|
||||||
strs: connection.get_recipe_titles(&recipe_ids.ids).await?,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[debug_handler]
|
|
||||||
pub async fn set_recipe_title(
|
|
||||||
State(connection): State<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::SetRecipeTitle>,
|
|
||||||
) -> Result<StatusCode> {
|
|
||||||
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<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::SetRecipeDescription>,
|
|
||||||
) -> Result<StatusCode> {
|
|
||||||
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<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::SetRecipeServings>,
|
|
||||||
) -> Result<StatusCode> {
|
|
||||||
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<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::SetRecipeEstimatedTime>,
|
|
||||||
) -> Result<StatusCode> {
|
|
||||||
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<db::Connection>,
|
|
||||||
recipe_id: Query<Id>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
Ok(ron_response_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<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::Tags>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
check_user_rights_recipe(&connection, &user, ron.recipe_id).await?;
|
|
||||||
connection
|
|
||||||
.add_recipe_tags(
|
|
||||||
ron.recipe_id,
|
|
||||||
&ron.tags
|
|
||||||
.into_iter()
|
|
||||||
.map(|tag| tag.to_lowercase())
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Ok(StatusCode::OK)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[debug_handler]
|
|
||||||
pub async fn rm_tags(
|
|
||||||
State(connection): State<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::Tags>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
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<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::SetRecipeDifficulty>,
|
|
||||||
) -> Result<StatusCode> {
|
|
||||||
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<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::SetRecipeLanguage>,
|
|
||||||
) -> Result<StatusCode> {
|
|
||||||
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<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::SetIsPublished>,
|
|
||||||
) -> Result<StatusCode> {
|
|
||||||
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<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::Id>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
check_user_rights_recipe(&connection, &user, ron.id).await?;
|
|
||||||
connection.rm_recipe(ron.id).await?;
|
|
||||||
Ok(StatusCode::OK)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<model::Group> 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<model::Step> 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<model::Ingredient> 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<db::Connection>,
|
|
||||||
recipe_id: Query<Id>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
// Here we don't check user rights on purpose.
|
|
||||||
Ok(ron_response_ok(
|
|
||||||
connection
|
|
||||||
.get_groups(recipe_id.id)
|
|
||||||
.await?
|
|
||||||
.into_iter()
|
|
||||||
.map(common::ron_api::Group::from)
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[debug_handler]
|
|
||||||
pub async fn add_group(
|
|
||||||
State(connection): State<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::Id>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
check_user_rights_recipe(&connection, &user, ron.id).await?;
|
|
||||||
let id = connection.add_recipe_group(ron.id).await?;
|
|
||||||
|
|
||||||
Ok(ron_response_ok(common::ron_api::Id { id }))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[debug_handler]
|
|
||||||
pub async fn rm_group(
|
|
||||||
State(connection): State<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::Id>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
check_user_rights_recipe_group(&connection, &user, ron.id).await?;
|
|
||||||
connection.rm_recipe_group(ron.id).await?;
|
|
||||||
Ok(StatusCode::OK)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[debug_handler]
|
|
||||||
pub async fn set_group_name(
|
|
||||||
State(connection): State<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::SetGroupName>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
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<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::SetGroupComment>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
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 set_groups_order(
|
|
||||||
State(connection): State<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::Ids>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
check_user_rights_recipe_groups(&connection, &user, &ron.ids).await?;
|
|
||||||
connection.set_groups_order(&ron.ids).await?;
|
|
||||||
Ok(StatusCode::OK)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[debug_handler]
|
|
||||||
pub async fn add_step(
|
|
||||||
State(connection): State<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::Id>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
check_user_rights_recipe_group(&connection, &user, ron.id).await?;
|
|
||||||
let id = connection.add_recipe_step(ron.id).await?;
|
|
||||||
|
|
||||||
Ok(ron_response_ok(common::ron_api::Id { id }))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[debug_handler]
|
|
||||||
pub async fn rm_step(
|
|
||||||
State(connection): State<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::Id>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
check_user_rights_recipe_step(&connection, &user, ron.id).await?;
|
|
||||||
connection.rm_recipe_step(ron.id).await?;
|
|
||||||
Ok(StatusCode::OK)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[debug_handler]
|
|
||||||
pub async fn set_step_action(
|
|
||||||
State(connection): State<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::SetStepAction>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
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 set_steps_order(
|
|
||||||
State(connection): State<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::Ids>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
check_user_rights_recipe_steps(&connection, &user, &ron.ids).await?;
|
|
||||||
connection.set_steps_order(&ron.ids).await?;
|
|
||||||
Ok(StatusCode::OK)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[debug_handler]
|
|
||||||
pub async fn add_ingredient(
|
|
||||||
State(connection): State<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::Id>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
check_user_rights_recipe_step(&connection, &user, ron.id).await?;
|
|
||||||
let id = connection.add_recipe_ingredient(ron.id).await?;
|
|
||||||
Ok(ron_response_ok(common::ron_api::Id { id }))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[debug_handler]
|
|
||||||
pub async fn rm_ingredient(
|
|
||||||
State(connection): State<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::Id>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
check_user_rights_recipe_ingredient(&connection, &user, ron.id).await?;
|
|
||||||
connection.rm_recipe_ingredient(ron.id).await?;
|
|
||||||
Ok(StatusCode::OK)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[debug_handler]
|
|
||||||
pub async fn set_ingredient_name(
|
|
||||||
State(connection): State<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::SetIngredientName>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
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<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::SetIngredientComment>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
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<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::SetIngredientQuantity>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
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<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::SetIngredientUnit>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
check_user_rights_recipe_ingredient(&connection, &user, ron.ingredient_id).await?;
|
|
||||||
connection
|
|
||||||
.set_ingredient_unit(ron.ingredient_id, &ron.unit)
|
|
||||||
.await?;
|
|
||||||
Ok(StatusCode::OK)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[debug_handler]
|
|
||||||
pub async fn set_ingredients_order(
|
|
||||||
State(connection): State<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::Ids>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
check_user_rights_recipe_ingredients(&connection, &user, &ron.ids).await?;
|
|
||||||
connection.set_ingredients_order(&ron.ids).await?;
|
|
||||||
Ok(StatusCode::OK)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*** Calendar ***/
|
|
||||||
|
|
||||||
#[debug_handler]
|
|
||||||
pub async fn get_scheduled_recipes(
|
|
||||||
State(connection): State<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
date_range: Query<common::ron_api::DateRange>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
if let Some(user) = user {
|
|
||||||
Ok(ron_response_ok(common::ron_api::ScheduledRecipes {
|
|
||||||
recipes: connection
|
|
||||||
.get_scheduled_recipes(user.id, date_range.start_date, date_range.end_date)
|
|
||||||
.await?,
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
Err(ErrorResponse::from(ron_error(
|
|
||||||
StatusCode::UNAUTHORIZED,
|
|
||||||
NOT_AUTHORIZED_MESSAGE,
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<data::db::recipe::AddScheduledRecipeResult> for common::ron_api::ScheduleRecipeResult {
|
|
||||||
fn from(db_res: data::db::recipe::AddScheduledRecipeResult) -> Self {
|
|
||||||
match db_res {
|
|
||||||
db::recipe::AddScheduledRecipeResult::Ok => Self::Ok,
|
|
||||||
db::recipe::AddScheduledRecipeResult::RecipeAlreadyScheduledAtThisDate => {
|
|
||||||
Self::RecipeAlreadyScheduledAtThisDate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[debug_handler]
|
|
||||||
pub async fn schedule_recipe(
|
|
||||||
State(connection): State<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::ScheduleRecipe>,
|
|
||||||
) -> Result<Response> {
|
|
||||||
check_user_rights_recipe(&connection, &user, ron.recipe_id).await?;
|
|
||||||
if let Some(user) = user {
|
|
||||||
connection
|
|
||||||
.add_scheduled_recipe(
|
|
||||||
user.id,
|
|
||||||
ron.recipe_id,
|
|
||||||
ron.date,
|
|
||||||
ron.servings,
|
|
||||||
ron.add_ingredients_to_shopping_list,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map(|res| {
|
|
||||||
ron_response_ok(common::ron_api::ScheduleRecipeResult::from(res)).into_response()
|
|
||||||
})
|
|
||||||
.map_err(ErrorResponse::from)
|
|
||||||
} else {
|
|
||||||
Ok(StatusCode::OK.into_response())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[debug_handler]
|
|
||||||
pub async fn rm_scheduled_recipe(
|
|
||||||
State(connection): State<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::RemoveScheduledRecipe>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
check_user_rights_recipe(&connection, &user, ron.recipe_id).await?;
|
|
||||||
if let Some(user) = user {
|
|
||||||
connection
|
|
||||||
.rm_scheduled_recipe(
|
|
||||||
user.id,
|
|
||||||
ron.recipe_id,
|
|
||||||
ron.date,
|
|
||||||
ron.remove_ingredients_from_shopping_list,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
Ok(StatusCode::OK)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*** Shopping list ***/
|
|
||||||
|
|
||||||
impl From<model::ShoppingListItem> for common::ron_api::ShoppingListItem {
|
|
||||||
fn from(item: model::ShoppingListItem) -> Self {
|
|
||||||
Self {
|
|
||||||
id: item.id,
|
|
||||||
name: item.name,
|
|
||||||
quantity_value: item.quantity_value,
|
|
||||||
quantity_unit: item.quantity_unit,
|
|
||||||
recipe_id: item.recipe_id,
|
|
||||||
recipe_title: item.recipe_title,
|
|
||||||
date: item.date,
|
|
||||||
is_checked: item.is_checked,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[debug_handler]
|
|
||||||
pub async fn get_shopping_list(
|
|
||||||
State(connection): State<db::Connection>,
|
|
||||||
Extension(user): Extension<Option<model::User>>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
if let Some(user) = user {
|
|
||||||
Ok(ron_response_ok(
|
|
||||||
connection
|
|
||||||
.get_shopping_list(user.id)
|
|
||||||
.await?
|
|
||||||
.into_iter()
|
|
||||||
.map(common::ron_api::ShoppingListItem::from)
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Err(ErrorResponse::from(ron_error(
|
|
||||||
StatusCode::UNAUTHORIZED,
|
|
||||||
NOT_AUTHORIZED_MESSAGE,
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*** 404 ***/
|
|
||||||
|
|
||||||
#[debug_handler]
|
|
||||||
pub async fn not_found(Extension(_user): Extension<Option<model::User>>) -> impl IntoResponse {
|
|
||||||
ron_error(StatusCode::NOT_FOUND, "Not found")
|
|
||||||
}
|
|
||||||
94
backend/src/services/ron/calendar.rs
Normal file
94
backend/src/services/ron/calendar.rs
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
use axum::{
|
||||||
|
debug_handler,
|
||||||
|
extract::{Extension, State},
|
||||||
|
http::StatusCode,
|
||||||
|
response::{ErrorResponse, IntoResponse, Response, Result},
|
||||||
|
};
|
||||||
|
use axum_extra::extract::Query;
|
||||||
|
// use tracing::{event, Level};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
data::{self, db},
|
||||||
|
model,
|
||||||
|
ron_extractor::ExtractRon,
|
||||||
|
ron_utils::{ron_error, ron_response_ok},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::rights::*;
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn get_scheduled_recipes(
|
||||||
|
State(connection): State<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
date_range: Query<common::ron_api::DateRange>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
if let Some(user) = user {
|
||||||
|
Ok(ron_response_ok(common::ron_api::ScheduledRecipes {
|
||||||
|
recipes: connection
|
||||||
|
.get_scheduled_recipes(user.id, date_range.start_date, date_range.end_date)
|
||||||
|
.await?,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
Err(ErrorResponse::from(ron_error(
|
||||||
|
StatusCode::UNAUTHORIZED,
|
||||||
|
super::NOT_AUTHORIZED_MESSAGE,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<data::db::recipe::AddScheduledRecipeResult> for common::ron_api::ScheduleRecipeResult {
|
||||||
|
fn from(db_res: data::db::recipe::AddScheduledRecipeResult) -> Self {
|
||||||
|
match db_res {
|
||||||
|
db::recipe::AddScheduledRecipeResult::Ok => Self::Ok,
|
||||||
|
db::recipe::AddScheduledRecipeResult::RecipeAlreadyScheduledAtThisDate => {
|
||||||
|
Self::RecipeAlreadyScheduledAtThisDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn schedule_recipe(
|
||||||
|
State(connection): State<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<common::ron_api::ScheduleRecipe>,
|
||||||
|
) -> Result<Response> {
|
||||||
|
check_user_rights_recipe(&connection, &user, ron.recipe_id).await?;
|
||||||
|
if let Some(user) = user {
|
||||||
|
connection
|
||||||
|
.add_scheduled_recipe(
|
||||||
|
user.id,
|
||||||
|
ron.recipe_id,
|
||||||
|
ron.date,
|
||||||
|
ron.servings,
|
||||||
|
ron.add_ingredients_to_shopping_list,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map(|res| {
|
||||||
|
ron_response_ok(common::ron_api::ScheduleRecipeResult::from(res)).into_response()
|
||||||
|
})
|
||||||
|
.map_err(ErrorResponse::from)
|
||||||
|
} else {
|
||||||
|
Ok(StatusCode::OK.into_response())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn rm_scheduled_recipe(
|
||||||
|
State(connection): State<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<common::ron_api::RemoveScheduledRecipe>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
check_user_rights_recipe(&connection, &user, ron.recipe_id).await?;
|
||||||
|
if let Some(user) = user {
|
||||||
|
connection
|
||||||
|
.rm_scheduled_recipe(
|
||||||
|
user.id,
|
||||||
|
ron.recipe_id,
|
||||||
|
ron.date,
|
||||||
|
ron.remove_ingredients_from_shopping_list,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
Ok(StatusCode::OK)
|
||||||
|
}
|
||||||
41
backend/src/services/ron/mod.rs
Normal file
41
backend/src/services/ron/mod.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
use axum::{
|
||||||
|
debug_handler,
|
||||||
|
extract::{Extension, State},
|
||||||
|
http::{HeaderMap, StatusCode},
|
||||||
|
response::{IntoResponse, Result},
|
||||||
|
};
|
||||||
|
use axum_extra::extract::cookie::{Cookie, CookieJar};
|
||||||
|
// use tracing::{event, Level};
|
||||||
|
|
||||||
|
use crate::{consts, data::db, model, ron_extractor::ExtractRon, ron_utils::ron_error};
|
||||||
|
|
||||||
|
pub mod calendar;
|
||||||
|
pub mod recipe;
|
||||||
|
mod rights;
|
||||||
|
pub mod shopping_list;
|
||||||
|
|
||||||
|
const NOT_AUTHORIZED_MESSAGE: &str = "Action not authorized";
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn set_lang(
|
||||||
|
State(connection): State<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
headers: HeaderMap,
|
||||||
|
ExtractRon(ron): ExtractRon<common::ron_api::SetLang>,
|
||||||
|
) -> 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))
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** 404 ***/
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn not_found(Extension(_user): Extension<Option<model::User>>) -> impl IntoResponse {
|
||||||
|
ron_error(StatusCode::NOT_FOUND, "Not found")
|
||||||
|
}
|
||||||
417
backend/src/services/ron/recipe.rs
Normal file
417
backend/src/services/ron/recipe.rs
Normal file
|
|
@ -0,0 +1,417 @@
|
||||||
|
use axum::{
|
||||||
|
debug_handler,
|
||||||
|
extract::{Extension, State},
|
||||||
|
http::StatusCode,
|
||||||
|
response::{IntoResponse, Result},
|
||||||
|
};
|
||||||
|
use axum_extra::extract::Query;
|
||||||
|
use common::ron_api;
|
||||||
|
// use tracing::{event, Level};
|
||||||
|
|
||||||
|
use crate::{data::db, model, ron_extractor::ExtractRon, ron_utils::ron_response_ok};
|
||||||
|
|
||||||
|
use super::rights::*;
|
||||||
|
|
||||||
|
/// Ask recipe titles associated with each given id. The returned titles are in the same order
|
||||||
|
/// as the given ids.
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn get_titles(
|
||||||
|
State(connection): State<db::Connection>,
|
||||||
|
recipe_ids: Query<ron_api::Ids>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
Ok(ron_response_ok(ron_api::Strings {
|
||||||
|
strs: connection.get_recipe_titles(&recipe_ids.ids).await?,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn set_title(
|
||||||
|
State(connection): State<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::SetRecipeTitle>,
|
||||||
|
) -> Result<StatusCode> {
|
||||||
|
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_description(
|
||||||
|
State(connection): State<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::SetRecipeDescription>,
|
||||||
|
) -> Result<StatusCode> {
|
||||||
|
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<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::SetRecipeServings>,
|
||||||
|
) -> Result<StatusCode> {
|
||||||
|
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<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::SetRecipeEstimatedTime>,
|
||||||
|
) -> Result<StatusCode> {
|
||||||
|
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<db::Connection>,
|
||||||
|
recipe_id: Query<ron_api::Id>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
Ok(ron_response_ok(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<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::Tags>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
check_user_rights_recipe(&connection, &user, ron.recipe_id).await?;
|
||||||
|
connection
|
||||||
|
.add_recipe_tags(
|
||||||
|
ron.recipe_id,
|
||||||
|
&ron.tags
|
||||||
|
.into_iter()
|
||||||
|
.map(|tag| tag.to_lowercase())
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(StatusCode::OK)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn rm_tags(
|
||||||
|
State(connection): State<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::Tags>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
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<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::SetRecipeDifficulty>,
|
||||||
|
) -> Result<StatusCode> {
|
||||||
|
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<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::SetRecipeLanguage>,
|
||||||
|
) -> Result<StatusCode> {
|
||||||
|
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<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::SetIsPublished>,
|
||||||
|
) -> Result<StatusCode> {
|
||||||
|
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<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::Id>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
check_user_rights_recipe(&connection, &user, ron.id).await?;
|
||||||
|
connection.rm_recipe(ron.id).await?;
|
||||||
|
Ok(StatusCode::OK)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<model::Group> for 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(ron_api::Step::from).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<model::Step> for ron_api::Step {
|
||||||
|
fn from(step: model::Step) -> Self {
|
||||||
|
Self {
|
||||||
|
id: step.id,
|
||||||
|
action: step.action,
|
||||||
|
ingredients: step
|
||||||
|
.ingredients
|
||||||
|
.into_iter()
|
||||||
|
.map(ron_api::Ingredient::from)
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<model::Ingredient> for 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<db::Connection>,
|
||||||
|
recipe_id: Query<ron_api::Id>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
// Here we don't check user rights on purpose.
|
||||||
|
Ok(ron_response_ok(
|
||||||
|
connection
|
||||||
|
.get_groups(recipe_id.id)
|
||||||
|
.await?
|
||||||
|
.into_iter()
|
||||||
|
.map(ron_api::Group::from)
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn add_group(
|
||||||
|
State(connection): State<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::Id>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
check_user_rights_recipe(&connection, &user, ron.id).await?;
|
||||||
|
let id = connection.add_recipe_group(ron.id).await?;
|
||||||
|
|
||||||
|
Ok(ron_response_ok(ron_api::Id { id }))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn rm_group(
|
||||||
|
State(connection): State<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::Id>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
check_user_rights_recipe_group(&connection, &user, ron.id).await?;
|
||||||
|
connection.rm_recipe_group(ron.id).await?;
|
||||||
|
Ok(StatusCode::OK)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn set_group_name(
|
||||||
|
State(connection): State<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::SetGroupName>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
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<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::SetGroupComment>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
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 set_groups_order(
|
||||||
|
State(connection): State<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::Ids>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
check_user_rights_recipe_groups(&connection, &user, &ron.ids).await?;
|
||||||
|
connection.set_groups_order(&ron.ids).await?;
|
||||||
|
Ok(StatusCode::OK)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn add_step(
|
||||||
|
State(connection): State<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::Id>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
check_user_rights_recipe_group(&connection, &user, ron.id).await?;
|
||||||
|
let id = connection.add_recipe_step(ron.id).await?;
|
||||||
|
|
||||||
|
Ok(ron_response_ok(ron_api::Id { id }))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn rm_step(
|
||||||
|
State(connection): State<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::Id>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
check_user_rights_recipe_step(&connection, &user, ron.id).await?;
|
||||||
|
connection.rm_recipe_step(ron.id).await?;
|
||||||
|
Ok(StatusCode::OK)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn set_step_action(
|
||||||
|
State(connection): State<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::SetStepAction>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
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 set_steps_order(
|
||||||
|
State(connection): State<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::Ids>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
check_user_rights_recipe_steps(&connection, &user, &ron.ids).await?;
|
||||||
|
connection.set_steps_order(&ron.ids).await?;
|
||||||
|
Ok(StatusCode::OK)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn add_ingredient(
|
||||||
|
State(connection): State<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::Id>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
check_user_rights_recipe_step(&connection, &user, ron.id).await?;
|
||||||
|
let id = connection.add_recipe_ingredient(ron.id).await?;
|
||||||
|
Ok(ron_response_ok(ron_api::Id { id }))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn rm_ingredient(
|
||||||
|
State(connection): State<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::Id>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
check_user_rights_recipe_ingredient(&connection, &user, ron.id).await?;
|
||||||
|
connection.rm_recipe_ingredient(ron.id).await?;
|
||||||
|
Ok(StatusCode::OK)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn set_ingredient_name(
|
||||||
|
State(connection): State<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::SetIngredientName>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
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<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::SetIngredientComment>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
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<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::SetIngredientQuantity>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
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<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::SetIngredientUnit>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
check_user_rights_recipe_ingredient(&connection, &user, ron.ingredient_id).await?;
|
||||||
|
connection
|
||||||
|
.set_ingredient_unit(ron.ingredient_id, &ron.unit)
|
||||||
|
.await?;
|
||||||
|
Ok(StatusCode::OK)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn set_ingredients_order(
|
||||||
|
State(connection): State<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::Ids>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
check_user_rights_recipe_ingredients(&connection, &user, &ron.ids).await?;
|
||||||
|
connection.set_ingredients_order(&ron.ids).await?;
|
||||||
|
Ok(StatusCode::OK)
|
||||||
|
}
|
||||||
158
backend/src/services/ron/rights.rs
Normal file
158
backend/src/services/ron/rights.rs
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
use axum::{
|
||||||
|
http::StatusCode,
|
||||||
|
response::{ErrorResponse, Result},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{data::db, model, ron_utils::ron_error};
|
||||||
|
|
||||||
|
pub async fn check_user_rights_recipe(
|
||||||
|
connection: &db::Connection,
|
||||||
|
user: &Option<model::User>,
|
||||||
|
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,
|
||||||
|
super::NOT_AUTHORIZED_MESSAGE,
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn check_user_rights_recipe_group(
|
||||||
|
connection: &db::Connection,
|
||||||
|
user: &Option<model::User>,
|
||||||
|
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,
|
||||||
|
super::NOT_AUTHORIZED_MESSAGE,
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn check_user_rights_recipe_groups(
|
||||||
|
connection: &db::Connection,
|
||||||
|
user: &Option<model::User>,
|
||||||
|
group_ids: &[i64],
|
||||||
|
) -> Result<()> {
|
||||||
|
if user.is_none()
|
||||||
|
|| !connection
|
||||||
|
.can_edit_recipe_all_groups(user.as_ref().unwrap().id, group_ids)
|
||||||
|
.await?
|
||||||
|
{
|
||||||
|
Err(ErrorResponse::from(ron_error(
|
||||||
|
StatusCode::UNAUTHORIZED,
|
||||||
|
super::NOT_AUTHORIZED_MESSAGE,
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn check_user_rights_recipe_step(
|
||||||
|
connection: &db::Connection,
|
||||||
|
user: &Option<model::User>,
|
||||||
|
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,
|
||||||
|
super::NOT_AUTHORIZED_MESSAGE,
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn check_user_rights_recipe_steps(
|
||||||
|
connection: &db::Connection,
|
||||||
|
user: &Option<model::User>,
|
||||||
|
step_ids: &[i64],
|
||||||
|
) -> Result<()> {
|
||||||
|
if user.is_none()
|
||||||
|
|| !connection
|
||||||
|
.can_edit_recipe_all_steps(user.as_ref().unwrap().id, step_ids)
|
||||||
|
.await?
|
||||||
|
{
|
||||||
|
Err(ErrorResponse::from(ron_error(
|
||||||
|
StatusCode::UNAUTHORIZED,
|
||||||
|
super::NOT_AUTHORIZED_MESSAGE,
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn check_user_rights_recipe_ingredient(
|
||||||
|
connection: &db::Connection,
|
||||||
|
user: &Option<model::User>,
|
||||||
|
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,
|
||||||
|
super::NOT_AUTHORIZED_MESSAGE,
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn check_user_rights_recipe_ingredients(
|
||||||
|
connection: &db::Connection,
|
||||||
|
user: &Option<model::User>,
|
||||||
|
ingredient_ids: &[i64],
|
||||||
|
) -> Result<()> {
|
||||||
|
if user.is_none()
|
||||||
|
|| !connection
|
||||||
|
.can_edit_recipe_all_ingredients(user.as_ref().unwrap().id, ingredient_ids)
|
||||||
|
.await?
|
||||||
|
{
|
||||||
|
Err(ErrorResponse::from(ron_error(
|
||||||
|
StatusCode::UNAUTHORIZED,
|
||||||
|
super::NOT_AUTHORIZED_MESSAGE,
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn check_user_rights_shopping_list_entry(
|
||||||
|
connection: &db::Connection,
|
||||||
|
user: &Option<model::User>,
|
||||||
|
entry_id: i64,
|
||||||
|
) -> Result<()> {
|
||||||
|
if user.is_none()
|
||||||
|
|| !connection
|
||||||
|
.can_edit_shopping_list_entry(user.as_ref().unwrap().id, entry_id)
|
||||||
|
.await?
|
||||||
|
{
|
||||||
|
Err(ErrorResponse::from(ron_error(
|
||||||
|
StatusCode::UNAUTHORIZED,
|
||||||
|
super::NOT_AUTHORIZED_MESSAGE,
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
65
backend/src/services/ron/shopping_list.rs
Normal file
65
backend/src/services/ron/shopping_list.rs
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
use axum::{
|
||||||
|
debug_handler,
|
||||||
|
extract::{Extension, State},
|
||||||
|
http::StatusCode,
|
||||||
|
response::{ErrorResponse, IntoResponse, Result},
|
||||||
|
};
|
||||||
|
use common::ron_api;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
data::db,
|
||||||
|
model,
|
||||||
|
ron_extractor::ExtractRon,
|
||||||
|
ron_utils::{ron_error, ron_response_ok},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::rights::*;
|
||||||
|
|
||||||
|
impl From<model::ShoppingListItem> for common::ron_api::ShoppingListItem {
|
||||||
|
fn from(item: model::ShoppingListItem) -> Self {
|
||||||
|
Self {
|
||||||
|
id: item.id,
|
||||||
|
name: item.name,
|
||||||
|
quantity_value: item.quantity_value,
|
||||||
|
quantity_unit: item.quantity_unit,
|
||||||
|
recipe_id: item.recipe_id,
|
||||||
|
recipe_title: item.recipe_title,
|
||||||
|
date: item.date,
|
||||||
|
is_checked: item.is_checked,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn get(
|
||||||
|
State(connection): State<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
if let Some(user) = user {
|
||||||
|
Ok(ron_response_ok(
|
||||||
|
connection
|
||||||
|
.get_shopping_list(user.id)
|
||||||
|
.await?
|
||||||
|
.into_iter()
|
||||||
|
.map(common::ron_api::ShoppingListItem::from)
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Err(ErrorResponse::from(ron_error(
|
||||||
|
StatusCode::UNAUTHORIZED,
|
||||||
|
super::NOT_AUTHORIZED_MESSAGE,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn set_entry_checked(
|
||||||
|
State(connection): State<db::Connection>,
|
||||||
|
Extension(user): Extension<Option<model::User>>,
|
||||||
|
ExtractRon(ron): ExtractRon<ron_api::Value<bool>>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
check_user_rights_shopping_list_entry(&connection, &user, ron.id).await?;
|
||||||
|
Ok(ron_response_ok(
|
||||||
|
connection.set_entry_checked(ron.id, ron.value).await?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,9 @@
|
||||||
|
|
||||||
<div id="shopping-list">
|
<div id="shopping-list">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="shopping-list-checked">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="hidden-templates">
|
<div id="hidden-templates">
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,13 @@ pub struct Id {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A value associated with an id.
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub struct Value<T> {
|
||||||
|
pub id: i64,
|
||||||
|
pub value: T,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
pub struct Strings {
|
pub struct Strings {
|
||||||
pub strs: Vec<String>,
|
pub strs: Vec<String>,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use std::str::FromStr;
|
||||||
|
|
||||||
use chrono::Locale;
|
use chrono::Locale;
|
||||||
use common::{ron_api, utils::substitute_with_names};
|
use common::{ron_api, utils::substitute_with_names};
|
||||||
|
use futures::TryFutureExt;
|
||||||
use gloo::events::EventListener;
|
use gloo::events::EventListener;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
use wasm_bindgen_futures::spawn_local;
|
use wasm_bindgen_futures::spawn_local;
|
||||||
|
|
@ -33,10 +34,14 @@ pub fn setup_page(is_user_logged: bool) -> Result<(), JsValue> {
|
||||||
spawn_local(async move {
|
spawn_local(async move {
|
||||||
let item_template: Element = selector("#hidden-templates .shopping-item");
|
let item_template: Element = selector("#hidden-templates .shopping-item");
|
||||||
let container: Element = by_id("shopping-list");
|
let container: Element = by_id("shopping-list");
|
||||||
|
let container_checked: Element = by_id("shopping-list-checked");
|
||||||
let date_format =
|
let date_format =
|
||||||
selector::<Element>("#hidden-templates .calendar-date-format").inner_html();
|
selector::<Element>("#hidden-templates .calendar-date-format").inner_html();
|
||||||
for item in shopping_list.get_items().await.unwrap() {
|
for item in shopping_list.get_items().await.unwrap() {
|
||||||
let item_element = item_template.deep_clone();
|
let item_element = item_template.deep_clone();
|
||||||
|
|
||||||
|
// item_element.set_id(format!("shopping-item-{}", ));
|
||||||
|
|
||||||
item_element
|
item_element
|
||||||
.selector::<Element>(".item-name")
|
.selector::<Element>(".item-name")
|
||||||
.set_inner_html(&item.name);
|
.set_inner_html(&item.name);
|
||||||
|
|
@ -62,7 +67,48 @@ pub fn setup_page(is_user_logged: bool) -> Result<(), JsValue> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
container.append_child(&item_element).unwrap();
|
EventListener::new(
|
||||||
|
&item_element.selector(".item-is-checked"),
|
||||||
|
"change",
|
||||||
|
move |event| {
|
||||||
|
let input: HtmlInputElement = event.target().unwrap().dyn_into().unwrap();
|
||||||
|
spawn_local(async move {
|
||||||
|
shopping_list
|
||||||
|
.set_item_checked(item.id, input.checked())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let item_element = input.parent_element().unwrap();
|
||||||
|
item_element.remove();
|
||||||
|
// TODO: Find the correct place to insert the element.
|
||||||
|
if input.checked() {
|
||||||
|
by_id::<Element>("shopping-list-checked")
|
||||||
|
.append_child(&item_element)
|
||||||
|
.unwrap();
|
||||||
|
} else {
|
||||||
|
by_id::<Element>("shopping-list")
|
||||||
|
.append_child(&item_element)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.forget();
|
||||||
|
|
||||||
|
EventListener::new(&item_element, "click", move |event| {
|
||||||
|
let target: Element = event.target().unwrap().dyn_into().unwrap();
|
||||||
|
|
||||||
|
// if target.class_name() == "item-is-checked"
|
||||||
|
})
|
||||||
|
.forget();
|
||||||
|
|
||||||
|
if item.is_checked {
|
||||||
|
item_element
|
||||||
|
.selector::<HtmlInputElement>(".item-is-checked")
|
||||||
|
.set_checked(true);
|
||||||
|
container_checked.append_child(&item_element).unwrap();
|
||||||
|
} else {
|
||||||
|
container.append_child(&item_element).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,4 +32,20 @@ impl ShoppingList {
|
||||||
Ok(request::get("shopping_list/get_list", ()).await?)
|
Ok(request::get("shopping_list/get_list", ()).await?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn set_item_checked(&self, item_id: i64, is_checked: bool) -> Result<()> {
|
||||||
|
if self.is_local {
|
||||||
|
todo!();
|
||||||
|
} else {
|
||||||
|
request::put(
|
||||||
|
"shopping_list/set_checked",
|
||||||
|
ron_api::Value {
|
||||||
|
id: item_id,
|
||||||
|
value: is_checked,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(Error::from)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue