From 03ebbb74fa251da59739a62fac30433968e11318 Mon Sep 17 00:00:00 2001 From: Greg Burri Date: Mon, 6 Jan 2025 22:52:22 +0100 Subject: [PATCH] Set user language to new recipe + cleaning --- backend/src/consts.rs | 9 +--- backend/src/data/db.rs | 3 -- backend/src/data/db/recipe.rs | 78 ++++++++++++++++-------------- backend/src/html_templates.rs | 2 - backend/src/services.rs | 5 +- backend/src/services/fragments.rs | 6 ++- backend/src/services/recipe.rs | 15 +++--- backend/src/services/ron.rs | 8 +++ backend/templates/recipe_edit.html | 8 +-- frontend/src/handles.rs | 1 - frontend/src/lib.rs | 22 +-------- frontend/src/on_click.rs | 2 +- 12 files changed, 73 insertions(+), 86 deletions(-) diff --git a/backend/src/consts.rs b/backend/src/consts.rs index 222499a..bbe1cdd 100644 --- a/backend/src/consts.rs +++ b/backend/src/consts.rs @@ -1,4 +1,4 @@ -use std::{sync::LazyLock, time::Duration}; +use std::time::Duration; pub const FILE_CONF: &str = "conf.ron"; pub const TRANSLATION_FILE: &str = "translation.ron"; @@ -23,10 +23,3 @@ pub const SEND_EMAIL_TIMEOUT: Duration = Duration::from_secs(60); pub const REVERSE_PROXY_IP_HTTP_FIELD: &str = "x-real-ip"; // Set by the reverse proxy (Nginx). pub const MAX_DB_CONNECTION: u32 = 1; // To avoid database lock. - -// TODO: remove, should be replaced by the translation module. -pub static LANGUAGES: LazyLock<[(&str, &str); 2]> = LazyLock::new(|| { - let mut langs = [("Français", "fr"), ("English", "en")]; - langs.sort(); - langs -}); diff --git a/backend/src/data/db.rs b/backend/src/data/db.rs index ab30bb8..bf58047 100644 --- a/backend/src/data/db.rs +++ b/backend/src/data/db.rs @@ -32,9 +32,6 @@ pub enum DBError { )] UnsupportedVersion(u32), - #[error("Unknown language: {0}")] - UnknownLanguage(String), - #[error("Unknown error: {0}")] Other(String), } diff --git a/backend/src/data/db/recipe.rs b/backend/src/data/db/recipe.rs index 28f714e..48a94f9 100644 --- a/backend/src/data/db/recipe.rs +++ b/backend/src/data/db/recipe.rs @@ -1,18 +1,38 @@ use super::{Connection, DBError, Result}; -use crate::{consts, data::model}; +use crate::data::model; use common::ron_api::Difficulty; impl Connection { - pub async fn get_all_published_recipe_titles(&self) -> Result> { - sqlx::query_as( - r#" -SELECT [id], [title] -FROM [Recipe] -WHERE [is_published] = true -ORDER BY [title] - "#, - ) + /// Returns all the recipe titles where recipe is written in the given language. + /// If a user_id is given, the language constraint is ignored for recipes owned by user_id. + pub async fn get_all_published_recipe_titles( + &self, + lang: &str, + user_id: Option, + ) -> Result> { + if let Some(user_id) = user_id { + sqlx::query_as( + r#" + SELECT [id], [title] + FROM [Recipe] + WHERE [is_published] = true AND ([lang] = $1 OR [user_id] = $2) + ORDER BY [title] + "#, + ) + .bind(lang) + .bind(user_id) + } else { + sqlx::query_as( + r#" + SELECT [id], [title] + FROM [Recipe] + WHERE [is_published] = true AND [lang] = $1 + ORDER BY [title] + "#, + ) + .bind(lang) + } .fetch_all(&self.pool) .await .map_err(DBError::from) @@ -147,11 +167,18 @@ WHERE [Recipe].[user_id] = $1 { Some(recipe_id) => Ok(recipe_id), None => { - let db_result = - sqlx::query("INSERT INTO [Recipe] ([user_id], [title]) VALUES ($1, '')") - .bind(user_id) - .execute(&mut *tx) - .await?; + let lang: String = sqlx::query_scalar("SELECT [lang] FROM [User] WHERE [id] = $1") + .bind(user_id) + .fetch_one(&mut *tx) + .await?; + + let db_result = sqlx::query( + "INSERT INTO [Recipe] ([user_id], [lang], [title]) VALUES ($1, $2, '')", + ) + .bind(user_id) + .bind(lang) + .execute(&mut *tx) + .await?; tx.commit().await?; Ok(db_result.last_insert_rowid()) @@ -345,9 +372,6 @@ WHERE [id] = $1 AND [id] NOT IN ( } pub async fn set_recipe_language(&self, recipe_id: i64, lang: &str) -> Result<()> { - if !consts::LANGUAGES.iter().any(|(_, l)| *l == lang) { - return Err(DBError::UnknownLanguage(lang.to_string())); - } sqlx::query("UPDATE [Recipe] SET [lang] = $2 WHERE [id] = $1") .bind(recipe_id) .bind(lang) @@ -598,24 +622,6 @@ mod tests { Ok(()) } - #[tokio::test] - async fn set_nonexistent_language() -> Result<()> { - let connection = Connection::new_in_memory().await?; - - let user_id = create_a_user(&connection).await?; - let recipe_id = connection.create_recipe(user_id).await?; - - match connection.set_recipe_language(recipe_id, "asdf").await { - // Nominal case. - Err(DBError::UnknownLanguage(message)) => { - println!("Ok: {}", message); - } - other => panic!("Set an nonexistent language must fail: {:?}", other), - } - - Ok(()) - } - async fn create_a_user(connection: &Connection) -> Result { let user_id = 1; connection.execute_sql( diff --git a/backend/src/html_templates.rs b/backend/src/html_templates.rs index 25b0587..ab8ca0a 100644 --- a/backend/src/html_templates.rs +++ b/backend/src/html_templates.rs @@ -133,13 +133,11 @@ pub struct RecipeEditTemplate { pub recipes: Recipes, pub recipe: model::Recipe, - pub languages: [(&'static str, &'static str); 2], } #[derive(Template)] #[template(path = "recipes_list_fragment.html")] pub struct RecipesListFragmentTemplate { - pub user: Option, pub tr: Tr, pub recipes: Recipes, diff --git a/backend/src/services.rs b/backend/src/services.rs index 9fcfc1a..1a18e16 100644 --- a/backend/src/services.rs +++ b/backend/src/services.rs @@ -5,7 +5,6 @@ use axum::{ middleware::Next, response::{IntoResponse, Response, Result}, }; -// use tracing::{event, Level}; use crate::{ data::{db, model}, @@ -54,7 +53,9 @@ pub async fn home_page( Extension(tr): Extension, ) -> Result { let recipes = Recipes { - published: connection.get_all_published_recipe_titles().await?, + published: connection + .get_all_published_recipe_titles(tr.current_lang_code(), user.as_ref().map(|u| u.id)) + .await?, unpublished: if let Some(user) = user.as_ref() { connection .get_all_unpublished_recipe_titles(user.id) diff --git a/backend/src/services/fragments.rs b/backend/src/services/fragments.rs index 47e9e93..1a50a5f 100644 --- a/backend/src/services/fragments.rs +++ b/backend/src/services/fragments.rs @@ -25,7 +25,9 @@ pub async fn recipes_list_fragments( Extension(tr): Extension, ) -> Result { let recipes = Recipes { - published: connection.get_all_published_recipe_titles().await?, + published: connection + .get_all_published_recipe_titles(tr.current_lang_code(), user.as_ref().map(|u| u.id)) + .await?, unpublished: if let Some(user) = user.as_ref() { connection .get_all_unpublished_recipe_titles(user.id) @@ -35,5 +37,5 @@ pub async fn recipes_list_fragments( }, current_id: current_recipe.current_recipe_id, }; - Ok(RecipesListFragmentTemplate { user, tr, recipes }) + Ok(RecipesListFragmentTemplate { tr, recipes }) } diff --git a/backend/src/services/recipe.rs b/backend/src/services/recipe.rs index 6d1e0cd..9887b2d 100644 --- a/backend/src/services/recipe.rs +++ b/backend/src/services/recipe.rs @@ -6,7 +6,6 @@ use axum::{ // use tracing::{event, Level}; use crate::{ - consts, data::{db, model}, html_templates::*, translation::{self, Sentence}, @@ -37,21 +36,20 @@ pub async fn edit_recipe( if let Some(recipe) = connection.get_recipe(recipe_id, false).await? { if recipe.user_id == user.id { let recipes = Recipes { - published: connection.get_all_published_recipe_titles().await?, + published: connection + .get_all_published_recipe_titles(tr.current_lang_code(), Some(user.id)) + .await?, unpublished: connection .get_all_unpublished_recipe_titles(user.id) .await?, current_id: Some(recipe_id), }; - dbg!(&recipe); - Ok(RecipeEditTemplate { user: Some(user), tr, recipes, recipe, - languages: *consts::LANGUAGES, } .into_response()) } else { @@ -89,7 +87,12 @@ pub async fn view( } let recipes = Recipes { - published: connection.get_all_published_recipe_titles().await?, + published: connection + .get_all_published_recipe_titles( + tr.current_lang_code(), + user.as_ref().map(|u| u.id), + ) + .await?, unpublished: if let Some(user) = user.as_ref() { connection .get_all_unpublished_recipe_titles(user.id) diff --git a/backend/src/services/ron.rs b/backend/src/services/ron.rs index d7c52bc..0d31103 100644 --- a/backend/src/services/ron.rs +++ b/backend/src/services/ron.rs @@ -249,6 +249,14 @@ pub async fn set_language( Extension(user): Extension>, ExtractRon(ron): ExtractRon, ) -> Result { + if !crate::translation::Tr::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) diff --git a/backend/templates/recipe_edit.html b/backend/templates/recipe_edit.html index be5a91c..7e2b677 100644 --- a/backend/templates/recipe_edit.html +++ b/backend/templates/recipe_edit.html @@ -59,12 +59,12 @@ diff --git a/frontend/src/handles.rs b/frontend/src/handles.rs index d523042..56b69ac 100644 --- a/frontend/src/handles.rs +++ b/frontend/src/handles.rs @@ -1,5 +1,4 @@ use gloo::{ - console::log, events::EventListener, net::http::Request, utils::{document, window}, diff --git a/frontend/src/lib.rs b/frontend/src/lib.rs index 06a8bf7..93c98b9 100644 --- a/frontend/src/lib.rs +++ b/frontend/src/lib.rs @@ -5,11 +5,7 @@ mod request; mod toast; mod utils; -use gloo::{ - console::log, - events::EventListener, - utils::{document, window}, -}; +use gloo::{events::EventListener, utils::window}; use utils::by_id; use wasm_bindgen::prelude::*; use wasm_bindgen_futures::spawn_local; @@ -17,24 +13,10 @@ use web_sys::HtmlSelectElement; use common::ron_api; -// #[wasm_bindgen] -// extern "C" { -// fn alert(s: &str); -// } - -// #[wasm_bindgen] -// pub fn greet(name: &str) { -// alert(&format!("Hello, {}!", name)); -// console::log_1(&"Hello bg".into()); -// } - #[wasm_bindgen(start)] pub fn main() -> Result<(), JsValue> { console_error_panic_hook::set_once(); - // let window = web_sys::window().expect("no global `window` exists"); - // let document = window.document().expect("should have a document on window"); - let location = window().location().pathname()?; let path: Vec<&str> = location.split('/').skip(1).collect(); @@ -56,8 +38,6 @@ pub fn main() -> Result<(), JsValue> { let _ = request::put::<(), _>("set_lang", body).await; let _ = window().location().reload(); }); - - // log!(lang); }) .forget(); diff --git a/frontend/src/on_click.rs b/frontend/src/on_click.rs index a634e30..0c2b694 100644 --- a/frontend/src/on_click.rs +++ b/frontend/src/on_click.rs @@ -1,6 +1,6 @@ use futures::channel::mpsc; use futures::stream::Stream; -use gloo::{console::log, events::EventListener, net::http::Request, utils::document}; +use gloo::events::EventListener; use std::{ future::Future, pin::Pin, -- 2.49.0