use actix_web::http::header::ContentType; use actix_web::{get, post, web, HttpResponse, Responder}; use gamenight_database::user::{count_users_with_email, count_users_with_username}; use serde::{Deserialize, Serialize}; use uuid::Uuid; use validator::{Validate, ValidateArgs, ValidationError}; use crate::models::login::Login; use crate::models::registration::Registration; use crate::models::token::Token; use crate::models::user::User; use crate::request::error::ApiError; use crate::request::authorization::get_token; use serde_json; use gamenight_database::{DbPool, GetConnection}; use super::authorization::AuthUser; impl From for gamenight_database::user::LoginUser { fn from(val: Login) -> Self { gamenight_database::user::LoginUser { username: val.username, password: val.password } } } impl From 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 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, login_data: web::Json) -> Result { 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("/user")] pub async fn register(pool: web::Data, register_data: web::Json) -> Result { 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()) } #[derive(Deserialize)] struct UserInfo { pub uuid: String } impl From for User { fn from(value: gamenight_database::user::User) -> Self { Self { id: Some(value.id.to_string()), username: value.username, email: None, } } } #[get("/user/{user_id}")] pub async fn get_user(pool: web::Data, _user: AuthUser, path: web::Path) -> Result { let mut conn = pool.get_conn(); let user = gamenight_database::user::get_user(&mut conn, Uuid::parse_str(&path.uuid)?)?; Ok(HttpResponse::Ok() .content_type(ContentType::json()) .body(serde_json::to_string(&user)?)) } #[get("/user/{user_id}")] pub async fn get_user_unauthenticated(_path: web::Path) -> Result { Ok(HttpResponse::Forbidden()) }