forked from Roflin/gamenight
		
	Started reimplementation of the Rest api in actix-web
This commit is contained in:
		
							parent
							
								
									7741c1dbae
								
							
						
					
					
						commit
						d961896242
					
				
							
								
								
									
										1
									
								
								backend-actix/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								backend-actix/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
target
 | 
			
		||||
							
								
								
									
										1792
									
								
								backend-actix/Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										1792
									
								
								backend-actix/Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										18
									
								
								backend-actix/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								backend-actix/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "backend-actix"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
 | 
			
		||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
actix-web = "4"
 | 
			
		||||
serde = { version = "1.0", features = ["derive"] }
 | 
			
		||||
serde_json = "1.0"
 | 
			
		||||
uuid = { version = "1.3.0", features = ["serde", "v4"] }
 | 
			
		||||
diesel = { version = "2.0", features = ["postgres", "r2d2", "uuid"] }
 | 
			
		||||
diesel-derive-enum = { version = "2.0", features = ["postgres"] }
 | 
			
		||||
argon2 = "0.5"
 | 
			
		||||
chrono = "0.4"
 | 
			
		||||
jsonwebtoken = "8.1"
 | 
			
		||||
validator = { version = "0.16", features = ["derive"] }
 | 
			
		||||
							
								
								
									
										35
									
								
								backend-actix/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								backend-actix/src/main.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
			
		||||
pub mod request;
 | 
			
		||||
pub mod schema;
 | 
			
		||||
 | 
			
		||||
use actix_web::HttpServer;
 | 
			
		||||
use actix_web::App;
 | 
			
		||||
use actix_web::web;
 | 
			
		||||
use diesel::PgConnection;
 | 
			
		||||
use request::{login, register};
 | 
			
		||||
use diesel::r2d2::ConnectionManager;
 | 
			
		||||
use diesel::r2d2::Pool;
 | 
			
		||||
 | 
			
		||||
pub(crate) type DbPool = Pool<ConnectionManager<PgConnection>>;
 | 
			
		||||
 | 
			
		||||
#[actix_web::main]
 | 
			
		||||
async fn main() -> std::io::Result<()> {
 | 
			
		||||
 | 
			
		||||
    let url = "postgres://root:root@localhost/gamenight";
 | 
			
		||||
    let manager = ConnectionManager::<PgConnection>::new(url);
 | 
			
		||||
    // Refer to the `r2d2` documentation for more methods to use
 | 
			
		||||
    // when building a connection pool
 | 
			
		||||
    let pool = Pool::builder()
 | 
			
		||||
        .test_on_check_out(true)
 | 
			
		||||
        .build(manager)
 | 
			
		||||
        .expect("Could not build connection pool");
 | 
			
		||||
 | 
			
		||||
    HttpServer::new(move || {
 | 
			
		||||
        App::new()
 | 
			
		||||
            .app_data(web::Data::new(pool.clone()))
 | 
			
		||||
            .service(login)
 | 
			
		||||
            .service(register)
 | 
			
		||||
    })
 | 
			
		||||
    .bind(("::1", 8080))?
 | 
			
		||||
    .run()
 | 
			
		||||
    .await
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										60
									
								
								backend-actix/src/request/error.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								backend-actix/src/request/error.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,60 @@
 | 
			
		||||
use std::fmt::{Display, Formatter, Result};
 | 
			
		||||
use actix_web::{ResponseError, error::BlockingError};
 | 
			
		||||
use serde::{Serialize, Deserialize};
 | 
			
		||||
use validator::ValidationErrors;
 | 
			
		||||
 | 
			
		||||
use crate::schema::error::DatabaseError;
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize, Debug)]
 | 
			
		||||
pub struct ApiError {
 | 
			
