From db8cb0efff86695f45515186ae3cf439f0daa11e Mon Sep 17 00:00:00 2001 From: Quentin Boyer Date: Sat, 20 May 2023 16:53:07 +0200 Subject: [PATCH] Create a login page --- Cargo.lock | 8 ++++ app/Cargo.toml | 8 ++++ app/Trunk.toml | 3 ++ app/dl_bootstrap.sh | 17 ++++++++ app/index.html | 4 +- app/src/main.rs | 100 ++++++++++++++++++++++++++++++++++++++++++- app/static/login.css | 33 ++++++++++++++ 7 files changed, 170 insertions(+), 3 deletions(-) create mode 100644 app/Trunk.toml create mode 100755 app/dl_bootstrap.sh create mode 100644 app/static/login.css diff --git a/Cargo.lock b/Cargo.lock index 0b35303..49b5506 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,9 +68,17 @@ dependencies = [ name = "app" version = "0.1.0" dependencies = [ + "anyhow", "api", "console_log", + "gloo-net", + "gloo-storage", + "gloo-utils", "log", + "serde_json", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", "yew", "yew-router", ] diff --git a/app/Cargo.toml b/app/Cargo.toml index 45af8c2..306e2a8 100644 --- a/app/Cargo.toml +++ b/app/Cargo.toml @@ -6,8 +6,16 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "1.0.71" api = { version = "0.1.0", path = "../api" } console_log = { version = "1.0.0", features = ["color"] } +gloo-net = "0.2.6" +gloo-storage = "0.2.2" +gloo-utils = "0.1.6" log = "0.4.17" +serde_json = "1.0.96" +wasm-bindgen = "0.2.86" +wasm-bindgen-futures = "0.4.36" +web-sys = "0.3.63" yew = { version = "0.20.0", features = ["csr"] } yew-router = "0.17.0" diff --git a/app/Trunk.toml b/app/Trunk.toml new file mode 100644 index 0000000..f966ead --- /dev/null +++ b/app/Trunk.toml @@ -0,0 +1,3 @@ +[[hooks]] +stage = "build" +command = "./dl_bootstrap.sh" diff --git a/app/dl_bootstrap.sh b/app/dl_bootstrap.sh new file mode 100755 index 0000000..f242723 --- /dev/null +++ b/app/dl_bootstrap.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +VERSION=5.3.0-alpha3 +URL=https://github.com/twbs/bootstrap/releases/download/v${VERSION}/bootstrap-${VERSION}-dist.zip + +if [[ ! -d "$TRUNK_DIST_DIR/bootstrap" ]]; then + cd "$TRUNK_STAGING_DIR" || { + echo "Can't cd to staging directory" + exit 1 + } + wget "$URL" + unzip bootstrap-*.zip + rm bootstrap-*.zip + mv bootstrap-* bootstrap +else + cp -r "$TRUNK_DIST_DIR/bootstrap" "$TRUNK_STAGING_DIR" +fi diff --git a/app/index.html b/app/index.html index 995c87d..a5b92f3 100644 --- a/app/index.html +++ b/app/index.html @@ -1,8 +1,10 @@ - + + + diff --git a/app/src/main.rs b/app/src/main.rs index 4761443..b3f159a 100644 --- a/app/src/main.rs +++ b/app/src/main.rs @@ -1,4 +1,8 @@ +use api::{LoginRequest, LoginResponse}; +use gloo_storage::{LocalStorage, Storage, errors::StorageError}; use log::Level; +use wasm_bindgen::JsCast; +use web_sys::HtmlInputElement; use yew::prelude::*; use yew_router::prelude::*; @@ -30,7 +34,7 @@ fn switch(route: Route) -> Html { }, Route::Login => html! { - "Login" + }, Route::NotFound => html! { "Page not found" @@ -40,7 +44,11 @@ fn switch(route: Route) -> Html { #[function_component] fn Index() -> Html { - let token = use_state(|| None::); + let token = use_state(|| match LocalStorage::get::("token") { + Ok(v) => Some(v), + Err(StorageError::KeyNotFound(_)) => None, + Err(e) => unreachable!("Could not get token: {e:?}"), + }); match &*token { Some(_) => html! { @@ -52,6 +60,94 @@ fn Index() -> Html { } } +async fn do_login(username: String, password: String) -> anyhow::Result<()> { + let rsp = gloo_net::http::Request::post("http://localhost:8085/api/login") + .json(&LoginRequest { username, password })? + .send() + .await?; + + if rsp.status() == 404 { + anyhow::bail!("Account not foud") + } else if !rsp.ok() { + anyhow::bail!("Request failed: {rsp:?}") + } + + let rsp: LoginResponse = rsp.json().await?; + + LocalStorage::set("token", rsp.token)?; + + Ok(()) +} + +#[function_component] +fn Login() -> Html { + let error = use_state(|| None); + + let navigator = use_navigator().unwrap(); + let err = error.clone(); + let onsubmit = Callback::from(move |e: SubmitEvent| { + e.prevent_default(); + + let document = gloo_utils::document(); + + let username: HtmlInputElement = document + .get_element_by_id("floatingUser") + .unwrap() + .dyn_into() + .expect("floatingUser is not an input element"); + let username = username.value(); + + let password: HtmlInputElement = document + .get_element_by_id("floatingPass") + .unwrap() + .dyn_into() + .expect("floatingUser is not an input element"); + let password = password.value(); + + let err = err.clone(); + let navigator = navigator.clone(); + wasm_bindgen_futures::spawn_local(async move { + match do_login(username, password).await { + Ok(_) => { + navigator.push(&Route::Index); + err.set(None); + } + Err(e) => { + err.set(Some(format!("Could not log in: {e:?}"))); + } + } + }); + }); + + html! {<> + +
+

{"Please log in"}

+ if let Some(err) = &*error { + + } +
+ + +
+
+ + +
+ +
+ } +} + fn main() { console_log::init_with_level(Level::Debug).unwrap(); diff --git a/app/static/login.css b/app/static/login.css new file mode 100644 index 0000000..0ea4ed4 --- /dev/null +++ b/app/static/login.css @@ -0,0 +1,33 @@ +html, +body, +main { + height: 100%; +} + +main { + display: flex; + align-items: center; + padding-top: 40px; + padding-bottom: 40px; +} + +.form-signin { + max-width: 330px; + padding: 15px; +} + +.form-signin .form-floating:focus-within { + z-index: 2; +} + +#floatingUser { + margin-bottom: -1px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +#floatingPass { + margin-bottom: 10px; + border-top-right-radius: 0; + border-top-left-radius: 0; +}