Added Login and Register handlers for actix backend

This commit is contained in:
2023-03-24 22:28:18 +01:00
parent d961896242
commit 1c8110cdb0
21 changed files with 331 additions and 114 deletions

View File

@@ -1,5 +1,5 @@
use std::fmt::{Display, Formatter, Result};
use actix_web::{ResponseError, error::BlockingError};
use actix_web::{ResponseError, error::BlockingError, HttpResponse, http::{header::ContentType, StatusCode}};
use serde::{Serialize, Deserialize};
use validator::ValidationErrors;
@@ -7,22 +7,32 @@ use crate::schema::error::DatabaseError;
#[derive(Serialize, Deserialize, Debug)]
pub struct ApiError {
pub error: String
#[serde(skip_serializing)]
pub status: u16,
pub message: String
}
impl Display for ApiError {
// This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "{}", self.error)
write!(f, "{}", self.message)
}
}
impl ResponseError for ApiError { }
impl ResponseError for ApiError {
fn error_response(&self) -> HttpResponse {
HttpResponse::build(StatusCode::from_u16(self.status).unwrap())
.content_type(ContentType::json())
.body(serde_json::to_string(&self).unwrap())
}
}
impl From<DatabaseError> for ApiError {
fn from(value: DatabaseError) -> Self {
ApiError {
error: value.0
//Todo, split this in unrecoverable and schema error
status: 500,
message: value.0
}
}
}
@@ -30,7 +40,8 @@ impl From<DatabaseError> for ApiError {
impl From<BlockingError> for ApiError {
fn from(value: BlockingError) -> Self {
ApiError {
error: value.to_string()
status: 500,
message: value.to_string()
}
}
}
@@ -38,15 +49,17 @@ impl From<BlockingError> for ApiError {
impl From<serde_json::Error> for ApiError {
fn from(value: serde_json::Error) -> Self {
ApiError {
error: value.to_string()
status: 500,
message: value.to_string()
}
}
}
impl From<jsonwebtoken::errors::Error> for ApiError {
fn from(value: jsonwebtoken::errors::Error) -> Self {
ApiError {
error: value.to_string()
ApiError {
status: 500,
message: value.to_string()
}
}
}
@@ -54,7 +67,8 @@ impl From<jsonwebtoken::errors::Error> for ApiError {
impl From<ValidationErrors> for ApiError {
fn from(value: ValidationErrors) -> Self {
ApiError {
error: value.to_string()
status: 422,
message: value.to_string()
}
}
}

View File

@@ -6,7 +6,7 @@ use serde::{Serialize, Deserialize};
use uuid::Uuid;
use validator::ValidateArgs;
use crate::DbPool;
use crate::request::request_data::{Login, Register};
use crate::request::requests::{Login, Register};
use crate::request::error::ApiError;
use crate::request::responses::LoginResponse;
use crate::schema::user::Role;
@@ -83,6 +83,7 @@ pub async fn register(pool: web::Data<DbPool>, register_data: web::Json<Register
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??;

View File

@@ -1,5 +1,5 @@
mod request_data;
mod requests;
mod responses;
mod handler;
mod error;

View File

@@ -1,5 +1,6 @@
pub mod user;
pub mod error;
pub mod schema;
pub use user::login;
pub use user::register;
pub use user::register;

View File

@@ -0,0 +1,80 @@
// @generated automatically by Diesel CLI.
pub mod sql_types {
#[derive(diesel::sql_types::SqlType)]
#[diesel(postgres_type(name = "role"))]
pub struct Role;
}
diesel::table! {
gamenight (id) {
id -> Uuid,
name -> Varchar,
datetime -> Timestamptz,
owner_id -> Uuid,
}
}
diesel::table! {
gamenight_gamelist (gamenight_id, game_id) {
gamenight_id -> Uuid,
game_id -> Uuid,
}
}
diesel::table! {
gamenight_participants (gamenight_id, user_id) {
gamenight_id -> Uuid,
user_id -> Uuid,
}
}
diesel::table! {
known_games (id) {
id -> Uuid,
name -> Varchar,
}
}
diesel::table! {
pwd (user_id) {
user_id -> Uuid,
password -> Varchar,
}
}
diesel::table! {
registration_tokens (id) {
id -> Uuid,
token -> Bpchar,
single_use -> Bool,
expires -> Nullable<Timestamptz>,
}
}
diesel::table! {
use diesel::sql_types::*;
use super::sql_types::Role;
users (id) {
id -> Uuid,
username -> Varchar,
email -> Varchar,
role -> Role,
}
}
diesel::joinable!(gamenight -> users (owner_id));
diesel::joinable!(gamenight_gamelist -> known_games (game_id));
diesel::joinable!(gamenight_participants -> users (user_id));
diesel::joinable!(pwd -> users (user_id));
diesel::allow_tables_to_appear_in_same_query!(
gamenight,
gamenight_gamelist,
gamenight_participants,
known_games,
pwd,
registration_tokens,
users,
);

View File

@@ -1,7 +1,7 @@
use diesel::Connection;
use serde::{Serialize, Deserialize};
use uuid::Uuid;
use diesel::{PgConnection, ExpressionMethods, QueryDsl, RunQueryDsl, table, Insertable, Queryable};
use diesel::{PgConnection, ExpressionMethods, QueryDsl, RunQueryDsl, Insertable, Queryable};
use diesel_derive_enum::DbEnum;
use argon2::password_hash::SaltString;
use argon2::PasswordHash;
@@ -11,6 +11,7 @@ use argon2::{
Argon2,
};
use validator::ValidationError;
use super::schema::{pwd, users};
pub use super::error::DatabaseError;
@@ -30,10 +31,11 @@ pub struct User {
pub role: Role,
}
#[derive(Debug, Serialize, Deserialize, DbEnum, Clone, Copy, PartialEq)]
#[derive(DbEnum, Debug, Serialize, Deserialize, Clone, Copy, PartialEq)]
#[ExistingTypePath = "crate::schema::schema::sql_types::Role"]
pub enum Role {
Admin,
User,
User
}
pub struct LoginUser {
@@ -59,23 +61,6 @@ pub struct Register {
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))