2023-05-20 16:53:07 +02:00
|
|
|
use api::{LoginRequest, LoginResponse};
|
|
|
|
|
use gloo_storage::{LocalStorage, Storage, errors::StorageError};
|
2023-05-19 11:23:44 +02:00
|
|
|
use log::Level;
|
2023-05-20 16:53:07 +02:00
|
|
|
use wasm_bindgen::JsCast;
|
|
|
|
|
use web_sys::HtmlInputElement;
|
2023-05-19 11:23:44 +02:00
|
|
|
use yew::prelude::*;
|
|
|
|
|
use yew_router::prelude::*;
|
|
|
|
|
|
|
|
|
|
#[derive(Routable, Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
enum Route {
|
|
|
|
|
#[at("/")]
|
|
|
|
|
Index,
|
2023-05-20 12:27:33 +02:00
|
|
|
#[at("/login")]
|
|
|
|
|
Login,
|
2023-05-19 11:23:44 +02:00
|
|
|
#[at("/404")]
|
|
|
|
|
#[not_found]
|
|
|
|
|
NotFound,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[function_component]
|
|
|
|
|
fn App() -> Html {
|
|
|
|
|
html! {
|
|
|
|
|
<BrowserRouter>
|
|
|
|
|
<main>
|
|
|
|
|
<Switch<Route> render={switch} />
|
|
|
|
|
</main>
|
|
|
|
|
</BrowserRouter>
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn switch(route: Route) -> Html {
|
|
|
|
|
match route {
|
|
|
|
|
Route::Index => html! {
|
2023-05-20 12:27:33 +02:00
|
|
|
<Index />
|
|
|
|
|
},
|
|
|
|
|
Route::Login => html! {
|
2023-05-20 16:53:07 +02:00
|
|
|
<Login />
|
2023-05-19 11:23:44 +02:00
|
|
|
},
|
|
|
|
|
Route::NotFound => html! {
|
|
|
|
|
"Page not found"
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-20 12:27:33 +02:00
|
|
|
#[function_component]
|
|
|
|
|
fn Index() -> Html {
|
2023-05-20 16:53:07 +02:00
|
|
|
let token = use_state(|| match LocalStorage::get::<String>("token") {
|
|
|
|
|
Ok(v) => Some(v),
|
|
|
|
|
Err(StorageError::KeyNotFound(_)) => None,
|
|
|
|
|
Err(e) => unreachable!("Could not get token: {e:?}"),
|
|
|
|
|
});
|
2023-05-20 12:27:33 +02:00
|
|
|
|
|
|
|
|
match &*token {
|
|
|
|
|
Some(_) => html! {
|
|
|
|
|
"Index"
|
|
|
|
|
},
|
|
|
|
|
None => html! {
|
|
|
|
|
<Redirect<Route> to={Route::Login} />
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-20 16:53:07 +02:00
|
|
|
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! {<>
|
|
|
|
|
<link href="/login.css" rel="stylesheet" />
|
|
|
|
|
<form class={classes!("form-signin", "w-100", "m-auto", "text-center")} {onsubmit}>
|
|
|
|
|
<h1 class={classes!("h3", "mb-3")}>{"Please log in"}</h1>
|
|
|
|
|
if let Some(err) = &*error {
|
|
|
|
|
<div class={classes!("alert", "alert-danger")} role="alert">
|
|
|
|
|
{err}
|
|
|
|
|
</div>
|
|
|
|
|
}
|
|
|
|
|
<div class={classes!("form-floating")}>
|
|
|
|
|
<input id="floatingUser" class={classes!("form-control")} placeholder="Username" />
|
|
|
|
|
<label for="floatingUser">{"Username"}</label>
|
|
|
|
|
</div>
|
|
|
|
|
<div class={classes!("form-floating")}>
|
|
|
|
|
<input
|
|
|
|
|
id="floatingPass"
|
|
|
|
|
class={classes!("form-control")}
|
|
|
|
|
placeholder="Password"
|
|
|
|
|
type="password"
|
|
|
|
|
/>
|
|
|
|
|
<label for="floatingPass">{"Password"}</label>
|
|
|
|
|
</div>
|
|
|
|
|
<button class={classes!("w-100", "btn", "btn-lg", "btn-primary")} type="submit">
|
|
|
|
|
{"Login"}
|
|
|
|
|
</button>
|
|
|
|
|
</form>
|
|
|
|
|
</>}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-19 11:23:44 +02:00
|
|
|
fn main() {
|
|
|
|
|
console_log::init_with_level(Level::Debug).unwrap();
|
|
|
|
|
|
|
|
|
|
yew::Renderer::<App>::new().render();
|
|
|
|
|
}
|