From c125cbb7a5729db892a39b4967ee386f58567650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Dlouh=C3=BD?= <marek.dlouhy@plus4u.net> Date: Sun, 14 Jan 2024 14:44:51 +0100 Subject: [PATCH] feat: user registration --- .../src/handlers/templates/register.rs | 57 +++++++++++- cumhub_backend/src/init.rs | 3 +- .../src/repositories/user/repository.rs | 12 +++ .../templates/register_page/page_content.html | 87 ++++++++++++++----- 4 files changed, 134 insertions(+), 25 deletions(-) diff --git a/cumhub_backend/src/handlers/templates/register.rs b/cumhub_backend/src/handlers/templates/register.rs index 8759969..5dab0f3 100644 --- a/cumhub_backend/src/handlers/templates/register.rs +++ b/cumhub_backend/src/handlers/templates/register.rs @@ -1,10 +1,16 @@ use std::ops::Deref; -use actix_web::{get, HttpRequest, Result as ActixResult, HttpResponse, error::ErrorInternalServerError}; -use actix_web::web::Data; +use actix_web::{get, HttpRequest, Result as ActixResult, HttpResponse, post, error::ErrorInternalServerError}; +use actix_web::web::{Data, Form, Json}; use askama::Template; +use diesel::IntoSql; use jwt_simple::algorithms::HS256Key; +use jwt_simple::prelude::Serialize; +use serde::Deserialize; use crate::{templates::RegisterTemplate}; -use crate::utils::auth::AuthHandler; +use crate::repositories::user::models::NewUser; +use crate::repositories::user::repository::{PgUserRepository, UserRepository}; +use crate::repositories::video::repository::PgVideoRepository; +use crate::utils::auth::{AuthHandler, hash_password}; #[get("/register")] pub async fn get_register( @@ -14,6 +20,8 @@ pub async fn get_register( let cookies = data.cookies().unwrap().clone(); let auth = AuthHandler::new(cookies, key.deref()); let _ = auth.get_token_from_cookie(); + let user = auth.get_user_info_from_token(); + let template = RegisterTemplate { already_logged_in: auth.user_logged_in(), @@ -22,4 +30,47 @@ pub async fn get_register( let body = template.render().map_err(ErrorInternalServerError)?; Ok(HttpResponse::Ok().content_type("text/html").body(body)) +} + + +#[derive(Serialize, Deserialize)] +struct RegisterParams { + username: String, + email: String, + profile_pic: String, + password: String +} +#[post("/register")] +pub async fn post_register( + user: Form<RegisterParams>, + data: Data<PgUserRepository> +) -> ActixResult<HttpResponse> { + let form = user.into_inner(); + + let email_taken = data.get_user_by_email(form.email.clone()); + if email_taken.is_ok() { + return Ok(HttpResponse::BadRequest().body("Email already taken".to_string())); + } + + let username_taken = data.get_user_by_username(form.username.clone()); + if username_taken.is_ok() { + return Ok(HttpResponse::BadRequest().body("Username already taken".to_string())); + } + + let hash_result = hash_password(form.password.clone()); + let hash = match hash_result { + Ok(hash) => hash, + Err(_) => return Ok(HttpResponse::InternalServerError().body("Error hashing password".to_string())) + }; + + let user = NewUser { + username: form.username, + email: form.email, + password_hash: hash.unprotected_as_encoded().to_string(), + profile_pic_url: form.profile_pic + }; + + let user = data.create_user(user).unwrap(); + + Ok(HttpResponse::SeeOther().append_header(("Location", "/")).content_type("text/html").finish()) } \ No newline at end of file diff --git a/cumhub_backend/src/init.rs b/cumhub_backend/src/init.rs index 3189cd7..f78b182 100644 --- a/cumhub_backend/src/init.rs +++ b/cumhub_backend/src/init.rs @@ -14,7 +14,7 @@ use crate::handlers::templates::index::filter_index_videos; use crate::handlers::templates::login::{get_login, logout_user, post_login}; use crate::handlers::templates::premium::{get_premium, post_premium_subscribe}; use crate::handlers::templates::profile::filter_videos; -use crate::handlers::templates::register::get_register; +use crate::handlers::templates::register::{get_register, post_register}; use crate::handlers::templates::subscribed::{ get_subscribed, post_search_channels, post_search_subscribed_videos, }; @@ -80,6 +80,7 @@ pub fn configure_webapp(_config: &mut ServiceConfig) { .service(get_account_settings) .service(upload_video) .service(get_register) + .service(post_register) .service(get_subscribed) .service(post_search_channels) .service(get_channel) diff --git a/cumhub_backend/src/repositories/user/repository.rs b/cumhub_backend/src/repositories/user/repository.rs index 3e50ade..4506745 100644 --- a/cumhub_backend/src/repositories/user/repository.rs +++ b/cumhub_backend/src/repositories/user/repository.rs @@ -16,6 +16,7 @@ use crate::schema::user::{deleted_at, email, id, username}; pub trait UserRepository { fn get_user(&self, user_id: Uuid) -> Result<User, DatabaseError>; fn get_user_by_username(&self, username_filter: String) -> Result<User, DatabaseError>; + fn get_user_by_email(&self, email_filter: String) -> Result<User, DatabaseError>; fn create_user(&self, new_user: NewUser) -> Result<User, DatabaseError>; fn edit_user(&self, user_id: Uuid, patch_user: PartialUser) -> Result<User, DatabaseError>; fn delete_user(&self, user_id: Uuid) -> Result<(), DatabaseError>; @@ -55,6 +56,17 @@ impl UserRepository for PgUserRepository { Ok(user) } + fn get_user_by_email(&self, email_filter: String) -> Result<User, DatabaseError> { + let mut conn = self.pg_pool.get()?; + let user = schema::user::dsl::user + .filter(email.eq(email_filter)) + .filter(deleted_at.is_null()) + .select(User::as_select()) + .first(conn.deref_mut())?; + + Ok(user) + } + fn create_user(&self, new_user: NewUser) -> Result<User, DatabaseError> { let mut conn = self.pg_pool.get()?; let user = diesel::insert_into(schema::user::table) diff --git a/cumhub_backend/templates/register_page/page_content.html b/cumhub_backend/templates/register_page/page_content.html index 3f205b2..66de9ef 100644 --- a/cumhub_backend/templates/register_page/page_content.html +++ b/cumhub_backend/templates/register_page/page_content.html @@ -4,24 +4,21 @@ {% include "./common/user_already_logged.html" %} {% else %} - <p class="text-white text-3xl">Welcome to Cumhub!</p> + <p class="text-3xl text-white">Welcome to Cumhub!</p> - <form hx-on:submit="{ - event.preventDefault(); - document.cookie = 'token=token; path=/'; - window.location.href = '/'; - }" class="w-2/5 h-auto py-10 bg-gray-800 rounded-xl flex flex-col items-center"> + <form class="flex flex-col items-center w-2/5 h-auto py-10 bg-gray-800 rounded-xl" + hx-post="/register" hx-push-url="true"> <!--REGISTER CONTAINER--> - <div id="register_container" class="w-full h-auto pb-10 bg-gray-800 rounded-xl flex flex-col items-center"> + <div id="register_container" class="flex flex-col items-center w-full h-auto pb-10 bg-gray-800 rounded-xl"> - <a href="/" class="mb-4 w-full flex justify-start pl-8"> - <span class="material-symbols-outlined text-gray-400"> + <a href="/" class="flex justify-start w-full pl-8 mb-4"> + <span class="text-gray-400 material-symbols-outlined"> arrow_back_ios </span> </a> <div id="username_div" class="w-[90%] flex flex-col gap-2 mb-8"> - <label for="username_input" class="text-white text-xl"> + <label for="username_input" class="text-xl text-white"> Username: </label> <div id="username_input_div" @@ -29,23 +26,71 @@ hx-on:click="{ document.getElementById('username_input').focus(); }"> - <span class="material-symbols-outlined text-white"> + <span class="text-white material-symbols-outlined"> person </span> - <input id="username_input" hx-on:focus="{ + <input name="username" id="username_input" hx-on:focus="{ document.getElementById('username_input_div').classList.remove('border-white'); document.getElementById('username_input_div').classList.add('border-cyan-300'); }" hx-on:blur="{ document.getElementById('username_input_div').classList.remove('border-cyan-300'); document.getElementById('username_input_div').classList.add('border-white'); - }" placeholder="Username or email" + }" placeholder="Username" class="w-full text-white h-full bg-transparent !outline-none" /> </div> + </div> + <div id="email_div" class="w-[90%] flex flex-col gap-2 mb-8"> + <label for="email_input" class="text-xl text-white"> + Email: + </label> + <div id="email_input_div" + class="px-2 email_input_div border-white border-2 flex h-[4.5rem] w-full items-center gap-2 rounded-lg" + hx-on:click="{ + document.getElementById('email_input').focus(); + }"> + <span class="text-white material-symbols-outlined"> + person + </span> + <input id="email_input" + name="email" + hx-on:focus="{ + document.getElementById('email_input_div').classList.remove('border-white'); + document.getElementById('email_input_div').classList.add('border-cyan-300'); + }" hx-on:blur="{ + document.getElementById('email_input_div').classList.remove('border-cyan-300'); + document.getElementById('email_input_div').classList.add('border-white'); + }" placeholder="Email" + class="w-full text-white h-full bg-transparent !outline-none" /> + </div> + </div> + <div id="profile_pic_div" class="w-[90%] flex flex-col gap-2 mb-8"> + <label for="profile_pic_input" class="text-xl text-white"> + Profile picture url: + </label> + <div id="profile_pic_input_div" + class="px-2 profile_pic_input_div border-white border-2 flex h-[4.5rem] w-full items-center gap-2 rounded-lg" + hx-on:click="{ + document.getElementById('profile_pic_input').focus(); + }"> + <span class="text-white material-symbols-outlined"> + person + </span> + <input id="profile_pic_input" + name="profile_pic" + hx-on:focus="{ + document.getElementById('profile_pic_input_div').classList.remove('border-white'); + document.getElementById('profile_pic_input_div').classList.add('border-cyan-300'); + }" hx-on:blur="{ + document.getElementById('profile_pic_input_div').classList.remove('border-cyan-300'); + document.getElementById('profile_pic_input_div').classList.add('border-white'); + }" placeholder="profile_pic" + class="w-full text-white h-full bg-transparent !outline-none" /> + </div> </div> <div id="password_div" class="w-[90%] flex flex-col gap-2"> - <label for="password_input" class="text-white text-xl"> + <label for="password_input" class="text-xl text-white"> Password: </label> <div id="password_input_div" @@ -53,10 +98,10 @@ hx-on:click="{ document.getElementById('password_input').focus(); }"> - <span class="material-symbols-outlined text-white"> + <span class="text-white material-symbols-outlined"> lock </span> - <input type="password" id="password_input" hx-on:focus="{ + <input name="password" type="password" id="password_input" hx-on:focus="{ document.getElementById('password_input_div').classList.remove('border-white'); document.getElementById('password_input_div').classList.add('border-cyan-300'); }" hx-on:blur="{ @@ -64,11 +109,11 @@ document.getElementById('password_input_div').classList.add('border-white'); }" placeholder="Password" class="w-full text-white h-full bg-transparent !outline-none" /> </div> - <span id="password_error" class="invisible text-red-800 text-sm">Password must have one lowercase letter, one capital letter, one number and has at least 8 characters!</span> + <span id="password_error" class="invisible text-sm text-red-800">Password must have one lowercase letter, one capital letter, one number and has at least 8 characters!</span> </div> <div id="password_confirm_div" class="w-[90%] flex flex-col gap-2"> - <label for="password_confirm_input" class="text-white text-xl"> + <label for="password_confirm_input" class="text-xl text-white"> Password Again: </label> <div id="password_confirm_input_div" @@ -76,7 +121,7 @@ hx-on:click="{ document.getElementById('password_confirm_input').focus(); }"> - <span class="material-symbols-outlined text-white"> + <span class="text-white material-symbols-outlined"> lock </span> <input type="password" id="password_confirm_input" hx-on:focus="{ @@ -87,14 +132,14 @@ document.getElementById('password_confirm_input_div').classList.add('border-white'); }" placeholder="Password" class="w-full text-white h-full bg-transparent !outline-none" /> </div> - <span id="password_confirm_error" class="invisible text-red-800 text-sm">Passwords must match!</span> + <span id="password_confirm_error" class="invisible text-sm text-red-800">Passwords must match!</span> </div> <button type="submit" class="w-2/5 h-[4rem] mt-4 rounded-md mb-4 text-lg bg-blue-700 hover:bg-blue-800 text-white"> Submit </button> - <div class="w-full flex justify-center gap-4 px-10 mt-10"> + <div class="flex justify-center w-full gap-4 px-10 mt-10"> <p class="text-white"> Already have an account? </p> -- GitLab