feat signin form

This commit is contained in:
Per Stark
2024-12-27 01:08:39 +01:00
parent 61e81ca894
commit a3984ab348
4 changed files with 61 additions and 53 deletions

View File

@@ -26,6 +26,7 @@ use zettle_db::{
html::{ html::{
index::index_handler, index::index_handler,
search_result::search_result_handler, search_result::search_result_handler,
signin::{authenticate_user, show_signin_form},
signout::sign_out_user, signout::sign_out_user,
signup::{process_signup_and_show_verification, show_signup_form}, signup::{process_signup_and_show_verification, show_signup_form},
}, },
@@ -149,6 +150,7 @@ fn html_routes(
.route("/", get(index_handler)) .route("/", get(index_handler))
.route("/search", get(search_result_handler)) .route("/search", get(search_result_handler))
.route("/signout", get(sign_out_user)) .route("/signout", get(sign_out_user))
.route("/signin", get(show_signin_form).post(authenticate_user))
.route( .route(
"/signup", "/signup",
get(show_signup_form).post(process_signup_and_show_verification), get(show_signup_form).post(process_signup_and_show_verification),

View File

@@ -1,9 +1,10 @@
use axum::{ use axum::{
extract::State, extract::State,
http::{StatusCode, Uri},
response::{IntoResponse, Redirect}, response::{IntoResponse, Redirect},
Form, Form,
}; };
use axum_htmx::HxBoosted; use axum_htmx::{HxBoosted, HxRedirect};
use axum_session_auth::AuthSession; use axum_session_auth::AuthSession;
use axum_session_surreal::SessionSurrealPool; use axum_session_surreal::SessionSurrealPool;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -17,6 +18,7 @@ use super::{render_block, render_template};
pub struct SignupParams { pub struct SignupParams {
pub email: String, pub email: String,
pub password: String, pub password: String,
pub remember_me: Option<String>,
} }
#[derive(Serialize)] #[derive(Serialize)]
@@ -24,7 +26,7 @@ struct PageData {
// name: String, // name: String,
} }
pub async fn show_login_form( pub async fn show_signin_form(
State(state): State<AppState>, State(state): State<AppState>,
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>, auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
HxBoosted(boosted): HxBoosted, HxBoosted(boosted): HxBoosted,
@@ -34,12 +36,12 @@ pub async fn show_login_form(
} }
let output = match boosted { let output = match boosted {
true => render_block( true => render_block(
"auth/signup_form.html", "auth/signin_form.html",
"body", "body",
PageData {}, PageData {},
state.templates, state.templates,
)?, )?,
false => render_template("auth/signup_form.html", PageData {}, state.templates)?, false => render_template("auth/signin_form.html", PageData {}, state.templates)?,
}; };
Ok(output.into_response()) Ok(output.into_response())
@@ -50,7 +52,13 @@ pub async fn authenticate_user(
auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>, auth: AuthSession<User, String, SessionSurrealPool<Any>, Surreal<Any>>,
Form(form): Form<SignupParams>, Form(form): Form<SignupParams>,
) -> Result<impl IntoResponse, ApiError> { ) -> Result<impl IntoResponse, ApiError> {
let user = User::create_new(form.email, form.password, &state.surreal_db_client).await?; let user = User::authenticate(form.email, form.password, &state.surreal_db_client).await?;
auth.login_user(user.id); auth.login_user(user.id);
Ok(()) if form
.remember_me
.is_some_and(|string| string == "on".to_string())
{
auth.remember_user(true);
}
Ok((HxRedirect::from(Uri::from_static("/")), StatusCode::OK).into_response())
} }

View File

@@ -1,47 +0,0 @@
{% block content %}
<div class="max-w-md mx-auto">
<h2 class="text-2xl font-bold text-center mb-8">Sign in to your account</h2>
<form hx-post="/signin" hx-target="#signin-result" hx-indicator="#spinner" class="space-y-6">
<div class="form-control">
<label class="label">
<span class="label-text">Email</span>
</label>
<input type="email" name="email" placeholder="Enter your email" class="input input-bordered w-full" required
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$" />
</div>
<div class="form-control">
<label class="label">
<span class="label-text">Password</span>
</label>
<input type="password" name="password" placeholder="Enter your password" class="input input-bordered w-full"
required />
</div>
<div class="flex items-center justify-between">
<label class="label cursor-pointer">
<input type="checkbox" name="remember" class="checkbox checkbox-primary checkbox-sm mr-2" />
<span class="label-text">Remember me</span>
</label>
<a href="/forgot-password" class="link link-primary text-sm">Forgot password?</a>
</div>
<div class="form-control mt-6">
<button class="btn btn-primary w-full">
Sign in
<span id="spinner" class="loading loading-spinner text-primary htmx-indicator"></span>
</button>
</div>
<div id="signin-result"></div>
<div class="divider">OR</div>
<div class="text-center text-sm">
Don't have an account?
<a href="/signup" class="link link-primary">Create one</a>
</div>
</form>
</div>
{% endblock %}

View File

@@ -0,0 +1,45 @@
{% extends "head_base.html" %}
{% block body %}
<style>
form.htmx-request {
opacity: 0.5;
}
</style>
<div class="min-h-screen grid place-items-center place-content-center">
<div class="max-w-lg mx-auto">
<h2 class="text-2xl font-bold text-center mb-8">Login to your account</h2>
<form hx-post="/signin">
<div class="form-control">
<label class="label">
<span class="label-text">Email</span>
</label>
<input type="email" name="email" placeholder="Enter your email" class="input input-bordered w-full" required />
</div>
<div class="form-control">
<label class="label">
<span class="label-text">Password</span>
</label>
<input type="password" name="password" placeholder="Enter your password" class="input input-bordered w-full"
required minlength="8" />
</div>
<div class="form-control mt-4">
<label class="label cursor-pointer justify-start gap-2">
<input type="checkbox" name="remember_me" class="checkbox checkbox-primary" />
<span class="label-text">Remember me</span>
</label>
</div>
<div class="form-control mt-6">
<button id="submit-btn" class="btn btn-primary w-full">
Login
</button>
</div>
<div id="login-result"></div>
</form>
<div class="divider">OR</div>
<div class="text-center text-sm">
Don't have an account?
<a href="/signup" class="link link-primary">Sign up</a>
</div>
</div>
</div>
{% endblock %}