Add sign up
This commit is contained in:
parent
70cb64a2e0
commit
9805a96510
@ -15,6 +15,7 @@ use uuid::Uuid;
|
||||
|
||||
mod email_check;
|
||||
mod sign_in;
|
||||
mod sign_up;
|
||||
mod social_auth;
|
||||
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
@ -27,7 +28,8 @@ pub fn configure(http_client: reqwest::Client, config: &mut ServiceConfig) {
|
||||
config
|
||||
.service(email_check::email_check)
|
||||
.service(oauth)
|
||||
.service(sign_in::sign_in);
|
||||
.service(sign_in::sign_in)
|
||||
.service(sign_up::sign_up);
|
||||
social_auth::configure(http_client, config);
|
||||
}
|
||||
|
||||
|
@ -77,25 +77,27 @@ async fn try_sign_in(
|
||||
tracing::error!("Invalid email address: {e}");
|
||||
return Err(JsonError::new("Please provide a valid email address."));
|
||||
}
|
||||
if !config.enable_signup && !has_workspace_invites(&email, &mut *db).await? {
|
||||
return Err(JsonError::new(
|
||||
"New account creation is disabled. Please contact your site administrator",
|
||||
));
|
||||
}
|
||||
|
||||
let (user, was_created) = match Users::find()
|
||||
match Users::find()
|
||||
.filter(entities::users::Column::Email.eq(&email))
|
||||
.one(&mut *db)
|
||||
.await
|
||||
{
|
||||
Ok(Some(user)) => (user, false),
|
||||
Ok(None) if !config.enable_signup && !has_workspace_invites(&email, &mut *db).await? => {
|
||||
return Err(JsonError::new(
|
||||
"New account creation is disabled. Please contact your site administrator",
|
||||
));
|
||||
}
|
||||
Ok(None) => (create_user(&req, &email, &password, &mut *db).await?, true),
|
||||
Ok(None) => {}
|
||||
Ok(Some(user)) => return Err(JsonError::new("User with this email already exists")),
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to load user for sign-in: {e}");
|
||||
return Ok(Error::DatabaseError.error_response());
|
||||
}
|
||||
};
|
||||
|
||||
let user = create_user(&req, &email, &password, db).await?;
|
||||
|
||||
let user_id = user.id;
|
||||
let mut user: UserModel = user.into();
|
||||
|
||||
|
144
crates/jet-api/src/http/api/authentication/sign_up.rs
Normal file
144
crates/jet-api/src/http/api/authentication/sign_up.rs
Normal file
@ -0,0 +1,144 @@
|
||||
use actix_jwt_session::SessionStorage;
|
||||
use actix_web::web::{Data, Json};
|
||||
use actix_web::ResponseError;
|
||||
use actix_web::{post, HttpRequest, HttpResponse};
|
||||
use entities::prelude::Users;
|
||||
use entities::users::ActiveModel as UserModel;
|
||||
use jet_contract::event_bus::{Msg, SignInMedium, Topic, UserMsg};
|
||||
use jet_contract::UserId;
|
||||
use reqwest::StatusCode;
|
||||
use rumqttc::QoS;
|
||||
use sea_orm::DatabaseConnection;
|
||||
use sea_orm::*;
|
||||
use validators::models::Host;
|
||||
use validators::prelude::*;
|
||||
|
||||
use crate::config::ApplicationConfig;
|
||||
use crate::extractors::RequireInstanceConfigured;
|
||||
use crate::http::api::authentication::{auth_http_response, create_user, has_workspace_invites};
|
||||
use crate::models::{Error, JsonError};
|
||||
|
||||
#[post("/sign-up")]
|
||||
pub async fn sign_up(
|
||||
_: RequireInstanceConfigured,
|
||||
req: HttpRequest,
|
||||
payload: Json<SignUpInput>,
|
||||
db: Data<DatabaseConnection>,
|
||||
config: Data<ApplicationConfig>,
|
||||
event_bus: Data<crate::EventBusClient>,
|
||||
session: Data<SessionStorage>,
|
||||
) -> Result<HttpResponse, JsonError> {
|
||||
let mut t = db.begin().await.map_err(|e| {
|
||||
tracing::error!("Failed to start transaction for sign-in: {e}");
|
||||
Error::DatabaseError
|
||||
})?;
|
||||
let res = try_sign_in(req, payload, &mut t, config, event_bus, session).await?;
|
||||
t.commit().await.map_err(|e| {
|
||||
tracing::error!("Failed to commit transaction for sign-in: {e}");
|
||||
Error::DatabaseError
|
||||
})?;
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
async fn try_sign_in(
|
||||
req: HttpRequest,
|
||||
payload: Json<SignUpInput>,
|
||||
db: &mut DatabaseTransaction,
|
||||
config: Data<ApplicationConfig>,
|
||||
event_bus: Data<crate::EventBusClient>,
|
||||
session: Data<SessionStorage>,
|
||||
) -> Result<HttpResponse, JsonError> {
|
||||
if payload.email.trim().is_empty() || payload.password.trim().is_empty() {
|
||||
return Err(JsonError::new("Both email and password are required"));
|
||||
}
|
||||
let email = payload.email.trim().to_lowercase();
|
||||
|
||||
#[derive(Validator)]
|
||||
#[validator(email(
|
||||
comment(Allow),
|
||||
ip(Allow),
|
||||
local(Allow),
|
||||
at_least_two_labels(Allow),
|
||||
non_ascii(Allow)
|
||||
))]
|
||||
pub struct EmailAllowComment {
|
||||
pub local_part: String,
|
||||
pub need_quoted: bool,
|
||||
pub domain_part: Host,
|
||||
pub comment_before_local_part: Option<String>,
|
||||
pub comment_after_local_part: Option<String>,
|
||||
pub comment_before_domain_part: Option<String>,
|
||||
pub comment_after_domain_part: Option<String>,
|
||||
}
|
||||
|
||||
let password = payload.password.clone();
|
||||
if let Err(e) = EmailAllowComment::validate_str(&email) {
|
||||
tracing::error!("Invalid email address: {e}");
|
||||
return Err(JsonError::new("Please provide a valid email address."));
|
||||
}
|
||||
|
||||
let (user, was_created) = match Users::find()
|
||||
.filter(entities::users::Column::Email.eq(&email))
|
||||
.one(&mut *db)
|
||||
.await
|
||||
{
|
||||
Ok(Some(user)) => (user, false),
|
||||
Ok(None) if !config.enable_signup && !has_workspace_invites(&email, &mut *db).await? => {
|
||||
return Err(JsonError::new(
|
||||
"New account creation is disabled. Please contact your site administrator",
|
||||
));
|
||||
}
|
||||
Ok(None) => (create_user(&req, &email, &password, &mut *db).await?, true),
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to load user for sign-in: {e}");
|
||||
return Ok(Error::DatabaseError.error_response());
|
||||
}
|
||||
};
|
||||
|
||||
let user_id = user.id;
|
||||
let mut user: UserModel = user.into();
|
||||
|
||||
let (ip, user_agent, _current_site) = crate::utils::extract_req_info(&req)?;
|
||||
|
||||
user.is_active = Set(true);
|
||||
user.last_active = Set(Some(chrono::Utc::now().fixed_offset()));
|
||||
user.last_login_time = Set(Some(chrono::Utc::now().fixed_offset()));
|
||||
user.last_login_ip = Set(ip.clone());
|
||||
user.last_login_uagent = Set(user_agent.clone());
|
||||
user.token_updated_at = Set(Some(chrono::Utc::now().fixed_offset()));
|
||||
let user = Users::update(user).exec(&mut *db).await.map_err(|e| {
|
||||
tracing::error!("Failed to update account for {email:?}: {e}");
|
||||
Error::DatabaseError
|
||||
})?;
|
||||
crate::utils::invites_to_membership(user_id, &email, None, &mut *db).await?;
|
||||
|
||||
if let Err(e) = event_bus
|
||||
.publish(
|
||||
Topic::User,
|
||||
Msg::User(UserMsg::SignIn {
|
||||
user_id: UserId::new(user_id),
|
||||
email,
|
||||
user_agent,
|
||||
ip,
|
||||
medium: SignInMedium::Email,
|
||||
first_time: false,
|
||||
}),
|
||||
QoS::AtLeastOnce,
|
||||
true,
|
||||
)
|
||||
.await
|
||||
{
|
||||
tracing::warn!("Failed to publish sign-in msg after sign in: {e}");
|
||||
};
|
||||
|
||||
auth_http_response(user, session, StatusCode::OK)
|
||||
.await
|
||||
.map_err(JsonError::new)
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct SignUpInput {
|
||||
email: String,
|
||||
password: String,
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
use actix_web::{HttpRequest, HttpResponse};
|
||||
use actix_web::HttpRequest;
|
||||
use chrono::Utc;
|
||||
use entities::project_member_invites::{
|
||||
Column as ProjectMemberInviteColumn, Model as ProjectMemberInvite,
|
||||
@ -21,7 +21,6 @@ use tracing::error;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::http::OAuthError;
|
||||
use crate::models::JsonError;
|
||||
use crate::{http::AuthError, models::Error};
|
||||
|
||||
pub fn extract_req_info(req: &HttpRequest) -> Result<(String, String, String), Error> {
|
||||
|
Loading…
Reference in New Issue
Block a user