forked from Roflin/gamenight
193 lines
5.6 KiB
Rust
193 lines
5.6 KiB
Rust
use crate::models::{
|
|
login::Login, registration::Registration, token::Token, user::User, user_id::UserId,
|
|
};
|
|
use crate::request::{authorization::get_token, error::ApiError};
|
|
use actix_web::{get, http::header::ContentType, post, web, HttpResponse, Responder};
|
|
use gamenight_database::user::{count_users_with_email, count_users_with_username};
|
|
use gamenight_database::{DbPool, GetConnection};
|
|
use serde::{Deserialize, Serialize};
|
|
use serde_json;
|
|
use uuid::Uuid;
|
|
use validator::{Validate, ValidateArgs, ValidationError};
|
|
|
|
use super::authorization::AuthUser;
|
|
|
|
impl From<Login> for gamenight_database::user::LoginUser {
|
|
fn from(val: Login) -> Self {
|
|
gamenight_database::user::LoginUser {
|
|
username: val.username,
|
|
password: val.password,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Registration> for gamenight_database::user::Register {
|
|
fn from(val: Registration) -> Self {
|
|
gamenight_database::user::Register {
|
|
email: val.email,
|
|
username: val.username,
|
|
password: val.password,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct RegisterContext<'v_a> {
|
|
pub pool: &'v_a DbPool,
|
|
}
|
|
|
|
pub fn unique_username(
|
|
username: &String,
|
|
context: &RegisterContext,
|
|
) -> Result<(), ValidationError> {
|
|
let mut conn = context.pool.get_conn();
|
|
|
|
match count_users_with_username(&mut conn, username) {
|
|
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, context: &RegisterContext) -> Result<(), ValidationError> {
|
|
let mut conn = context.pool.get_conn();
|
|
|
|
match count_users_with_email(&mut conn, email) {
|
|
Ok(0) => Ok(()),
|
|
Ok(_) => Err(ValidationError::new("email already exists")),
|
|
Err(_) => Err(ValidationError::new(
|
|
"Database error while validating email",
|
|
)),
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Validate)]
|
|
#[validate(context = RegisterContext::<'v_a>)]
|
|
pub struct ValidatableRegistration {
|
|
#[validate(length(min = 1), custom(function = "unique_username", use_context))]
|
|
pub username: String,
|
|
#[validate(email, custom(function = "unique_email", use_context))]
|
|
pub email: String,
|
|
#[validate(length(min = 10), must_match(other = "password_repeat",))]
|
|
pub password: String,
|
|
pub password_repeat: String,
|
|
}
|
|
|
|
impl From<Registration> for ValidatableRegistration {
|
|
fn from(value: Registration) -> Self {
|
|
Self {
|
|
username: value.username,
|
|
email: value.email,
|
|
password: value.password,
|
|
password_repeat: value.password_repeat,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[get("/token")]
|
|
pub async fn login(
|
|
pool: web::Data<DbPool>,
|
|
login_data: web::Json<Login>,
|
|
) -> Result<impl Responder, ApiError> {
|
|
let data = login_data.into_inner();
|
|
|
|
if let Ok(Some(user)) = web::block(move || {
|
|
let mut conn = pool.get_conn();
|
|
gamenight_database::login(&mut conn, data.into())
|
|
})
|
|
.await?
|
|
{
|
|
let token = get_token(&user)?;
|
|
let response = Token {
|
|
jwt_token: Some(token),
|
|
};
|
|
Ok(HttpResponse::Ok()
|
|
.content_type(ContentType::json())
|
|
.body(serde_json::to_string(&response)?))
|
|
} else {
|
|
Err(ApiError {
|
|
status: 401,
|
|
message: "User doesn't exist or password doesn't match".to_string(),
|
|
})
|
|
}
|
|
}
|
|
|
|
#[post("/token")]
|
|
pub async fn refresh(user: AuthUser) -> Result<impl Responder, ApiError> {
|
|
let new_token = get_token(&user.0)?;
|
|
let response = Token {
|
|
jwt_token: Some(new_token),
|
|
};
|
|
Ok(HttpResponse::Ok()
|
|
.content_type(ContentType::json())
|
|
.body(serde_json::to_string(&response)?))
|
|
}
|
|
|
|
#[post("/user")]
|
|
pub async fn register(
|
|
pool: web::Data<DbPool>,
|
|
register_data: web::Json<Registration>,
|
|
) -> Result<impl Responder, ApiError> {
|
|
web::block(move || -> Result<(), ApiError> {
|
|
let validatable_registration: ValidatableRegistration = register_data.clone().into();
|
|
validatable_registration.validate_with_args(&RegisterContext { pool: &pool })?;
|
|
let register_request = register_data.into_inner().into();
|
|
let mut conn = pool.get_conn();
|
|
gamenight_database::register(&mut conn, register_request)?;
|
|
Ok(())
|
|
})
|
|
.await??;
|
|
|
|
Ok(HttpResponse::Ok())
|
|
}
|
|
|
|
impl From<gamenight_database::user::User> for User {
|
|
fn from(value: gamenight_database::user::User) -> Self {
|
|
Self {
|
|
id: value.id.to_string(),
|
|
username: value.username,
|
|
email: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[get("/user")]
|
|
pub async fn get_user(
|
|
pool: web::Data<DbPool>,
|
|
_user: AuthUser,
|
|
user_info: web::Json<UserId>,
|
|
) -> Result<impl Responder, ApiError> {
|
|
let mut conn = pool.get_conn();
|
|
|
|
let user = gamenight_database::user::get_user(
|
|
&mut conn,
|
|
Uuid::parse_str(&user_info.into_inner().user_id)?,
|
|
)?;
|
|
|
|
Ok(HttpResponse::Ok()
|
|
.content_type(ContentType::json())
|
|
.body(serde_json::to_string(&user)?))
|
|
}
|
|
|
|
#[get("/user")]
|
|
pub async fn get_user_unauthenticated(
|
|
_path: web::Path<UserId>,
|
|
) -> Result<impl Responder, ApiError> {
|
|
Ok(HttpResponse::Forbidden())
|
|
}
|
|
|
|
#[get("/users")]
|
|
|
|
pub async fn get_users(
|
|
pool: web::Data<DbPool>,
|
|
_user: AuthUser,
|
|
) -> Result<impl Responder, ApiError> {
|
|
let mut conn = pool.get_conn();
|
|
let users = gamenight_database::user::get_users(&mut conn)?;
|
|
|
|
let model: Vec<User> = users.into_iter().map(Into::into).collect();
|
|
|
|
Ok(HttpResponse::Ok()
|
|
.content_type(ContentType::json())
|
|
.body(serde_json::to_string(&model)?))
|
|
}
|