Skip to content
Snippets Groups Projects
Commit c125cbb7 authored by Marek Dlouhý's avatar Marek Dlouhý
Browse files

feat: user registration

parent de54aa08
No related branches found
No related tags found
1 merge request!32Develop
use std::ops::Deref; use std::ops::Deref;
use actix_web::{get, HttpRequest, Result as ActixResult, HttpResponse, error::ErrorInternalServerError}; use actix_web::{get, HttpRequest, Result as ActixResult, HttpResponse, post, error::ErrorInternalServerError};
use actix_web::web::Data; use actix_web::web::{Data, Form, Json};
use askama::Template; use askama::Template;
use diesel::IntoSql;
use jwt_simple::algorithms::HS256Key; use jwt_simple::algorithms::HS256Key;
use jwt_simple::prelude::Serialize;
use serde::Deserialize;
use crate::{templates::RegisterTemplate}; 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")] #[get("/register")]
pub async fn get_register( pub async fn get_register(
...@@ -14,6 +20,8 @@ pub async fn get_register( ...@@ -14,6 +20,8 @@ pub async fn get_register(
let cookies = data.cookies().unwrap().clone(); let cookies = data.cookies().unwrap().clone();
let auth = AuthHandler::new(cookies, key.deref()); let auth = AuthHandler::new(cookies, key.deref());
let _ = auth.get_token_from_cookie(); let _ = auth.get_token_from_cookie();
let user = auth.get_user_info_from_token();
let template = RegisterTemplate { let template = RegisterTemplate {
already_logged_in: auth.user_logged_in(), already_logged_in: auth.user_logged_in(),
...@@ -22,4 +30,47 @@ pub async fn get_register( ...@@ -22,4 +30,47 @@ pub async fn get_register(
let body = template.render().map_err(ErrorInternalServerError)?; let body = template.render().map_err(ErrorInternalServerError)?;
Ok(HttpResponse::Ok().content_type("text/html").body(body)) 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
...@@ -14,7 +14,7 @@ use crate::handlers::templates::index::filter_index_videos; ...@@ -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::login::{get_login, logout_user, post_login};
use crate::handlers::templates::premium::{get_premium, post_premium_subscribe}; use crate::handlers::templates::premium::{get_premium, post_premium_subscribe};
use crate::handlers::templates::profile::filter_videos; 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::{ use crate::handlers::templates::subscribed::{
get_subscribed, post_search_channels, post_search_subscribed_videos, get_subscribed, post_search_channels, post_search_subscribed_videos,
}; };
...@@ -80,6 +80,7 @@ pub fn configure_webapp(_config: &mut ServiceConfig) { ...@@ -80,6 +80,7 @@ pub fn configure_webapp(_config: &mut ServiceConfig) {
.service(get_account_settings) .service(get_account_settings)
.service(upload_video) .service(upload_video)
.service(get_register) .service(get_register)
.service(post_register)
.service(get_subscribed) .service(get_subscribed)
.service(post_search_channels) .service(post_search_channels)
.service(get_channel) .service(get_channel)
......
...@@ -16,6 +16,7 @@ use crate::schema::user::{deleted_at, email, id, username}; ...@@ -16,6 +16,7 @@ use crate::schema::user::{deleted_at, email, id, username};
pub trait UserRepository { pub trait UserRepository {
fn get_user(&self, user_id: Uuid) -> Result<User, DatabaseError>; 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_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 create_user(&self, new_user: NewUser) -> Result<User, DatabaseError>;
fn edit_user(&self, user_id: Uuid, patch_user: PartialUser) -> 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>; fn delete_user(&self, user_id: Uuid) -> Result<(), DatabaseError>;
...@@ -55,6 +56,17 @@ impl UserRepository for PgUserRepository { ...@@ -55,6 +56,17 @@ impl UserRepository for PgUserRepository {
Ok(user) 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> { fn create_user(&self, new_user: NewUser) -> Result<User, DatabaseError> {
let mut conn = self.pg_pool.get()?; let mut conn = self.pg_pool.get()?;
let user = diesel::insert_into(schema::user::table) let user = diesel::insert_into(schema::user::table)
......
...@@ -4,24 +4,21 @@ ...@@ -4,24 +4,21 @@
{% include {% include
"./common/user_already_logged.html" %} "./common/user_already_logged.html" %}
{% else %} {% 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="{ <form class="flex flex-col items-center w-2/5 h-auto py-10 bg-gray-800 rounded-xl"
event.preventDefault(); hx-post="/register" hx-push-url="true">
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">
<!--REGISTER CONTAINER--> <!--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"> <a href="/" class="flex justify-start w-full pl-8 mb-4">
<span class="material-symbols-outlined text-gray-400"> <span class="text-gray-400 material-symbols-outlined">
arrow_back_ios arrow_back_ios
</span> </span>
</a> </a>
<div id="username_div" class="w-[90%] flex flex-col gap-2 mb-8"> <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: Username:
</label> </label>
<div id="username_input_div" <div id="username_input_div"
...@@ -29,23 +26,71 @@ ...@@ -29,23 +26,71 @@
hx-on:click="{ hx-on:click="{
document.getElementById('username_input').focus(); document.getElementById('username_input').focus();
}"> }">
<span class="material-symbols-outlined text-white"> <span class="text-white material-symbols-outlined">
person person
</span> </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.remove('border-white');
document.getElementById('username_input_div').classList.add('border-cyan-300'); document.getElementById('username_input_div').classList.add('border-cyan-300');
}" hx-on:blur="{ }" hx-on:blur="{
document.getElementById('username_input_div').classList.remove('border-cyan-300'); document.getElementById('username_input_div').classList.remove('border-cyan-300');
document.getElementById('username_input_div').classList.add('border-white'); 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" /> class="w-full text-white h-full bg-transparent !outline-none" />
</div> </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>
<div id="password_div" class="w-[90%] flex flex-col gap-2"> <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: Password:
</label> </label>
<div id="password_input_div" <div id="password_input_div"
...@@ -53,10 +98,10 @@ ...@@ -53,10 +98,10 @@
hx-on:click="{ hx-on:click="{
document.getElementById('password_input').focus(); document.getElementById('password_input').focus();
}"> }">
<span class="material-symbols-outlined text-white"> <span class="text-white material-symbols-outlined">
lock lock
</span> </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.remove('border-white');
document.getElementById('password_input_div').classList.add('border-cyan-300'); document.getElementById('password_input_div').classList.add('border-cyan-300');
}" hx-on:blur="{ }" hx-on:blur="{
...@@ -64,11 +109,11 @@ ...@@ -64,11 +109,11 @@
document.getElementById('password_input_div').classList.add('border-white'); document.getElementById('password_input_div').classList.add('border-white');
}" placeholder="Password" class="w-full text-white h-full bg-transparent !outline-none" /> }" placeholder="Password" class="w-full text-white h-full bg-transparent !outline-none" />
</div> </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>
<div id="password_confirm_div" class="w-[90%] flex flex-col gap-2"> <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: Password Again:
</label> </label>
<div id="password_confirm_input_div" <div id="password_confirm_input_div"
...@@ -76,7 +121,7 @@ ...@@ -76,7 +121,7 @@
hx-on:click="{ hx-on:click="{
document.getElementById('password_confirm_input').focus(); document.getElementById('password_confirm_input').focus();
}"> }">
<span class="material-symbols-outlined text-white"> <span class="text-white material-symbols-outlined">
lock lock
</span> </span>
<input type="password" id="password_confirm_input" hx-on:focus="{ <input type="password" id="password_confirm_input" hx-on:focus="{
...@@ -87,14 +132,14 @@ ...@@ -87,14 +132,14 @@
document.getElementById('password_confirm_input_div').classList.add('border-white'); document.getElementById('password_confirm_input_div').classList.add('border-white');
}" placeholder="Password" class="w-full text-white h-full bg-transparent !outline-none" /> }" placeholder="Password" class="w-full text-white h-full bg-transparent !outline-none" />
</div> </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> </div>
<button type="submit" <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"> class="w-2/5 h-[4rem] mt-4 rounded-md mb-4 text-lg bg-blue-700 hover:bg-blue-800 text-white">
Submit Submit
</button> </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"> <p class="text-white">
Already have an account? Already have an account?
</p> </p>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment