add login functionality

This commit is contained in:
Charlotte 🦝 Delenk 2024-12-10 11:40:41 +01:00
parent f10f57b698
commit 8b3565f4e9
Signed by: darkkirb
GPG key ID: AB2BD8DAF2E37122
7 changed files with 18249 additions and 8881 deletions

25
Cargo.lock generated
View file

@ -1084,15 +1084,19 @@ dependencies = [
name = "chir-rs-fe"
version = "0.1.0"
dependencies = [
"bincode 2.0.0-rc.3",
"chir-rs-http-api",
"console_error_panic_hook",
"eyre",
"gloo-net 0.6.0",
"gloo-storage 0.3.0",
"tracing",
"tracing-subscriber",
"unicode-normalization",
"wasm-bindgen",
"wasm-bindgen-cli",
"wasm-tracing",
"web-sys",
"yew",
"yew-router",
]
@ -2164,6 +2168,27 @@ dependencies = [
"web-sys",
]
[[package]]
name = "gloo-net"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06f627b1a58ca3d42b45d6104bf1e1a03799df472df00988b6ba21accc10580"
dependencies = [
"futures-channel",
"futures-core",
"futures-sink",
"gloo-utils 0.2.0",
"http 1.2.0",
"js-sys",
"pin-project",
"serde",
"serde_json",
"thiserror 1.0.69",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "gloo-render"
version = "0.1.1"

15573
Cargo.nix

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,8 @@ members = [
"chir-rs-castore",
"chir-rs-client",
"chir-rs-config",
"chir-rs-db", "chir-rs-fe",
"chir-rs-db",
"chir-rs-fe",
"chir-rs-gemini",
"chir-rs-http",
"chir-rs-http-api",

View file

@ -7,14 +7,18 @@ edition = "2021"
crate-type = ["cdylib"]
[dependencies]
bincode = "2.0.0-rc.3"
chir-rs-http-api = { version = "0.1.0", path = "../chir-rs-http-api" }
console_error_panic_hook = "0.1.7"
eyre = "0.6.12"
gloo-net = "0.6.0"
gloo-storage = "0.3.0"
tracing = "0.1.41"
tracing-subscriber = "0.3.19"
unicode-normalization = "0.1.24"
wasm-bindgen = "=0.2.99"
wasm-tracing = "0.2.1"
web-sys = { version = "0.3.76", features = ["HtmlInputElement"] }
yew = { version = "0.21.0", features = ["csr"] }
yew-router = "0.18.0"
@ -68,4 +72,3 @@ panic = "deny"
panic-in-result-fn = "forbid"
rc-buffer = "warn"
rc-mutex = "deny"
unwrap-used = "forbid"

View file

@ -1,14 +1,29 @@
//! Management frontend for chir.rs
use std::future::Future;
use tracing::{error, instrument};
use tracing_subscriber::{layer::SubscriberExt as _, util::SubscriberInitExt as _};
use wasm_bindgen::prelude::*;
use wasm_tracing::{WASMLayer, WASMLayerConfigBuilder};
use yew::prelude::*;
use yew::{platform::spawn_local, prelude::*};
use yew_router::prelude::*;
pub mod home;
pub mod login;
#[instrument(skip(fut))]
pub fn spawn<F>(fut: F)
where
F: Future<Output = eyre::Result<()>> + 'static,
{
spawn_local(async move {
if let Err(e) = fut.await {
error!("Failed to run async closure: {e:?}");
}
});
}
/// Routes defined by this SPA
#[derive(Clone, Routable, PartialEq)]
enum Route {
@ -25,7 +40,9 @@ enum Route {
fn switch(routes: Route) -> Html {
match routes {
Route::Home => home::home_page(),
Route::Login => login::login(),
Route::Login => html! {
<login::Login />
},
}
}

View file

@ -1,20 +1,99 @@
//! Login screen
use std::collections::HashSet;
use chir_rs_http_api::{
auth::{LoginRequest, PasetoToken, Scope},
errors::APIError,
};
use eyre::Context;
use gloo_net::http::Request;
use gloo_storage::{SessionStorage, Storage as _};
use tracing::info;
use unicode_normalization::UnicodeNormalization;
use wasm_bindgen::JsCast as _;
use web_sys::HtmlInputElement;
use yew::prelude::*;
use yew_router::hooks::use_navigator;
use crate::{spawn, Route};
/// Login view
pub fn login() -> Html {
#[function_component]
pub fn Login() -> Html {
let navigator = use_navigator().unwrap();
let username_handle = use_state(String::default);
let username = username_handle.clone();
let password_handle = use_state(String::default);
let password = password_handle.clone();
let on_username_input = Callback::from(move |e: InputEvent| {
let target = e.target();
let input = target.and_then(|t| t.dyn_into::<HtmlInputElement>().ok());
if let Some(input) = input {
username.set(input.value());
}
});
let on_password_input = Callback::from(move |e: InputEvent| {
let target = e.target();
let input = target.and_then(|t| t.dyn_into::<HtmlInputElement>().ok());
if let Some(input) = input {
password.set(input.value());
}
});
let onsubmit = Callback::from(move |e: SubmitEvent| {
let navigator = navigator.clone();
e.prevent_default();
let username = username_handle.trim();
let password = password_handle.trim().nfkc().collect::<String>();
let request = LoginRequest {
username: username.to_string(),
password,
scopes: HashSet::from([Scope::Full]),
};
spawn(async move {
let req_body = bincode::encode_to_vec(request, bincode::config::standard())
.context("Encoding login request")?;
let resp = Request::post("/.api/auth/login")
.header("Content-Type", "application/x+bincode")
.header("Accept", "application/x+bincode")
.body(req_body)
.context("Setting body for login request")?
.send()
.await
.context("Sending request for login request")?;
let status = resp.status();
let resp_body = resp
.binary()
.await
.context("Loading server response for login request")?;
if status >= 400 {
let error: APIError =
bincode::decode_from_slice(&resp_body, bincode::config::standard())
.context("Decoding error response")?
.0;
Err(error).context("While logging in")?;
} else {
let token: PasetoToken =
bincode::decode_from_slice(&resp_body, bincode::config::standard())
.context("Decoding success response")?
.0;
SessionStorage::set("api", token.to_paseto());
}
navigator.replace(&Route::Home);
Ok(())
});
info!("{e:?}");
});
html! {
<form accept-charset="utf-8" onsubmit={onsubmit}>
<label for="username">{"Username:"}</label>
<input id="username" type="text" placeholder="https://lotte.chir.rs/" />
<input id="username" type="text" placeholder="https://lotte.chir.rs/" oninput={on_username_input} />
<label for="password">{"Password:"}</label>
<input id="password" type="password" placeholder="●●●●●●●●●●●●●●●●" />
<input id="password" type="password" placeholder="●●●●●●●●●●●●●●●●" oninput={on_password_input} />
<input type="submit" value="Submit"/>
</form>
}

2
result
View file

@ -1 +1 @@
/nix/store/bb08g8h23lxqv4dgp9nmqwibkyllb976-chir-rs-fe
/nix/store/navjjbwfm21p9z8c9f62zpislxhbn1xb-chir-rs-fe