diff --git a/crates/api/src/local_user.rs b/crates/api/src/local_user.rs
index f4686d814..e4878683a 100644
--- a/crates/api/src/local_user.rs
+++ b/crates/api/src/local_user.rs
@@ -10,6 +10,9 @@ use lemmy_api_common::{
is_admin,
password_length_check,
person::*,
+ send_email_verification_success,
+ send_password_reset_email,
+ send_verification_email,
};
use lemmy_db_schema::{
diesel_option_overwrite,
@@ -47,14 +50,12 @@ use lemmy_db_views_actor::{
};
use lemmy_utils::{
claims::Claims,
- email::send_email,
location_info,
- utils::{generate_random_string, is_valid_display_name, is_valid_matrix_id, naive_from_unix},
+ utils::{is_valid_display_name, is_valid_matrix_id, naive_from_unix},
ConnectionId,
LemmyError,
};
use lemmy_websocket::{
- email::send_verification_email,
messages::{CaptchaItem, SendAllMessage},
LemmyContext,
UserOperation,
@@ -96,6 +97,10 @@ impl Perform for Login {
return Err(LemmyError::from_message("email_not_verified"));
}
+ if site.require_application && !local_user_view.local_user.accepted_application {
+ return Err(LemmyError::from_message("registration_application_pending"));
+ }
+
// Return the jwt
Ok(LoginResponse {
jwt: Some(
@@ -187,7 +192,8 @@ impl Perform for SaveUserSettings {
local_user_view.local_user.id,
email,
&local_user_view.person.name,
- context,
+ context.pool(),
+ &context.settings(),
)
.await?;
None
@@ -774,31 +780,8 @@ impl Perform for PasswordReset {
.map_err(LemmyError::from)
.map_err(|e| e.with_message("couldnt_find_that_username_or_email"))?;
- // Generate a random token
- let token = generate_random_string();
-
- // Insert the row
- let token2 = token.clone();
- let local_user_id = local_user_view.local_user.id;
- blocking(context.pool(), move |conn| {
- PasswordResetRequest::create_token(conn, local_user_id, &token2)
- })
- .await??;
-
// Email the pure token to the user.
- // TODO no i18n support here.
- let email = &local_user_view.local_user.email.expect("email");
- let subject = &format!("Password reset for {}", local_user_view.person.name);
- let protocol_and_hostname = &context.settings().get_protocol_and_hostname();
- let html = &format!("
Password Reset Request for {}
Click here to reset your password", local_user_view.person.name, protocol_and_hostname, &token);
- send_email(
- subject,
- email,
- &local_user_view.person.name,
- html,
- &context.settings(),
- )?;
-
+ send_password_reset_email(&local_user_view, context.pool(), &context.settings()).await?;
Ok(PasswordResetResponse {})
}
}
@@ -963,6 +946,13 @@ impl Perform for VerifyEmail {
})
.await??;
+ let local_user_view = blocking(context.pool(), move |conn| {
+ LocalUserView::read(conn, local_user_id)
+ })
+ .await??;
+
+ send_email_verification_success(&local_user_view, &context.settings())?;
+
Ok(VerifyEmailResponse {})
}
}
diff --git a/crates/api/src/site.rs b/crates/api/src/site.rs
index c7eaf633d..c653a212c 100644
--- a/crates/api/src/site.rs
+++ b/crates/api/src/site.rs
@@ -9,6 +9,7 @@ use lemmy_api_common::{
get_local_user_view_from_jwt,
get_local_user_view_from_jwt_opt,
is_admin,
+ send_application_approved_email,
site::*,
};
use lemmy_apub::{
@@ -656,6 +657,14 @@ impl Perform for ApproveRegistrationApplication {
})
.await??;
+ let require_email_verification = blocking(context.pool(), Site::read_simple)
+ .await??
+ .require_email_verification;
+
+ if require_email_verification && data.approve {
+ send_application_approved_email(&local_user_view, &context.settings())?;
+ }
+
// Read the view
let registration_application = blocking(context.pool(), move |conn| {
RegistrationApplicationView::read(conn, app_id)
diff --git a/crates/api_common/src/lib.rs b/crates/api_common/src/lib.rs
index 614c75151..94e7b1d8e 100644
--- a/crates/api_common/src/lib.rs
+++ b/crates/api_common/src/lib.rs
@@ -10,6 +10,8 @@ use lemmy_db_schema::{
newtypes::{CommunityId, LocalUserId, PersonId, PostId},
source::{
community::Community,
+ email_verification::{EmailVerification, EmailVerificationForm},
+ password_reset_request::PasswordResetRequest,
person_block::PersonBlock,
post::{Post, PostRead, PostReadForm},
secret::Secret,
@@ -23,7 +25,14 @@ use lemmy_db_views_actor::{
community_person_ban_view::CommunityPersonBanView,
community_view::CommunityView,
};
-use lemmy_utils::{claims::Claims, settings::structs::FederationConfig, LemmyError, Sensitive};
+use lemmy_utils::{
+ claims::Claims,
+ email::send_email,
+ settings::structs::{FederationConfig, Settings},
+ utils::generate_random_string,
+ LemmyError,
+ Sensitive,
+};
use url::Url;
pub async fn blocking(pool: &DbPool, f: F) -> Result
@@ -333,3 +342,122 @@ pub fn honeypot_check(honeypot: &Option) -> Result<(), LemmyError> {
Ok(())
}
}
+
+pub fn send_email_to_user(
+ local_user_view: &LocalUserView,
+ subject_text: &str,
+ body_text: &str,
+ comment_content: &str,
+ settings: &Settings,
+) {
+ if local_user_view.person.banned || !local_user_view.local_user.send_notifications_to_email {
+ return;
+ }
+
+ if let Some(user_email) = &local_user_view.local_user.email {
+ let subject = &format!(
+ "{} - {} {}",
+ subject_text, settings.hostname, local_user_view.person.name,
+ );
+ let html = &format!(
+ "{}
{} - {}
inbox",
+ body_text,
+ local_user_view.person.name,
+ comment_content,
+ settings.get_protocol_and_hostname()
+ );
+ match send_email(
+ subject,
+ user_email,
+ &local_user_view.person.name,
+ html,
+ settings,
+ ) {
+ Ok(_o) => _o,
+ Err(e) => tracing::error!("{}", e),
+ };
+ }
+}
+
+pub async fn send_password_reset_email(
+ local_user_view: &LocalUserView,
+ pool: &DbPool,
+ settings: &Settings,
+) -> Result<(), LemmyError> {
+ // Generate a random token
+ let token = generate_random_string();
+
+ // Insert the row
+ let token2 = token.clone();
+ let local_user_id = local_user_view.local_user.id;
+ blocking(pool, move |conn| {
+ PasswordResetRequest::create_token(conn, local_user_id, &token2)
+ })
+ .await??;
+
+ let email = &local_user_view.local_user.email.to_owned().expect("email");
+ let subject = &format!("Password reset for {}", local_user_view.person.name);
+ let protocol_and_hostname = settings.get_protocol_and_hostname();
+ let html = &format!("Password Reset Request for {}
Click here to reset your password", local_user_view.person.name, protocol_and_hostname, &token);
+ send_email(subject, email, &local_user_view.person.name, html, settings)
+}
+
+/// Send a verification email
+pub async fn send_verification_email(
+ local_user_id: LocalUserId,
+ new_email: &str,
+ username: &str,
+ pool: &DbPool,
+ settings: &Settings,
+) -> Result<(), LemmyError> {
+ let form = EmailVerificationForm {
+ local_user_id,
+ email: new_email.to_string(),
+ verification_token: generate_random_string(),
+ };
+ let verify_link = format!(
+ "{}/verify_email/{}",
+ settings.get_protocol_and_hostname(),
+ &form.verification_token
+ );
+ blocking(pool, move |conn| EmailVerification::create(conn, &form)).await??;
+
+ let subject = format!("Verify your email address for {}", settings.hostname);
+ let body = format!(
+ concat!(
+ "Please click the link below to verify your email address ",
+ "for the account @{}@{}. Ignore this email if the account isn't yours.\n\n",
+ ""
+ ),
+ username, settings.hostname, verify_link
+ );
+ send_email(&subject, new_email, username, &body, settings)?;
+
+ Ok(())
+}
+
+pub fn send_email_verification_success(
+ local_user_view: &LocalUserView,
+ settings: &Settings,
+) -> Result<(), LemmyError> {
+ let email = &local_user_view.local_user.email.to_owned().expect("email");
+ let subject = &format!("Email verified for {}", local_user_view.person.name);
+ let html = "Your email has been verified.";
+ send_email(subject, email, &local_user_view.person.name, html, settings)
+}
+
+pub fn send_application_approved_email(
+ local_user_view: &LocalUserView,
+ settings: &Settings,
+) -> Result<(), LemmyError> {
+ let email = &local_user_view.local_user.email.to_owned().expect("email");
+ let subject = &format!(
+ "Registration to {} approved for {}",
+ settings.hostname, local_user_view.person.name
+ );
+ let html = &format!(
+ "Your registration application has been approved. Welcome to {}!",
+ settings.hostname
+ );
+ send_email(subject, email, &local_user_view.person.name, html, settings)
+}
diff --git a/crates/api_crud/src/private_message/create.rs b/crates/api_crud/src/private_message/create.rs
index ae75d9eaf..54edc2408 100644
--- a/crates/api_crud/src/private_message/create.rs
+++ b/crates/api_crud/src/private_message/create.rs
@@ -5,6 +5,7 @@ use lemmy_api_common::{
check_person_block,
get_local_user_view_from_jwt,
person::{CreatePrivateMessage, PrivateMessageResponse},
+ send_email_to_user,
};
use lemmy_apub::{
generate_local_apub_endpoint,
@@ -20,11 +21,7 @@ use lemmy_db_schema::{
};
use lemmy_db_views::local_user_view::LocalUserView;
use lemmy_utils::{utils::remove_slurs, ConnectionId, LemmyError};
-use lemmy_websocket::{
- send::{send_email_to_user, send_pm_ws_message},
- LemmyContext,
- UserOperationCrud,
-};
+use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
#[async_trait::async_trait(?Send)]
impl PerformCrud for CreatePrivateMessage {
diff --git a/crates/api_crud/src/user/create.rs b/crates/api_crud/src/user/create.rs
index 006aeacf5..2160661ac 100644
--- a/crates/api_crud/src/user/create.rs
+++ b/crates/api_crud/src/user/create.rs
@@ -1,6 +1,12 @@
use crate::PerformCrud;
use actix_web::web::Data;
-use lemmy_api_common::{blocking, honeypot_check, password_length_check, person::*};
+use lemmy_api_common::{
+ blocking,
+ honeypot_check,
+ password_length_check,
+ person::*,
+ send_verification_email,
+};
use lemmy_apub::{
generate_followers_url,
generate_inbox_url,
@@ -34,7 +40,7 @@ use lemmy_utils::{
ConnectionId,
LemmyError,
};
-use lemmy_websocket::{email::send_verification_email, messages::CheckCaptcha, LemmyContext};
+use lemmy_websocket::{messages::CheckCaptcha, LemmyContext};
#[async_trait::async_trait(?Send)]
impl PerformCrud for Register {
@@ -258,7 +264,8 @@ impl PerformCrud for Register {
// we check at the beginning of this method that email is set
&inserted_local_user.email.expect("email was provided"),
&inserted_person.name,
- context,
+ context.pool(),
+ &context.settings(),
)
.await?;
LoginResponse {
diff --git a/crates/websocket/src/email.rs b/crates/websocket/src/email.rs
deleted file mode 100644
index 21b003de7..000000000
--- a/crates/websocket/src/email.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-use crate::LemmyContext;
-use lemmy_api_common::blocking;
-use lemmy_db_schema::{
- newtypes::LocalUserId,
- source::email_verification::{EmailVerification, EmailVerificationForm},
- traits::Crud,
-};
-use lemmy_utils::{email::send_email, utils::generate_random_string, LemmyError};
-
-pub async fn send_verification_email(
- local_user_id: LocalUserId,
- new_email: &str,
- username: &str,
- context: &LemmyContext,
-) -> Result<(), LemmyError> {
- let settings = context.settings();
- let form = EmailVerificationForm {
- local_user_id,
- email: new_email.to_string(),
- verification_token: generate_random_string(),
- };
- // TODO: link should be replaced with a frontend route once that exists
- let verify_link = format!(
- "{}/api/v3/user/verify_email?token={}",
- settings.get_protocol_and_hostname(),
- &form.verification_token
- );
- blocking(context.pool(), move |conn| {
- EmailVerification::create(conn, &form)
- })
- .await??;
-
- let subject = format!("Verify your email address for {}", settings.hostname);
- let body = format!(
- concat!(
- "Please click the link below to verify your email address ",
- "for the account @{}@{}. Ignore this email if the account isn't yours.\n\n",
- ""
- ),
- username, settings.hostname, verify_link
- );
- send_email(&subject, new_email, username, &body, &context.settings())?;
-
- Ok(())
-}
diff --git a/crates/websocket/src/lib.rs b/crates/websocket/src/lib.rs
index 3035dfe27..cf2f6a257 100644
--- a/crates/websocket/src/lib.rs
+++ b/crates/websocket/src/lib.rs
@@ -10,7 +10,6 @@ use reqwest::Client;
use serde::Serialize;
pub mod chat_server;
-pub mod email;
pub mod handlers;
pub mod messages;
pub mod routes;
diff --git a/crates/websocket/src/send.rs b/crates/websocket/src/send.rs
index ac0752c64..e7f265b50 100644
--- a/crates/websocket/src/send.rs
+++ b/crates/websocket/src/send.rs
@@ -10,6 +10,7 @@ use lemmy_api_common::{
community::CommunityResponse,
person::PrivateMessageResponse,
post::PostResponse,
+ send_email_to_user,
};
use lemmy_db_schema::{
newtypes::{CommentId, CommunityId, LocalUserId, PersonId, PostId, PrivateMessageId},
@@ -28,14 +29,7 @@ use lemmy_db_views::{
private_message_view::PrivateMessageView,
};
use lemmy_db_views_actor::community_view::CommunityView;
-use lemmy_utils::{
- email::send_email,
- settings::structs::Settings,
- utils::MentionData,
- ConnectionId,
- LemmyError,
-};
-use tracing::error;
+use lemmy_utils::{utils::MentionData, ConnectionId, LemmyError};
pub async fn send_post_ws_message(
post_id: PostId,
@@ -296,39 +290,3 @@ pub async fn send_local_notifs(
};
Ok(recipient_ids)
}
-
-pub fn send_email_to_user(
- local_user_view: &LocalUserView,
- subject_text: &str,
- body_text: &str,
- comment_content: &str,
- settings: &Settings,
-) {
- if local_user_view.person.banned || !local_user_view.local_user.send_notifications_to_email {
- return;
- }
-
- if let Some(user_email) = &local_user_view.local_user.email {
- let subject = &format!(
- "{} - {} {}",
- subject_text, settings.hostname, local_user_view.person.name,
- );
- let html = &format!(
- "{}
{} - {}
inbox",
- body_text,
- local_user_view.person.name,
- comment_content,
- settings.get_protocol_and_hostname()
- );
- match send_email(
- subject,
- user_email,
- &local_user_view.person.name,
- html,
- settings,
- ) {
- Ok(_o) => _o,
- Err(e) => error!("{}", e),
- };
- }
-}