signup
This commit is contained in:
parent
3a702ed3a5
commit
2d7607bbc1
|
@ -0,0 +1,9 @@
|
||||||
|
use std::error::Error;
|
||||||
|
use log::info;
|
||||||
|
|
||||||
|
pub fn send_magic_link(token: &str) -> Result<(), Box<dyn Error>> {
|
||||||
|
// TODO: actually do this
|
||||||
|
info!("sent magic link {}", token);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ pub mod routes;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod tokens;
|
pub mod tokens;
|
||||||
pub mod timers;
|
pub mod timers;
|
||||||
|
pub mod magic_link;
|
||||||
|
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
pub conn: DatabaseConnection
|
pub conn: DatabaseConnection
|
||||||
|
@ -60,6 +61,8 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
).into()
|
).into()
|
||||||
}))
|
}))
|
||||||
.wrap(RequestIdentifier::with_generator(random_id_no_id))
|
.wrap(RequestIdentifier::with_generator(random_id_no_id))
|
||||||
|
.service(routes::v1::auth::magic_link::magic_link_request)
|
||||||
|
.service(routes::v1::signup::signup_request)
|
||||||
}).bind(CONFIG.server.bind)?.run().await?;
|
}).bind(CONFIG.server.bind)?.run().await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
pub mod auth;
|
pub mod v1;
|
|
@ -3,11 +3,12 @@ use actix_web::web::{Data, Json};
|
||||||
use log::error;
|
use log::error;
|
||||||
use sea_orm::{ActiveModelTrait, ColumnTrait, DatabaseConnection, EntityTrait, IntoActiveModel, QueryFilter};
|
use sea_orm::{ActiveModelTrait, ColumnTrait, DatabaseConnection, EntityTrait, IntoActiveModel, QueryFilter};
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use trifid_api_entities::user::Entity as UserEntity;
|
use trifid_api_entities::entity::user::Entity as UserEntity;
|
||||||
use trifid_api_entities::user;
|
use trifid_api_entities::entity::user;
|
||||||
use crate::AppState;
|
use crate::AppState;
|
||||||
use crate::config::CONFIG;
|
use crate::config::CONFIG;
|
||||||
use crate::error::{APIError, APIErrorsResponse};
|
use crate::error::{APIError, APIErrorsResponse};
|
||||||
|
use crate::magic_link::send_magic_link;
|
||||||
use crate::timers::expires_in_seconds;
|
use crate::timers::expires_in_seconds;
|
||||||
use crate::tokens::random_token;
|
use crate::tokens::random_token;
|
||||||
|
|
||||||
|
@ -59,12 +60,28 @@ pub async fn magic_link_request(data: Data<AppState>, req: Json<MagicLinkRequest
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let model = trifid_api_entities::magic_link::Model {
|
let model = trifid_api_entities::entity::magic_link::Model {
|
||||||
id: random_token("ml"),
|
id: random_token("ml"),
|
||||||
user: user.id,
|
user: user.id,
|
||||||
expires_on: expires_in_seconds(CONFIG.tokens.magic_link_expiry_time_seconds) as i64,
|
expires_on: expires_in_seconds(CONFIG.tokens.magic_link_expiry_time_seconds) as i64,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
match send_magic_link(&model.id) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
error!("error sending magic link: {}", e);
|
||||||
|
return HttpResponse::InternalServerError().json(APIErrorsResponse {
|
||||||
|
errors: vec![
|
||||||
|
APIError {
|
||||||
|
code: "ERR_ML_ERROR".to_string(),
|
||||||
|
message: "There was an error sending the magic link email, please try again later.".to_string(),
|
||||||
|
path: None,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let active_model = model.into_active_model();
|
let active_model = model.into_active_model();
|
||||||
|
|
||||||
match active_model.insert(&data.conn).await {
|
match active_model.insert(&data.conn).await {
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod auth;
|
||||||
|
pub mod signup;
|
|
@ -0,0 +1,128 @@
|
||||||
|
use actix_web::{HttpResponse, post};
|
||||||
|
use actix_web::web::{Data, Json};
|
||||||
|
use log::error;
|
||||||
|
use sea_orm::{ActiveModelTrait, ColumnTrait, DatabaseConnection, EntityTrait, IntoActiveModel, QueryFilter};
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use trifid_api_entities::entity::user::Entity as UserEntity;
|
||||||
|
use trifid_api_entities::entity::user;
|
||||||
|
use crate::AppState;
|
||||||
|
use crate::config::CONFIG;
|
||||||
|
use crate::error::{APIError, APIErrorsResponse};
|
||||||
|
use crate::magic_link::send_magic_link;
|
||||||
|
use crate::timers::expires_in_seconds;
|
||||||
|
use crate::tokens::{random_id, random_token};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct SignupRequest {
|
||||||
|
pub email: String
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct SignupResponse {
|
||||||
|
pub data: Option<SignupResponseData>,
|
||||||
|
pub metadata: SignupResponseMetadata
|
||||||
|
}
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct SignupResponseData {}
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct SignupResponseMetadata {}
|
||||||
|
|
||||||
|
#[post("/v1/signup")]
|
||||||
|
pub async fn signup_request(data: Data<AppState>, req: Json<SignupRequest>) -> HttpResponse {
|
||||||
|
let user: Vec<user::Model> = match UserEntity::find().filter(user::Column::Email.eq(&req.email)).all(&data.conn).await {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(e) => {
|
||||||
|
error!("database error: {}", e);
|
||||||
|
return HttpResponse::InternalServerError().json(APIErrorsResponse {
|
||||||
|
errors: vec![
|
||||||
|
APIError {
|
||||||
|
code: "ERR_DB_ERROR".to_string(),
|
||||||
|
message: "There was an error with the database request, please try again later.".to_string(),
|
||||||
|
path: None,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if user.is_empty() {
|
||||||
|
return HttpResponse::Unauthorized().json(APIErrorsResponse {
|
||||||
|
errors: vec![
|
||||||
|
APIError {
|
||||||
|
code: "ERR_USER_EXISTS".to_string(),
|
||||||
|
message: "That user already exists.".to_string(),
|
||||||
|
path: None,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let model = user::Model {
|
||||||
|
id: random_id("user"),
|
||||||
|
email: req.email.clone()
|
||||||
|
};
|
||||||
|
let id = model.id.clone();
|
||||||
|
|
||||||
|
let active_model = model.into_active_model();
|
||||||
|
|
||||||
|
match active_model.insert(&data.conn).await {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
error!("database error: {}", e);
|
||||||
|
return HttpResponse::InternalServerError().json(APIErrorsResponse {
|
||||||
|
errors: vec![
|
||||||
|
APIError {
|
||||||
|
code: "ERR_DB_ERROR".to_string(),
|
||||||
|
message: "There was an error with the database request, please try again later.".to_string(),
|
||||||
|
path: None,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let model = trifid_api_entities::entity::magic_link::Model {
|
||||||
|
id: random_token("ml"),
|
||||||
|
user: id,
|
||||||
|
expires_on: expires_in_seconds(CONFIG.tokens.magic_link_expiry_time_seconds) as i64,
|
||||||
|
};
|
||||||
|
|
||||||
|
match send_magic_link(&model.id) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
error!("error sending magic link: {}", e);
|
||||||
|
return HttpResponse::InternalServerError().json(APIErrorsResponse {
|
||||||
|
errors: vec![
|
||||||
|
APIError {
|
||||||
|
code: "ERR_ML_ERROR".to_string(),
|
||||||
|
message: "There was an error sending the magic link email, please try again later.".to_string(),
|
||||||
|
path: None,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let active_model = model.into_active_model();
|
||||||
|
|
||||||
|
match active_model.insert(&data.conn).await {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
error!("database error: {}", e);
|
||||||
|
return HttpResponse::InternalServerError().json(APIErrorsResponse {
|
||||||
|
errors: vec![
|
||||||
|
APIError {
|
||||||
|
code: "ERR_DB_ERROR".to_string(),
|
||||||
|
message: "There was an error with the database request, please try again later.".to_string(),
|
||||||
|
path: None,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpResponse::Ok().json(SignupResponse {
|
||||||
|
data: None,
|
||||||
|
metadata: SignupResponseMetadata {}
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.2
|
||||||
|
|
||||||
|
pub mod prelude;
|
||||||
|
|
||||||
|
pub mod magic_link;
|
||||||
|
pub mod user;
|
|
@ -9,7 +9,6 @@ pub struct Model {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
#[sea_orm(unique)]
|
#[sea_orm(unique)]
|
||||||
pub email: String,
|
pub email: String,
|
||||||
pub password_hash: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
|
@ -1,6 +1 @@
|
||||||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.2
|
pub mod entity;
|
||||||
|
|
||||||
pub mod prelude;
|
|
||||||
|
|
||||||
pub mod user;
|
|
||||||
pub mod magic_link;
|
|
|
@ -13,7 +13,6 @@ impl MigrationTrait for Migration {
|
||||||
.if_not_exists()
|
.if_not_exists()
|
||||||
.col(ColumnDef::new(User::Id).string().not_null().primary_key())
|
.col(ColumnDef::new(User::Id).string().not_null().primary_key())
|
||||||
.col(ColumnDef::new(User::Email).string().not_null().unique_key())
|
.col(ColumnDef::new(User::Email).string().not_null().unique_key())
|
||||||
.col(ColumnDef::new(User::PasswordHash).string().not_null())
|
|
||||||
.to_owned()
|
.to_owned()
|
||||||
).await
|
).await
|
||||||
}
|
}
|
||||||
|
@ -28,6 +27,5 @@ impl MigrationTrait for Migration {
|
||||||
pub enum User {
|
pub enum User {
|
||||||
Table,
|
Table,
|
||||||
Id,
|
Id,
|
||||||
Email,
|
Email
|
||||||
PasswordHash,
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue