This commit is contained in:
core 2023-04-02 15:47:50 -04:00
parent 3a702ed3a5
commit 2d7607bbc1
Signed by: core
GPG Key ID: FDBF740DADDCEECF
13 changed files with 171 additions and 14 deletions

View File

@ -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(())
}

View File

@ -15,6 +15,7 @@ pub mod routes;
pub mod error;
pub mod tokens;
pub mod timers;
pub mod magic_link;
pub struct AppState {
pub conn: DatabaseConnection
@ -60,6 +61,8 @@ async fn main() -> Result<(), Box<dyn Error>> {
).into()
}))
.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?;
Ok(())

View File

@ -1 +1 @@
pub mod auth;
pub mod v1;

View File

@ -3,11 +3,12 @@ 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::user::Entity as UserEntity;
use trifid_api_entities::user;
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_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"),
user: 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 {

View File

@ -0,0 +1,2 @@
pub mod auth;
pub mod signup;

View File

@ -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 {}
})
}

View File

@ -0,0 +1,6 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.2
pub mod prelude;
pub mod magic_link;
pub mod user;

View File

@ -9,7 +9,6 @@ pub struct Model {
pub id: String,
#[sea_orm(unique)]
pub email: String,
pub password_hash: String,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]

View File

@ -1,6 +1 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.2
pub mod prelude;
pub mod user;
pub mod magic_link;
pub mod entity;

View File

@ -13,7 +13,6 @@ impl MigrationTrait for Migration {
.if_not_exists()
.col(ColumnDef::new(User::Id).string().not_null().primary_key())
.col(ColumnDef::new(User::Email).string().not_null().unique_key())
.col(ColumnDef::new(User::PasswordHash).string().not_null())
.to_owned()
).await
}
@ -28,6 +27,5 @@ impl MigrationTrait for Migration {
pub enum User {
Table,
Id,
Email,
PasswordHash,
Email
}