feat: added client side signup/verify data validation
This commit is contained in:
parent
ca7ed256f8
commit
f49205a2c6
|
@ -30,6 +30,7 @@ impl Display for AccountRegisterConflictResponse {
|
||||||
#[serde(tag = "problem", rename_all = "snake_case")]
|
#[serde(tag = "problem", rename_all = "snake_case")]
|
||||||
pub enum AccountRegisterCriteriaProblemResponse {
|
pub enum AccountRegisterCriteriaProblemResponse {
|
||||||
Email,
|
Email,
|
||||||
|
Username,
|
||||||
Password,
|
Password,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -125,6 +125,9 @@ impl Session {
|
||||||
data::AccountRegisterCriteriaProblemResponse::Email => {
|
data::AccountRegisterCriteriaProblemResponse::Email => {
|
||||||
Err(Error::msg(format!("Not a valid email!")))
|
Err(Error::msg(format!("Not a valid email!")))
|
||||||
}
|
}
|
||||||
|
data::AccountRegisterCriteriaProblemResponse::Username => Err(Error::msg(
|
||||||
|
format!("The username has to be alphanumerical!"),
|
||||||
|
)),
|
||||||
data::AccountRegisterCriteriaProblemResponse::Password => Err(Error::msg(
|
data::AccountRegisterCriteriaProblemResponse::Password => Err(Error::msg(
|
||||||
format!("The password does not meet the criteria!"),
|
format!("The password does not meet the criteria!"),
|
||||||
)),
|
)),
|
||||||
|
|
|
@ -2,7 +2,6 @@ mod signin;
|
||||||
mod signup;
|
mod signup;
|
||||||
|
|
||||||
use crate::topbar::Tab;
|
use crate::topbar::Tab;
|
||||||
use anyhow::Result;
|
|
||||||
use signin::SignIn;
|
use signin::SignIn;
|
||||||
use signup::SignUp;
|
use signup::SignUp;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::backend::Session;
|
use crate::backend::Session;
|
||||||
use crate::Callbacks;
|
use crate::Callbacks;
|
||||||
use anyhow::{Error, Result};
|
use anyhow::Result;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
use web_sys::HtmlInputElement;
|
use web_sys::HtmlInputElement;
|
||||||
|
@ -27,11 +27,50 @@ struct Data {
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub email: String,
|
pub email: String,
|
||||||
pub password: String,
|
pub password: String,
|
||||||
pub password_reenter: String,
|
pub password_confirm: String,
|
||||||
|
|
||||||
pub token: String,
|
pub token: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Data {
|
||||||
|
fn confirm_password(&self) {
|
||||||
|
let document = web_sys::window()
|
||||||
|
.expect("Global window does not exist!")
|
||||||
|
.document()
|
||||||
|
.expect("Expecting a document on window!");
|
||||||
|
|
||||||
|
if self.password != self.password_confirm {
|
||||||
|
document
|
||||||
|
.get_element_by_id("signup-password")
|
||||||
|
.unwrap()
|
||||||
|
.dyn_into::<HtmlInputElement>()
|
||||||
|
.unwrap()
|
||||||
|
.set_custom_validity("The passwords don't match!");
|
||||||
|
|
||||||
|
document
|
||||||
|
.get_element_by_id("signup-password-confirm")
|
||||||
|
.unwrap()
|
||||||
|
.dyn_into::<HtmlInputElement>()
|
||||||
|
.unwrap()
|
||||||
|
.set_custom_validity("The passwords don't match!");
|
||||||
|
} else {
|
||||||
|
document
|
||||||
|
.get_element_by_id("signup-password")
|
||||||
|
.unwrap()
|
||||||
|
.dyn_into::<HtmlInputElement>()
|
||||||
|
.unwrap()
|
||||||
|
.set_custom_validity("");
|
||||||
|
|
||||||
|
document
|
||||||
|
.get_element_by_id("signup-password-confirm")
|
||||||
|
.unwrap()
|
||||||
|
.dyn_into::<HtmlInputElement>()
|
||||||
|
.unwrap()
|
||||||
|
.set_custom_validity("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Properties, PartialEq)]
|
#[derive(Properties, PartialEq)]
|
||||||
pub struct Props {
|
pub struct Props {
|
||||||
pub close: Callback<()>,
|
pub close: Callback<()>,
|
||||||
|
@ -47,7 +86,7 @@ pub fn SignUp(props: &Props) -> Html {
|
||||||
let on_username_changed = Callback::from(move |event: Event| {
|
let on_username_changed = Callback::from(move |event: Event| {
|
||||||
let username = event
|
let username = event
|
||||||
.target()
|
.target()
|
||||||
.expect("WTF")
|
.expect("Expected event target!")
|
||||||
.unchecked_into::<HtmlInputElement>()
|
.unchecked_into::<HtmlInputElement>()
|
||||||
.value();
|
.value();
|
||||||
|
|
||||||
|
@ -60,46 +99,48 @@ pub fn SignUp(props: &Props) -> Html {
|
||||||
let on_email_changed = Callback::from(move |event: Event| {
|
let on_email_changed = Callback::from(move |event: Event| {
|
||||||
let email = event
|
let email = event
|
||||||
.target()
|
.target()
|
||||||
.expect("WTF")
|
.expect("Expected event target!")
|
||||||
.unchecked_into::<HtmlInputElement>()
|
.unchecked_into::<HtmlInputElement>()
|
||||||
.value();
|
.value();
|
||||||
|
|
||||||
let mut data = cloned_state.deref().clone();
|
let mut state = cloned_state.deref().clone();
|
||||||
data.email = email;
|
state.email = email;
|
||||||
cloned_state.set(data);
|
cloned_state.set(state);
|
||||||
});
|
});
|
||||||
|
|
||||||
let cloned_state = state.clone();
|
let cloned_state = state.clone();
|
||||||
let on_password_changed = Callback::from(move |event: Event| {
|
let on_password_input = Callback::from(move |event: InputEvent| {
|
||||||
let password = event
|
let password = event
|
||||||
.target()
|
.target()
|
||||||
.expect("WTF")
|
.expect("Expected event target!")
|
||||||
.unchecked_into::<HtmlInputElement>()
|
.unchecked_into::<HtmlInputElement>();
|
||||||
.value();
|
|
||||||
|
|
||||||
let mut data = cloned_state.deref().clone();
|
let mut state = cloned_state.deref().clone();
|
||||||
data.password = password;
|
state.password = password.value();
|
||||||
cloned_state.set(data);
|
state.confirm_password();
|
||||||
|
cloned_state.set(state);
|
||||||
});
|
});
|
||||||
|
|
||||||
let cloned_state = state.clone();
|
let cloned_state = state.clone();
|
||||||
let on_password_reenter_changed = Callback::from(move |event: Event| {
|
let on_password_confirm_input = Callback::from(move |event: InputEvent| {
|
||||||
let password_reenter = event
|
let password_confirm = event
|
||||||
.target()
|
.target()
|
||||||
.expect("WTF")
|
.expect("Expected event target!")
|
||||||
.unchecked_into::<HtmlInputElement>()
|
.unchecked_into::<HtmlInputElement>();
|
||||||
.value();
|
|
||||||
|
|
||||||
let mut data = cloned_state.deref().clone();
|
let mut state = cloned_state.deref().clone();
|
||||||
data.password_reenter = password_reenter;
|
state.password_confirm = password_confirm.value();
|
||||||
cloned_state.set(data);
|
if password_confirm.check_validity() {
|
||||||
|
state.confirm_password();
|
||||||
|
}
|
||||||
|
cloned_state.set(state);
|
||||||
});
|
});
|
||||||
|
|
||||||
let cloned_state = state.clone();
|
let cloned_state = state.clone();
|
||||||
let on_token_changed = Callback::from(move |event: Event| {
|
let on_token_changed = Callback::from(move |event: Event| {
|
||||||
let token = event
|
let token = event
|
||||||
.target()
|
.target()
|
||||||
.expect("WTF")
|
.expect("Expected event target!")
|
||||||
.unchecked_into::<HtmlInputElement>()
|
.unchecked_into::<HtmlInputElement>()
|
||||||
.value();
|
.value();
|
||||||
|
|
||||||
|
@ -171,22 +212,29 @@ pub fn SignUp(props: &Props) -> Html {
|
||||||
<form class="auth-container" onsubmit={onsubmit}>
|
<form class="auth-container" onsubmit={onsubmit}>
|
||||||
<div class="auth-field">
|
<div class="auth-field">
|
||||||
<label for="username">{"Username"}</label><br/>
|
<label for="username">{"Username"}</label><br/>
|
||||||
<input class="auth-input" type="text" name="username" onchange={on_username_changed}/><br/>
|
<input class="auth-input" type="text" name="username" onchange={on_username_changed}
|
||||||
|
required=true pattern="^[a-zA-Z0-9]+$" /><br/>
|
||||||
|
<div class="auth-input-tooltip">{"The username has to be alphanumeric."}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="auth-field">
|
<div class="auth-field">
|
||||||
<label for="email">{"Email"}</label><br/>
|
<label for="email">{"Email"}</label><br/>
|
||||||
<input class="auth-input" type="text" name="email" onchange={on_email_changed}/><br/>
|
<input class="auth-input" type="email" name="email" onchange={on_email_changed} required=true
|
||||||
|
pattern=r"^([a-z0-9_+]([a-z0-9_+.]*[a-z0-9_+])?)@([a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6})"/>
|
||||||
|
<div class="auth-input-tooltip">{"Enter a valid email."}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="auth-field">
|
<div class="auth-field">
|
||||||
<label for="password">{"Password"}</label><br/>
|
<label for="password">{"Password"}</label><br/>
|
||||||
<input class="auth-input" type="password" name="password" onchange={on_password_changed}/><br/>
|
<input id="signup-password" class="auth-input" type="password" name="password" oninput={on_password_input} required=true
|
||||||
|
pattern=r"^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[\x21-\x2F\x3A-\x40\x5B-\x60\x7B-\x7E])[A-Za-z0-9\x21-\x2F\x3A-\x40\x5B-\x60\x7B-\x7E]{12,}$" /><br/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="auth-field">
|
<div class="auth-field">
|
||||||
<label for="password-reenter">{"Password"}</label><br/>
|
<label for="password-confirm">{"Confirm Password"}</label><br/>
|
||||||
<input class="auth-input" type="password" name="password-reenter" onchange={on_password_reenter_changed}/><br/>
|
<input id="signup-password-confirm" class="auth-input" type="password" name="password-confirm" oninput={on_password_confirm_input} required=true
|
||||||
|
pattern=r"^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[\x21-\x2F\x3A-\x40\x5B-\x60\x7B-\x7E])[A-Za-z0-9\x21-\x2F\x3A-\x40\x5B-\x60\x7B-\x7E]{12,}$" /><br/>
|
||||||
|
<div class="auth-input-tooltip">{"A password must contain uppercase, lowercase, numbers and special characters."}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
if !state.loading {
|
if !state.loading {
|
||||||
|
@ -218,7 +266,8 @@ pub fn SignUp(props: &Props) -> Html {
|
||||||
|
|
||||||
<div class="auth-field">
|
<div class="auth-field">
|
||||||
<label for="token">{"Verification Token"}</label><br/>
|
<label for="token">{"Verification Token"}</label><br/>
|
||||||
<input class="auth-input" type="text" name="token" onchange={on_token_changed}/><br/>
|
<input class="auth-input" type="text" name="token" onchange={on_token_changed} required=true
|
||||||
|
pattern=r"^([0-9a-f]{32})" /><br/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
if !state.loading {
|
if !state.loading {
|
||||||
|
|
|
@ -107,6 +107,21 @@
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.auth-input:valid {
|
||||||
|
border-color: limegreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-input-tooltip {
|
||||||
|
display: none;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
.auth-input:invalid ~ .auth-input-tooltip {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
.auth-status {
|
.auth-status {
|
||||||
font: normal 14px Roboto;
|
font: normal 14px Roboto;
|
||||||
color: #ff0000;
|
color: #ff0000;
|
||||||
|
|
Loading…
Reference in New Issue