		||||
    pub error: String
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Display for ApiError {
 | 
			
		||||
    // This trait requires `fmt` with this exact signature.
 | 
			
		||||
    fn fmt(&self, f: &mut Formatter) -> Result {
 | 
			
		||||
        write!(f, "{}", self.error)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ResponseError for ApiError { }
 | 
			
		||||
 | 
			
		||||
impl From<DatabaseError> for ApiError {
 | 
			
		||||
    fn from(value: DatabaseError) -> Self {
 | 
			
		||||
        ApiError {
 | 
			
		||||
            error: value.0
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<BlockingError> for ApiError {
 | 
			
		||||
    fn from(value: BlockingError) -> Self {
 | 
			
		||||
        ApiError {
 | 
			
		||||
            error: value.to_string()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<serde_json::Error> for ApiError {
 | 
			
		||||
    fn from(value: serde_json::Error) -> Self {
 | 
			
		||||
        ApiError {
 | 
			
		||||
            error: value.to_string()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<jsonwebtoken::errors::Error> for ApiError {
 | 
			
		||||
    fn from(value: jsonwebtoken::errors::Error) -> Self {
 | 
			
		||||
        ApiError { 
 | 
			
		||||
            error: value.to_string() 
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<ValidationErrors> for ApiError {
 | 
			
		||||
    fn from(value: ValidationErrors) -> Self {
 | 
			
		||||
        ApiError {
 | 
			
		||||
            error: value.to_string()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										98
									
								
								backend-actix/src/request/handler.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								backend-actix/src/request/handler.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,98 @@
 | 
			
		||||
 | 
			
		||||
use actix_web::http::header::ContentType;
 | 
			
		||||
use actix_web::{web, post, HttpResponse, Responder};
 | 
			
		||||
use chrono::Utc;
 | 
			
		||||
use serde::{Serialize, Deserialize};
 | 
			
		||||
use uuid::Uuid;
 | 
			
		||||
use validator::ValidateArgs;
 | 
			
		||||
use crate::DbPool;
 | 
			
		||||
use crate::request::request_data::{Login, Register};
 | 
			
		||||
use crate::request::error::ApiError;
 | 
			
		||||
use crate::request::responses::LoginResponse;
 | 
			
		||||
use crate::schema::user::Role;
 | 
			
		||||
use crate::schema::{self};
 | 
			
		||||
use serde_json;
 | 
			
		||||
use jsonwebtoken::{encode, EncodingKey, Header};
 | 
			
		||||
 | 
			
		||||
impl Into<schema::user::LoginUser> for Login {
 | 
			
		||||
    fn into(self) -> schema::user::LoginUser {
 | 
			
		||||
        schema::user::LoginUser {
 | 
			
		||||
            username: self.username,
 | 
			
		||||
            password: self.password
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Into<schema::user::Register> for Register {
 | 
			
		||||
    fn into(self) -> schema::user::Register {
 | 
			
		||||
        schema::user::Register {
 | 
			
		||||
            email: self.email,
 | 
			
		||||
            username: self.username,
 | 
			
		||||
            password: self.password
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Serialize, Deserialize)]
 | 
			
		||||
struct Claims {
 | 
			
		||||
    exp: i64,
 | 
			
		||||
    uid: Uuid,
 | 
			
		||||
    role: Role,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[post("/login")]
 | 
			
		||||
pub async fn login(pool: web::Data<DbPool>, login_data: web::Json<Login>) -> Result<impl Responder, ApiError> {
 | 
			
		||||
    let data = login_data.into_inner();
 | 
			
		||||
 | 
			
		||||
    let response = if let Some(user) = web::block(move || {
 | 
			
		||||
        let mut conn = pool.get().expect("couldn't get db connection from pool");
 | 
			
		||||
        schema::login(&mut conn, data.into())
 | 
			
		||||
    })
 | 
			
		||||
    .await?? 
 | 
			
		||||
    {
 | 
			
		||||
        let my_claims = Claims {
 | 
			
		||||
            exp: Utc::now().timestamp() + chrono::Duration::days(7).num_seconds(),
 | 
			
		||||
            uid: user.id,
 | 
			
		||||
            role: user.role,
 | 
			
		||||
        };
 | 
			
		||||
    
 | 
			
		||||
        let secret = "secret";
 | 
			
		||||
        let token = encode(
 | 
			
		||||
            &Header::default(),
 | 
			
		||||
            &my_claims,
 | 
			
		||||
            &EncodingKey::from_secret(secret.as_bytes()))?;
 | 
			
		||||
 | 
			
		||||
        LoginResponse::success(user.id, token)
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        LoginResponse::failure("User doesn't exist or password doesn't match".to_string())
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Ok(HttpResponse::Ok()
 | 
			
		||||
        .content_type(ContentType::json())
 | 
			
		||||
        .body(serde_json::to_string(&response)?)
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[post("/register")]
 | 
			
		||||
pub async fn register(pool: web::Data<DbPool>, register_data: web::Json<Register>) -> Result<impl Responder, ApiError> {
 | 
			
		||||
    let data1 = register_data.clone();
 | 
			
		||||
    let data2 = register_data.clone();
 | 
			
		||||
 | 
			
		||||
    let register_request : schema::user::Register = data2.into();
 | 
			
		||||
    let mut conn1 = pool.get().expect("couldn't get db connection from pool");
 | 
			
		||||
    let mut conn2 = pool.get().expect("couldn't get db connection from pool");
 | 
			
		||||
    
 | 
			
		||||
    let _validation_result = web::block(move || {
 | 
			
		||||
        data1.validate_args((&mut conn1, &mut conn2))
 | 
			
		||||
    }).await??;
 | 
			
		||||
 | 
			
		||||
    let mut conn3 = pool.get().expect("couldn't get db connection from pool");
 | 
			
		||||
    let _register_result = web::block(move || {
 | 
			
		||||
        schema::register(&mut conn3, register_request)
 | 
			
		||||
    }).await??;
 | 
			
		||||
 | 
			
		||||
    return Ok(HttpResponse::Ok())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										8
									
								
								backend-actix/src/request/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								backend-actix/src/request/mod.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
 | 
			
		||||
mod request_data;
 | 
			
		||||
mod responses;
 | 
			
		||||
mod handler;
 | 
			
		||||
mod error;
 | 
			
		||||
 | 
			
		||||
pub use handler::login;
 | 
			
		||||
pub use handler::register;
 | 
			
		||||
							
								
								
									
										28
									
								
								backend-actix/src/request/request_data.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								backend-actix/src/request/request_data.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
			
		||||
use serde::{Serialize, Deserialize};
 | 
			
		||||
use validator::Validate;
 | 
			
		||||
 | 
			
		||||
use crate::schema::user::{unique_email, unique_username};
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize, Clone)]
 | 
			
		||||
pub struct Login {
 | 
			
		||||
    pub username: String,
 | 
			
		||||
    pub password: String
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize, Clone, Validate)]
 | 
			
		||||
pub struct Register {
 | 
			
		||||
    #[validate(
 | 
			
		||||
        length(min = 1),
 | 
			
		||||
        custom(function = "unique_username", arg = "&'v_a mut diesel::PgConnection")
 | 
			
		||||
    )]
 | 
			
		||||
    pub username: String,
 | 
			
		||||
    #[validate(
 | 
			
		||||
        email,
 | 
			
		||||
        custom(function = "unique_email", arg = "&'v_a mut diesel::PgConnection")
 | 
			
		||||
    )]
 | 
			
		||||
    #[validate(email)]
 | 
			
		||||
    pub email: String,
 | 
			
		||||
    #[validate(length(min = 10), must_match = "password_repeat")]
 | 
			
		||||
    pub password: String,
 | 
			
		||||
    pub password_repeat: String,
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								backend-actix/src/request/responses.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								backend-actix/src/request/responses.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
			
		||||
use serde::{Serialize, Deserialize};
 | 
			
		||||
use uuid::Uuid;
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize)]
 | 
			
		||||
pub struct LoginResponse {
 | 
			
		||||
    pub login_result: bool,
 | 
			
		||||
    pub message: Option<String>,
 | 
			
		||||
    pub user_id: Option<Uuid>,
 | 
			
		||||
    pub jwt_token: Option<String>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl LoginResponse {
 | 
			
		||||
    pub fn success(user_id: Uuid, token: String) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            login_result: true,
 | 
			
		||||
            message: None,
 | 
			
		||||
            user_id: Some(user_id),
 | 
			
		||||
            jwt_token: Some(token)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn failure(message: String) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            login_result: false,
 | 
			
		||||
            message: Some(message),
 | 
			
		||||
            user_id: None,
 | 
			
		||||
            jwt_token: None
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								backend-actix/src/schema/error.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								backend-actix/src/schema/error.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
			
		||||
pub struct DatabaseError(pub String);
 | 
			
		||||
 | 
			
		||||
impl From<diesel::result::Error> for DatabaseError {
 | 
			
		||||
    fn from(value: diesel::result::Error) -> Self {
 | 
			
		||||
        DatabaseError(value.to_string())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<argon2::password_hash::Error> for DatabaseError {
 | 
			
		||||
    fn from(value: argon2::password_hash::Error) -> Self {
 | 
			
		||||
        DatabaseError(value.to_string())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								backend-actix/src/schema/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								backend-actix/src/schema/mod.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
pub mod user;
 | 
			
		||||
pub mod error;
 | 
			
		||||
 | 
			
		||||
pub use user::login;
 | 
			
		||||
pub use user::register;
 | 
			
		||||
							
								
								
									
										159
									
								
								backend-actix/src/schema/user.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								backend-actix/src/schema/user.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,159 @@
 | 
			
		||||
use diesel::Connection;
 | 
			
		||||
use serde::{Serialize, Deserialize};
 | 
			
		||||
use uuid::Uuid;
 | 
			
		||||
use diesel::{PgConnection, ExpressionMethods, QueryDsl, RunQueryDsl, table, Insertable, Queryable};
 | 
			
		||||
use diesel_derive_enum::DbEnum;
 | 
			
		||||
use argon2::password_hash::SaltString;
 | 
			
		||||
use argon2::PasswordHash;
 | 
			
		||||
use argon2::PasswordVerifier;
 | 
			
		||||
use argon2::{
 | 
			
		||||
    password_hash::{rand_core::OsRng, PasswordHasher},
 | 
			
		||||
    Argon2,
 | 
			
		||||
};
 | 
			
		||||
use validator::ValidationError;
 | 
			
		||||
 | 
			
		||||
pub use super::error::DatabaseError;
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize, Debug, Insertable, Queryable)]
 | 
			
		||||
#[diesel(table_name = pwd)]
 | 
			
		||||
struct Pwd {
 | 
			
		||||
    user_id: Uuid,
 | 
			
		||||
    password: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize, Debug, Insertable, Queryable)]
 | 
			
		||||
#[diesel(table_name = users)]
 | 
			
		||||
pub struct User {
 | 
			
		||||
    pub id: Uuid,
 | 
			
		||||
    pub username: String,
 | 
			
		||||
    pub email: String,
 | 
			
		||||
    pub role: Role,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Serialize, Deserialize, DbEnum, Clone, Copy, PartialEq)]
 | 
			
		||||
pub enum Role {
 | 
			
		||||
    Admin,
 | 
			
		||||
    User,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct LoginUser {
 | 
			
		||||
    pub username: String,
 | 
			
		||||
    pub password: String
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize)]
 | 
			
		||||
pub struct LoginResult {
 | 
			
		||||
    pub result: bool,
 | 
			
		||||
    pub user: Option<User>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize)]
 | 
			
		||||
pub struct RegisterResult {
 | 
			
		||||
    pub result: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize)]
 | 
			
		||||
pub struct Register {
 | 
			
		||||
    pub username: String,
 | 
			
		||||
    pub email: String,
 | 
			
		||||
    pub password: String
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
table! {
 | 
			
		||||
    users(id) {
 | 
			
		||||
        id -> diesel::sql_types::Uuid,
 | 
			
		||||
        username -> VarChar,
 | 
			
		||||
        email -> VarChar,
 | 
			
		||||
        role -> crate::schema::user::RoleMapping,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table! {
 | 
			
		||||
    pwd(user_id) {
 | 
			
		||||
        user_id -> diesel::sql_types::Uuid,
 | 
			
		||||
        password -> VarChar,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn login(conn: &mut PgConnection, user: LoginUser) -> Result<Option<User>, DatabaseError> {
 | 
			
		||||
    let id: Uuid = users::table
 | 
			
		||||
        .filter(users::username.eq(&user.username))
 | 
			
		||||
        .or_filter(users::email.eq(&user.username))
 | 
			
		||||
        .select(users::id)
 | 
			
		||||
        .first(conn)?;
 | 
			
		||||
 | 
			
		||||
    let pwd: String = pwd::table
 | 
			
		||||
        .filter(pwd::user_id.eq(id))
 | 
			
		||||
        .select(pwd::password)
 | 
			
		||||
        .first(conn)?;
 | 
			
		||||
 | 
			
		||||
    let parsed_hash = PasswordHash::new(&pwd)?;
 | 
			
		||||
 | 
			
		||||
    if Argon2::default()
 | 
			
		||||
        .verify_password(&user.password.as_bytes(), &parsed_hash)
 | 
			
		||||
        .is_ok()
 | 
			
		||||
    {
 | 
			
		||||
        Ok(Some(users::table.find(id).first(conn)?))
 | 
			
		||||
    } else {
 | 
			
		||||
        Ok(None)
 | 
			
		||||
    } 
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn unique_username(username: &String, conn: &mut PgConnection) -> Result<(), ValidationError> {
 | 
			
		||||
    match users::table
 | 
			
		||||
        .count()
 | 
			
		||||
        .filter(users::username.eq(username))
 | 
			
		||||
        .get_result(conn)
 | 
			
		||||
    {
 | 
			
		||||
        Ok(0) => Ok(()),
 | 
			
		||||
        Ok(_) => Err(ValidationError::new("User already exists")),
 | 
			
		||||
        Err(_) => Err(ValidationError::new("Database error while validating user")),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn unique_email(email: &String, conn: &mut PgConnection) -> Result<(), ValidationError> {
 | 
			
		||||
    match users::table
 | 
			
		||||
        .count()
 | 
			
		||||
        .filter(users::email.eq(email))
 | 
			
		||||
        .get_result(conn)
 | 
			
		||||
    {
 | 
			
		||||
        Ok(0) => Ok(()),
 | 
			
		||||
        Ok(_) => Err(ValidationError::new("email already exists")),
 | 
			
		||||
        Err(_) => Err(ValidationError::new(
 | 
			
		||||
            "Database error while validating email",
 | 
			
		||||
        )),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn register(conn: &mut PgConnection, register: Register) -> Result<(), DatabaseError> {
 | 
			
		||||
    let salt = SaltString::generate(&mut OsRng);
 | 
			
		||||
 | 
			
		||||
    let argon2 = Argon2::default();
 | 
			
		||||
 | 
			
		||||
    let password_hash = argon2
 | 
			
		||||
        .hash_password(register.password.as_bytes(), &salt)?
 | 
			
		||||
        .to_string();
 | 
			
		||||
 | 
			
		||||
    conn.transaction(|c| {
 | 
			
		||||
        let id = Uuid::new_v4();
 | 
			
		||||
 | 
			
		||||
        diesel::insert_into(users::table)
 | 
			
		||||
            .values(User {
 | 
			
		||||
                id: id.clone(),
 | 
			
		||||
                username: register.username,
 | 
			
		||||
                email: register.email,
 | 
			
		||||
                role: Role::User,
 | 
			
		||||
            })
 | 
			
		||||
            .execute(c)?;
 | 
			
		||||
 | 
			
		||||
        diesel::insert_into(pwd::table)
 | 
			
		||||
            .values(Pwd {
 | 
			
		||||
                user_id: id,
 | 
			
		||||
                password: password_hash,
 | 
			
		||||
            })
 | 
			
		||||
            .execute(c)?;
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user