* Send out email after registration denied, email confirmed (fixes #5547) * wip * wip2 * all compiling * cleanup * move to subfolders * update * more fixes * clippy * remove line
This commit is contained in:
parent
e7ddb96659
commit
79f79a4e9a
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -1,4 +1,4 @@
|
|||||||
[submodule "crates/utils/translations"]
|
[submodule "crates/utils/translations"]
|
||||||
path = crates/utils/translations
|
path = crates/email/translations
|
||||||
url = https://github.com/LemmyNet/lemmy-translations.git
|
url = https://github.com/LemmyNet/lemmy-translations.git
|
||||||
branch = main
|
branch = main
|
||||||
|
24
Cargo.lock
generated
24
Cargo.lock
generated
@ -3119,6 +3119,7 @@ dependencies = [
|
|||||||
"lemmy_api_crud",
|
"lemmy_api_crud",
|
||||||
"lemmy_db_schema",
|
"lemmy_db_schema",
|
||||||
"lemmy_db_views",
|
"lemmy_db_views",
|
||||||
|
"lemmy_email",
|
||||||
"lemmy_utils",
|
"lemmy_utils",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"regex",
|
"regex",
|
||||||
@ -3148,6 +3149,7 @@ dependencies = [
|
|||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
"lemmy_db_schema",
|
"lemmy_db_schema",
|
||||||
"lemmy_db_views",
|
"lemmy_db_views",
|
||||||
|
"lemmy_email",
|
||||||
"lemmy_utils",
|
"lemmy_utils",
|
||||||
"mime",
|
"mime",
|
||||||
"mime_guess",
|
"mime_guess",
|
||||||
@ -3166,7 +3168,6 @@ dependencies = [
|
|||||||
"ts-rs",
|
"ts-rs",
|
||||||
"url",
|
"url",
|
||||||
"urlencoding",
|
"urlencoding",
|
||||||
"uuid",
|
|
||||||
"webmention",
|
"webmention",
|
||||||
"webpage",
|
"webpage",
|
||||||
]
|
]
|
||||||
@ -3186,6 +3187,7 @@ dependencies = [
|
|||||||
"lemmy_api_common",
|
"lemmy_api_common",
|
||||||
"lemmy_db_schema",
|
"lemmy_db_schema",
|
||||||
"lemmy_db_views",
|
"lemmy_db_views",
|
||||||
|
"lemmy_email",
|
||||||
"lemmy_utils",
|
"lemmy_utils",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
@ -3309,6 +3311,21 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lemmy_email"
|
||||||
|
version = "1.0.0-alpha.4"
|
||||||
|
dependencies = [
|
||||||
|
"html2text",
|
||||||
|
"lemmy_db_schema",
|
||||||
|
"lemmy_db_views",
|
||||||
|
"lemmy_utils",
|
||||||
|
"lettre",
|
||||||
|
"rosetta-build",
|
||||||
|
"rosetta-i18n",
|
||||||
|
"tracing",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_federate"
|
name = "lemmy_federate"
|
||||||
version = "1.0.0-alpha.4"
|
version = "1.0.0-alpha.4"
|
||||||
@ -3411,10 +3428,8 @@ dependencies = [
|
|||||||
"enum-map",
|
"enum-map",
|
||||||
"futures",
|
"futures",
|
||||||
"git-version",
|
"git-version",
|
||||||
"html2text",
|
|
||||||
"http 1.2.0",
|
"http 1.2.0",
|
||||||
"itertools 0.14.0",
|
"itertools 0.14.0",
|
||||||
"lettre",
|
|
||||||
"markdown-it",
|
"markdown-it",
|
||||||
"markdown-it-block-spoiler",
|
"markdown-it-block-spoiler",
|
||||||
"markdown-it-footnote",
|
"markdown-it-footnote",
|
||||||
@ -3425,8 +3440,6 @@ dependencies = [
|
|||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest-middleware",
|
"reqwest-middleware",
|
||||||
"rosetta-build",
|
|
||||||
"rosetta-i18n",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"smart-default",
|
"smart-default",
|
||||||
@ -3437,7 +3450,6 @@ dependencies = [
|
|||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"url",
|
"url",
|
||||||
"urlencoding",
|
"urlencoding",
|
||||||
"uuid",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -50,6 +50,7 @@ members = [
|
|||||||
"crates/db_views",
|
"crates/db_views",
|
||||||
"crates/routes",
|
"crates/routes",
|
||||||
"crates/federate",
|
"crates/federate",
|
||||||
|
"crates/email",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.lints.clippy]
|
[workspace.lints.clippy]
|
||||||
@ -88,6 +89,7 @@ lemmy_api_common = { version = "=1.0.0-alpha.4", path = "./crates/api_common" }
|
|||||||
lemmy_routes = { version = "=1.0.0-alpha.4", path = "./crates/routes" }
|
lemmy_routes = { version = "=1.0.0-alpha.4", path = "./crates/routes" }
|
||||||
lemmy_db_views = { version = "=1.0.0-alpha.4", path = "./crates/db_views" }
|
lemmy_db_views = { version = "=1.0.0-alpha.4", path = "./crates/db_views" }
|
||||||
lemmy_federate = { version = "=1.0.0-alpha.4", path = "./crates/federate" }
|
lemmy_federate = { version = "=1.0.0-alpha.4", path = "./crates/federate" }
|
||||||
|
lemmy_email = { version = "=1.0.0-alpha.4", path = "./crates/email" }
|
||||||
activitypub_federation = { version = "0.6.3", default-features = false, features = [
|
activitypub_federation = { version = "0.6.3", default-features = false, features = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
] }
|
] }
|
||||||
|
@ -22,6 +22,7 @@ lemmy_utils = { workspace = true }
|
|||||||
lemmy_db_schema = { workspace = true, features = ["full"] }
|
lemmy_db_schema = { workspace = true, features = ["full"] }
|
||||||
lemmy_db_views = { workspace = true, features = ["full"] }
|
lemmy_db_views = { workspace = true, features = ["full"] }
|
||||||
lemmy_api_common = { workspace = true, features = ["full"] }
|
lemmy_api_common = { workspace = true, features = ["full"] }
|
||||||
|
lemmy_email = { workspace = true }
|
||||||
activitypub_federation = { workspace = true }
|
activitypub_federation = { workspace = true }
|
||||||
bcrypt = { workspace = true }
|
bcrypt = { workspace = true }
|
||||||
actix-web = { workspace = true }
|
actix-web = { workspace = true }
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
use actix_web::web::{Data, Json};
|
use actix_web::web::{Data, Json};
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{context::LemmyContext, person::ResendVerificationEmail, SuccessResponse};
|
||||||
context::LemmyContext,
|
|
||||||
person::ResendVerificationEmail,
|
|
||||||
utils::send_verification_email_if_required,
|
|
||||||
SuccessResponse,
|
|
||||||
};
|
|
||||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||||
|
use lemmy_email::account::send_verification_email_if_required;
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
|
||||||
pub async fn resend_verification_email(
|
pub async fn resend_verification_email(
|
||||||
@ -19,10 +15,10 @@ pub async fn resend_verification_email(
|
|||||||
let local_user_view = LocalUserView::find_by_email(&mut context.pool(), &email).await?;
|
let local_user_view = LocalUserView::find_by_email(&mut context.pool(), &email).await?;
|
||||||
|
|
||||||
send_verification_email_if_required(
|
send_verification_email_if_required(
|
||||||
&context,
|
|
||||||
&site_view.local_site,
|
&site_view.local_site,
|
||||||
&local_user_view.local_user,
|
&local_user_view,
|
||||||
&local_user_view.person,
|
&mut context.pool(),
|
||||||
|
context.settings(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
@ -2,10 +2,11 @@ use actix_web::web::{Data, Json};
|
|||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
person::PasswordReset,
|
person::PasswordReset,
|
||||||
utils::{check_email_verified, send_password_reset_email},
|
utils::check_email_verified,
|
||||||
SuccessResponse,
|
SuccessResponse,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||||
|
use lemmy_email::account::send_password_reset_email;
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::LemmyResult;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ use actix_web::web::Json;
|
|||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
person::SaveUserSettings,
|
person::SaveUserSettings,
|
||||||
utils::{get_url_blocklist, process_markdown_opt, send_verification_email, slur_regex},
|
utils::{get_url_blocklist, process_markdown_opt, slur_regex},
|
||||||
SuccessResponse,
|
SuccessResponse,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
@ -16,6 +16,7 @@ use lemmy_db_schema::{
|
|||||||
utils::{diesel_opt_number_update, diesel_string_update},
|
utils::{diesel_opt_number_update, diesel_string_update},
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||||
|
use lemmy_email::account::send_verification_email;
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
error::{LemmyErrorType, LemmyResult},
|
error::{LemmyErrorType, LemmyResult},
|
||||||
utils::validation::{is_valid_bio_field, is_valid_display_name, is_valid_matrix_id},
|
utils::validation::{is_valid_bio_field, is_valid_display_name, is_valid_matrix_id},
|
||||||
@ -49,8 +50,7 @@ pub async fn save_user_settings(
|
|||||||
LocalUser::check_is_email_taken(&mut context.pool(), email).await?;
|
LocalUser::check_is_email_taken(&mut context.pool(), email).await?;
|
||||||
send_verification_email(
|
send_verification_email(
|
||||||
&site_view.local_site,
|
&site_view.local_site,
|
||||||
&local_user_view.local_user,
|
&local_user_view,
|
||||||
&local_user_view.person,
|
|
||||||
email,
|
email,
|
||||||
&mut context.pool(),
|
&mut context.pool(),
|
||||||
context.settings(),
|
context.settings(),
|
||||||
|
@ -1,15 +1,11 @@
|
|||||||
use actix_web::web::{Data, Json};
|
use actix_web::web::{Data, Json};
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{context::LemmyContext, person::VerifyEmail, SuccessResponse};
|
||||||
context::LemmyContext,
|
|
||||||
person::VerifyEmail,
|
|
||||||
utils::send_new_applicant_email_to_admins,
|
|
||||||
SuccessResponse,
|
|
||||||
};
|
|
||||||
use lemmy_db_schema::source::{
|
use lemmy_db_schema::source::{
|
||||||
email_verification::EmailVerification,
|
email_verification::EmailVerification,
|
||||||
local_user::{LocalUser, LocalUserUpdateForm},
|
local_user::{LocalUser, LocalUserUpdateForm},
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||||
|
use lemmy_email::{account::send_email_verified_email, admin::send_new_applicant_email_to_admins};
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
|
||||||
pub async fn verify_email(
|
pub async fn verify_email(
|
||||||
@ -48,5 +44,7 @@ pub async fn verify_email(
|
|||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
send_email_verified_email(&local_user_view, context.settings()).await?;
|
||||||
|
|
||||||
Ok(Json(SuccessResponse::default()))
|
Ok(Json(SuccessResponse::default()))
|
||||||
}
|
}
|
||||||
|
@ -5,18 +5,14 @@ use lemmy_api_common::{
|
|||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
reports::comment::{CommentReportResponse, CreateCommentReport},
|
reports::comment::{CommentReportResponse, CreateCommentReport},
|
||||||
send_activity::{ActivityChannel, SendActivityData},
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{
|
utils::{check_comment_deleted_or_removed, check_community_user_action, slur_regex},
|
||||||
check_comment_deleted_or_removed,
|
|
||||||
check_community_user_action,
|
|
||||||
send_new_report_email_to_admins,
|
|
||||||
slur_regex,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::comment_report::{CommentReport, CommentReportForm},
|
source::comment_report::{CommentReport, CommentReportForm},
|
||||||
traits::Reportable,
|
traits::Reportable,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{CommentReportView, CommentView, LocalUserView, SiteView};
|
use lemmy_db_views::structs::{CommentReportView, CommentView, LocalUserView, SiteView};
|
||||||
|
use lemmy_email::admin::send_new_report_email_to_admins;
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
|
||||||
/// Creates a comment report and notifies the moderators of the community
|
/// Creates a comment report and notifies the moderators of the community
|
||||||
|
@ -3,7 +3,7 @@ use actix_web::web::{Data, Json};
|
|||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
reports::community::{CommunityReportResponse, CreateCommunityReport},
|
reports::community::{CommunityReportResponse, CreateCommunityReport},
|
||||||
utils::{send_new_report_email_to_admins, slur_regex},
|
utils::slur_regex,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
@ -13,6 +13,7 @@ use lemmy_db_schema::{
|
|||||||
traits::{Crud, Reportable},
|
traits::{Crud, Reportable},
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{CommunityReportView, LocalUserView, SiteView};
|
use lemmy_db_views::structs::{CommunityReportView, LocalUserView, SiteView};
|
||||||
|
use lemmy_email::admin::send_new_report_email_to_admins;
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
|
||||||
pub async fn create_community_report(
|
pub async fn create_community_report(
|
||||||
|
@ -5,18 +5,14 @@ use lemmy_api_common::{
|
|||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
reports::post::{CreatePostReport, PostReportResponse},
|
reports::post::{CreatePostReport, PostReportResponse},
|
||||||
send_activity::{ActivityChannel, SendActivityData},
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{
|
utils::{check_community_user_action, check_post_deleted_or_removed, slur_regex},
|
||||||
check_community_user_action,
|
|
||||||
check_post_deleted_or_removed,
|
|
||||||
send_new_report_email_to_admins,
|
|
||||||
slur_regex,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::post_report::{PostReport, PostReportForm},
|
source::post_report::{PostReport, PostReportForm},
|
||||||
traits::Reportable,
|
traits::Reportable,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{LocalUserView, PostReportView, PostView, SiteView};
|
use lemmy_db_views::structs::{LocalUserView, PostReportView, PostView, SiteView};
|
||||||
|
use lemmy_email::admin::send_new_report_email_to_admins;
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
|
||||||
/// Creates a post report and notifies the moderators of the community
|
/// Creates a post report and notifies the moderators of the community
|
||||||
|
@ -3,7 +3,7 @@ use actix_web::web::{Data, Json};
|
|||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
reports::private_message::{CreatePrivateMessageReport, PrivateMessageReportResponse},
|
reports::private_message::{CreatePrivateMessageReport, PrivateMessageReportResponse},
|
||||||
utils::{send_new_report_email_to_admins, slur_regex},
|
utils::slur_regex,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
@ -13,6 +13,7 @@ use lemmy_db_schema::{
|
|||||||
traits::{Crud, Reportable},
|
traits::{Crud, Reportable},
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{LocalUserView, PrivateMessageReportView, SiteView};
|
use lemmy_db_views::structs::{LocalUserView, PrivateMessageReportView, SiteView};
|
||||||
|
use lemmy_email::admin::send_new_report_email_to_admins;
|
||||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||||
|
|
||||||
pub async fn create_pm_report(
|
pub async fn create_pm_report(
|
||||||
|
@ -4,7 +4,7 @@ use diesel_async::{scoped_futures::ScopedFutureExt, AsyncConnection};
|
|||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
site::{ApproveRegistrationApplication, RegistrationApplicationResponse},
|
site::{ApproveRegistrationApplication, RegistrationApplicationResponse},
|
||||||
utils::{is_admin, send_application_approved_email},
|
utils::is_admin,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
@ -15,6 +15,7 @@ use lemmy_db_schema::{
|
|||||||
utils::{diesel_string_update, get_conn},
|
utils::{diesel_string_update, get_conn},
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{LocalUserView, RegistrationApplicationView};
|
use lemmy_db_views::structs::{LocalUserView, RegistrationApplicationView};
|
||||||
|
use lemmy_email::account::{send_application_approved_email, send_application_denied_email};
|
||||||
use lemmy_utils::error::{LemmyError, LemmyResult};
|
use lemmy_utils::error::{LemmyError, LemmyResult};
|
||||||
|
|
||||||
pub async fn approve_registration_application(
|
pub async fn approve_registration_application(
|
||||||
@ -58,14 +59,20 @@ pub async fn approve_registration_application(
|
|||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if data.approve {
|
let approved_local_user_view = LocalUserView::read(&mut context.pool(), approved_user_id).await?;
|
||||||
let approved_local_user_view =
|
if approved_local_user_view.local_user.email.is_some() {
|
||||||
LocalUserView::read(&mut context.pool(), approved_user_id).await?;
|
// Email sending may fail, but this won't revert the application approval
|
||||||
if approved_local_user_view.local_user.email.is_some() {
|
if data.approve {
|
||||||
// Email sending may fail, but this won't revert the application approval
|
|
||||||
send_application_approved_email(&approved_local_user_view, context.settings()).await?;
|
send_application_approved_email(&approved_local_user_view, context.settings()).await?;
|
||||||
|
} else {
|
||||||
|
send_application_denied_email(
|
||||||
|
&approved_local_user_view,
|
||||||
|
data.deny_reason.clone(),
|
||||||
|
context.settings(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// Read the view
|
// Read the view
|
||||||
let registration_application =
|
let registration_application =
|
||||||
|
@ -299,7 +299,7 @@ async fn test_application_approval() -> LemmyResult<()> {
|
|||||||
expected_total_applications,
|
expected_total_applications,
|
||||||
);
|
);
|
||||||
|
|
||||||
approve_registration_application(
|
let deny = approve_registration_application(
|
||||||
Json(ApproveRegistrationApplication {
|
Json(ApproveRegistrationApplication {
|
||||||
id: app_with_email_2.id,
|
id: app_with_email_2.id,
|
||||||
approve: false,
|
approve: false,
|
||||||
@ -308,7 +308,8 @@ async fn test_application_approval() -> LemmyResult<()> {
|
|||||||
context.reset_request_count(),
|
context.reset_request_count(),
|
||||||
admin_local_user_view.clone(),
|
admin_local_user_view.clone(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await;
|
||||||
|
assert!(deny.is_err_and(|e| e.error_type == LemmyErrorType::NoEmailSetup));
|
||||||
|
|
||||||
expected_unread_applications -= 1;
|
expected_unread_applications -= 1;
|
||||||
|
|
||||||
|
@ -21,13 +21,13 @@ full = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
"lemmy_db_views/full",
|
"lemmy_db_views/full",
|
||||||
"lemmy_utils/full",
|
"lemmy_utils/full",
|
||||||
|
"lemmy_email",
|
||||||
"activitypub_federation",
|
"activitypub_federation",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"reqwest-middleware",
|
"reqwest-middleware",
|
||||||
"webpage",
|
"webpage",
|
||||||
"ts-rs",
|
"ts-rs",
|
||||||
"tokio",
|
"tokio",
|
||||||
"uuid",
|
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"futures",
|
"futures",
|
||||||
@ -46,6 +46,7 @@ full = [
|
|||||||
lemmy_db_views = { workspace = true }
|
lemmy_db_views = { workspace = true }
|
||||||
lemmy_db_schema = { workspace = true }
|
lemmy_db_schema = { workspace = true }
|
||||||
lemmy_utils = { workspace = true }
|
lemmy_utils = { workspace = true }
|
||||||
|
lemmy_email = { workspace = true, optional = true }
|
||||||
activitypub_federation = { workspace = true, optional = true }
|
activitypub_federation = { workspace = true, optional = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_with = { workspace = true }
|
serde_with = { workspace = true }
|
||||||
@ -55,7 +56,6 @@ tracing = { workspace = true, optional = true }
|
|||||||
reqwest-middleware = { workspace = true, optional = true }
|
reqwest-middleware = { workspace = true, optional = true }
|
||||||
regex = { workspace = true }
|
regex = { workspace = true }
|
||||||
futures = { workspace = true, optional = true }
|
futures = { workspace = true, optional = true }
|
||||||
uuid = { workspace = true, optional = true }
|
|
||||||
tokio = { workspace = true, optional = true }
|
tokio = { workspace = true, optional = true }
|
||||||
reqwest = { workspace = true, optional = true }
|
reqwest = { workspace = true, optional = true }
|
||||||
ts-rs = { workspace = true, optional = true }
|
ts-rs = { workspace = true, optional = true }
|
||||||
|
@ -3,7 +3,7 @@ use crate::{
|
|||||||
community::CommunityResponse,
|
community::CommunityResponse,
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
post::PostResponse,
|
post::PostResponse,
|
||||||
utils::{check_person_instance_community_block, is_mod_or_admin, send_email_to_user},
|
utils::{check_person_instance_community_block, is_mod_or_admin},
|
||||||
};
|
};
|
||||||
use actix_web::web::Json;
|
use actix_web::web::Json;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
@ -21,10 +21,12 @@ use lemmy_db_schema::{
|
|||||||
traits::Crud,
|
traits::Crud,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{CommentView, CommunityView, LocalUserView, PostView};
|
use lemmy_db_views::structs::{CommentView, CommunityView, LocalUserView, PostView};
|
||||||
use lemmy_utils::{
|
use lemmy_email::notifications::{
|
||||||
error::LemmyResult,
|
send_comment_reply_email,
|
||||||
utils::{markdown::markdown_to_html, mention::MentionData},
|
send_mention_email,
|
||||||
|
send_post_reply_email,
|
||||||
};
|
};
|
||||||
|
use lemmy_utils::{error::LemmyResult, utils::mention::MentionData};
|
||||||
|
|
||||||
pub async fn build_comment_response(
|
pub async fn build_comment_response(
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
@ -135,8 +137,6 @@ pub async fn send_local_notifs(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let inbox_link = format!("{}/inbox", context.settings().get_protocol_and_hostname());
|
|
||||||
|
|
||||||
// Send the local mentions
|
// Send the local mentions
|
||||||
for mention in mentions
|
for mention in mentions
|
||||||
.iter()
|
.iter()
|
||||||
@ -187,15 +187,14 @@ pub async fn send_local_notifs(
|
|||||||
|
|
||||||
// Send an email to those local users that have notifications on
|
// Send an email to those local users that have notifications on
|
||||||
if do_send_email {
|
if do_send_email {
|
||||||
let lang = &mention_user_view.local_user.interface_i18n_language();
|
send_mention_email(
|
||||||
let content = markdown_to_html(&comment_content_or_post_body);
|
|
||||||
send_email_to_user(
|
|
||||||
&mention_user_view,
|
&mention_user_view,
|
||||||
&lang.notification_mentioned_by_subject(&person.name),
|
&comment_content_or_post_body,
|
||||||
&lang.notification_mentioned_by_body(&link, &content, &inbox_link, &person.name),
|
person,
|
||||||
|
link,
|
||||||
context.settings(),
|
context.settings(),
|
||||||
)
|
)
|
||||||
.await
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -240,22 +239,15 @@ pub async fn send_local_notifs(
|
|||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
if do_send_email {
|
if do_send_email {
|
||||||
let lang = &parent_user_view.local_user.interface_i18n_language();
|
send_comment_reply_email(
|
||||||
let content = markdown_to_html(&comment.content);
|
|
||||||
send_email_to_user(
|
|
||||||
&parent_user_view,
|
&parent_user_view,
|
||||||
&lang.notification_comment_reply_subject(&person.name),
|
comment,
|
||||||
&lang.notification_comment_reply_body(
|
person,
|
||||||
comment.local_url(context.settings())?,
|
&parent_comment,
|
||||||
&content,
|
&post,
|
||||||
&inbox_link,
|
|
||||||
&parent_comment.content,
|
|
||||||
&post.name,
|
|
||||||
&person.name,
|
|
||||||
),
|
|
||||||
context.settings(),
|
context.settings(),
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -293,21 +285,14 @@ pub async fn send_local_notifs(
|
|||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
if do_send_email {
|
if do_send_email {
|
||||||
let lang = &parent_user_view.local_user.interface_i18n_language();
|
send_post_reply_email(
|
||||||
let content = markdown_to_html(&comment.content);
|
|
||||||
send_email_to_user(
|
|
||||||
&parent_user_view,
|
&parent_user_view,
|
||||||
&lang.notification_post_reply_subject(&person.name),
|
comment,
|
||||||
&lang.notification_post_reply_body(
|
person,
|
||||||
comment.local_url(context.settings())?,
|
&post,
|
||||||
&content,
|
|
||||||
&inbox_link,
|
|
||||||
&post.name,
|
|
||||||
&person.name,
|
|
||||||
),
|
|
||||||
context.settings(),
|
context.settings(),
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,13 +17,11 @@ use lemmy_db_schema::{
|
|||||||
source::{
|
source::{
|
||||||
comment::{Comment, CommentActions, CommentUpdateForm},
|
comment::{Comment, CommentActions, CommentUpdateForm},
|
||||||
community::{Community, CommunityActions, CommunityUpdateForm},
|
community::{Community, CommunityActions, CommunityUpdateForm},
|
||||||
email_verification::{EmailVerification, EmailVerificationForm},
|
|
||||||
images::{ImageDetails, RemoteImage},
|
images::{ImageDetails, RemoteImage},
|
||||||
instance::{Instance, InstanceActions},
|
instance::{Instance, InstanceActions},
|
||||||
local_site::LocalSite,
|
local_site::LocalSite,
|
||||||
local_site_rate_limit::LocalSiteRateLimit,
|
local_site_rate_limit::LocalSiteRateLimit,
|
||||||
local_site_url_blocklist::LocalSiteUrlBlocklist,
|
local_site_url_blocklist::LocalSiteUrlBlocklist,
|
||||||
local_user::LocalUser,
|
|
||||||
mod_log::moderator::{
|
mod_log::moderator::{
|
||||||
ModRemoveComment,
|
ModRemoveComment,
|
||||||
ModRemoveCommentForm,
|
ModRemoveCommentForm,
|
||||||
@ -31,7 +29,6 @@ use lemmy_db_schema::{
|
|||||||
ModRemovePostForm,
|
ModRemovePostForm,
|
||||||
},
|
},
|
||||||
oauth_account::OAuthAccount,
|
oauth_account::OAuthAccount,
|
||||||
password_reset_request::PasswordResetRequest,
|
|
||||||
person::{Person, PersonActions, PersonUpdateForm},
|
person::{Person, PersonActions, PersonUpdateForm},
|
||||||
post::{Post, PostActions, PostReadCommentsForm},
|
post::{Post, PostActions, PostReadCommentsForm},
|
||||||
private_message::PrivateMessage,
|
private_message::PrivateMessage,
|
||||||
@ -57,13 +54,9 @@ use lemmy_db_views::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
email::send_email,
|
|
||||||
error::{LemmyError, LemmyErrorExt, LemmyErrorExt2, LemmyErrorType, LemmyResult},
|
error::{LemmyError, LemmyErrorExt, LemmyErrorExt2, LemmyErrorType, LemmyResult},
|
||||||
rate_limit::{ActionType, BucketConfig},
|
rate_limit::{ActionType, BucketConfig},
|
||||||
settings::{
|
settings::{structs::PictrsImageMode, SETTINGS},
|
||||||
structs::{PictrsImageMode, Settings},
|
|
||||||
SETTINGS,
|
|
||||||
},
|
|
||||||
spawn_try_task,
|
spawn_try_task,
|
||||||
utils::{
|
utils::{
|
||||||
markdown::{image_links::markdown_rewrite_image_links, markdown_check_for_blocked_urls},
|
markdown::{image_links::markdown_rewrite_image_links, markdown_check_for_blocked_urls},
|
||||||
@ -76,7 +69,7 @@ use lemmy_utils::{
|
|||||||
use moka::future::Cache;
|
use moka::future::Cache;
|
||||||
use regex::{escape, Regex, RegexSet};
|
use regex::{escape, Regex, RegexSet};
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
use tracing::{warn, Instrument};
|
use tracing::Instrument;
|
||||||
use url::{ParseError, Url};
|
use url::{ParseError, Url};
|
||||||
use urlencoding::encode;
|
use urlencoding::encode;
|
||||||
use webmention::{Webmention, WebmentionError};
|
use webmention::{Webmention, WebmentionError};
|
||||||
@ -412,121 +405,6 @@ pub fn honeypot_check(honeypot: &Option<String>) -> LemmyResult<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_email_to_user(
|
|
||||||
local_user_view: &LocalUserView,
|
|
||||||
subject: &str,
|
|
||||||
body: &str,
|
|
||||||
settings: &Settings,
|
|
||||||
) {
|
|
||||||
if local_user_view.banned() || !local_user_view.local_user.send_notifications_to_email {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(user_email) = &local_user_view.local_user.email {
|
|
||||||
match send_email(
|
|
||||||
subject,
|
|
||||||
user_email,
|
|
||||||
&local_user_view.person.name,
|
|
||||||
body,
|
|
||||||
settings,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(_o) => _o,
|
|
||||||
Err(e) => warn!("{}", e),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn send_password_reset_email(
|
|
||||||
user: &LocalUserView,
|
|
||||||
pool: &mut DbPool<'_>,
|
|
||||||
settings: &Settings,
|
|
||||||
) -> LemmyResult<()> {
|
|
||||||
// Generate a random token
|
|
||||||
let token = uuid::Uuid::new_v4().to_string();
|
|
||||||
|
|
||||||
let email = &user
|
|
||||||
.local_user
|
|
||||||
.email
|
|
||||||
.clone()
|
|
||||||
.ok_or(LemmyErrorType::EmailRequired)?;
|
|
||||||
let lang = &user.local_user.interface_i18n_language();
|
|
||||||
let subject = &lang.password_reset_subject(&user.person.name);
|
|
||||||
let protocol_and_hostname = settings.get_protocol_and_hostname();
|
|
||||||
let reset_link = format!("{}/password_change/{}", protocol_and_hostname, &token);
|
|
||||||
let body = &lang.password_reset_body(reset_link, &user.person.name);
|
|
||||||
send_email(subject, email, &user.person.name, body, settings).await?;
|
|
||||||
|
|
||||||
// Insert the row after successful send, to avoid using daily reset limit while
|
|
||||||
// email sending is broken.
|
|
||||||
let local_user_id = user.local_user.id;
|
|
||||||
PasswordResetRequest::create(pool, local_user_id, token.clone()).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Send a verification email
|
|
||||||
pub async fn send_verification_email(
|
|
||||||
local_site: &LocalSite,
|
|
||||||
local_user: &LocalUser,
|
|
||||||
person: &Person,
|
|
||||||
new_email: &str,
|
|
||||||
pool: &mut DbPool<'_>,
|
|
||||||
settings: &Settings,
|
|
||||||
) -> LemmyResult<()> {
|
|
||||||
let form = EmailVerificationForm {
|
|
||||||
local_user_id: local_user.id,
|
|
||||||
email: new_email.to_string(),
|
|
||||||
verification_token: uuid::Uuid::new_v4().to_string(),
|
|
||||||
};
|
|
||||||
let verify_link = format!(
|
|
||||||
"{}/verify_email/{}",
|
|
||||||
settings.get_protocol_and_hostname(),
|
|
||||||
&form.verification_token
|
|
||||||
);
|
|
||||||
EmailVerification::create(pool, &form).await?;
|
|
||||||
|
|
||||||
let lang = local_user.interface_i18n_language();
|
|
||||||
let subject = lang.verify_email_subject(&settings.hostname);
|
|
||||||
|
|
||||||
// If an application is required, use a translation that includes that warning.
|
|
||||||
let body = if local_site.registration_mode == RegistrationMode::RequireApplication {
|
|
||||||
lang.verify_email_body_with_application(&settings.hostname, &person.name, verify_link)
|
|
||||||
} else {
|
|
||||||
lang.verify_email_body(&settings.hostname, &person.name, verify_link)
|
|
||||||
};
|
|
||||||
|
|
||||||
send_email(&subject, new_email, &person.name, &body, settings).await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if email was sent.
|
|
||||||
pub async fn send_verification_email_if_required(
|
|
||||||
context: &LemmyContext,
|
|
||||||
local_site: &LocalSite,
|
|
||||||
local_user: &LocalUser,
|
|
||||||
person: &Person,
|
|
||||||
) -> LemmyResult<bool> {
|
|
||||||
let email = &local_user
|
|
||||||
.email
|
|
||||||
.clone()
|
|
||||||
.ok_or(LemmyErrorType::EmailRequired)?;
|
|
||||||
|
|
||||||
if !local_user.admin && local_site.require_email_verification && !local_user.email_verified {
|
|
||||||
send_verification_email(
|
|
||||||
local_site,
|
|
||||||
local_user,
|
|
||||||
person,
|
|
||||||
email,
|
|
||||||
&mut context.pool(),
|
|
||||||
context.settings(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Ok(true)
|
|
||||||
} else {
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn local_site_rate_limit_to_rate_limit_config(
|
pub fn local_site_rate_limit_to_rate_limit_config(
|
||||||
l: &LocalSiteRateLimit,
|
l: &LocalSiteRateLimit,
|
||||||
) -> EnumMap<ActionType, BucketConfig> {
|
) -> EnumMap<ActionType, BucketConfig> {
|
||||||
@ -592,73 +470,6 @@ pub async fn get_url_blocklist(context: &LemmyContext) -> LemmyResult<RegexSet>
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_application_approved_email(
|
|
||||||
user: &LocalUserView,
|
|
||||||
settings: &Settings,
|
|
||||||
) -> LemmyResult<()> {
|
|
||||||
let email = &user
|
|
||||||
.local_user
|
|
||||||
.email
|
|
||||||
.clone()
|
|
||||||
.ok_or(LemmyErrorType::EmailRequired)?;
|
|
||||||
let lang = &user.local_user.interface_i18n_language();
|
|
||||||
let subject = lang.registration_approved_subject(&user.person.ap_id);
|
|
||||||
let body = lang.registration_approved_body(&settings.hostname);
|
|
||||||
send_email(&subject, email, &user.person.name, &body, settings).await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Send a new applicant email notification to all admins
|
|
||||||
pub async fn send_new_applicant_email_to_admins(
|
|
||||||
applicant_username: &str,
|
|
||||||
pool: &mut DbPool<'_>,
|
|
||||||
settings: &Settings,
|
|
||||||
) -> LemmyResult<()> {
|
|
||||||
// Collect the admins with emails
|
|
||||||
let admins = LocalUserView::list_admins_with_emails(pool).await?;
|
|
||||||
|
|
||||||
let applications_link = &format!(
|
|
||||||
"{}/registration_applications",
|
|
||||||
settings.get_protocol_and_hostname(),
|
|
||||||
);
|
|
||||||
|
|
||||||
for admin in &admins {
|
|
||||||
let email = &admin
|
|
||||||
.local_user
|
|
||||||
.email
|
|
||||||
.clone()
|
|
||||||
.ok_or(LemmyErrorType::EmailRequired)?;
|
|
||||||
let lang = &admin.local_user.interface_i18n_language();
|
|
||||||
let subject = lang.new_application_subject(&settings.hostname, applicant_username);
|
|
||||||
let body = lang.new_application_body(applications_link);
|
|
||||||
send_email(&subject, email, &admin.person.name, &body, settings).await?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Send a report to all admins
|
|
||||||
pub async fn send_new_report_email_to_admins(
|
|
||||||
reporter_username: &str,
|
|
||||||
reported_username: &str,
|
|
||||||
pool: &mut DbPool<'_>,
|
|
||||||
settings: &Settings,
|
|
||||||
) -> LemmyResult<()> {
|
|
||||||
// Collect the admins with emails
|
|
||||||
let admins = LocalUserView::list_admins_with_emails(pool).await?;
|
|
||||||
|
|
||||||
let reports_link = &format!("{}/reports", settings.get_protocol_and_hostname(),);
|
|
||||||
|
|
||||||
for admin in &admins {
|
|
||||||
if let Some(email) = &admin.local_user.email {
|
|
||||||
let lang = &admin.local_user.interface_i18n_language();
|
|
||||||
let subject =
|
|
||||||
lang.new_report_subject(&settings.hostname, reported_username, reporter_username);
|
|
||||||
let body = lang.new_report_body(reports_link);
|
|
||||||
send_email(&subject, email, &admin.person.name, &body, settings).await?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_nsfw_allowed(nsfw: Option<bool>, local_site: Option<&LocalSite>) -> LemmyResult<()> {
|
pub fn check_nsfw_allowed(nsfw: Option<bool>, local_site: Option<&LocalSite>) -> LemmyResult<()> {
|
||||||
let is_nsfw = nsfw.unwrap_or_default();
|
let is_nsfw = nsfw.unwrap_or_default();
|
||||||
let nsfw_disallowed = local_site.is_some_and(|s| s.disallow_nsfw_content);
|
let nsfw_disallowed = local_site.is_some_and(|s| s.disallow_nsfw_content);
|
||||||
|
@ -17,6 +17,7 @@ lemmy_utils = { workspace = true, features = ["full"] }
|
|||||||
lemmy_db_schema = { workspace = true, features = ["full"] }
|
lemmy_db_schema = { workspace = true, features = ["full"] }
|
||||||
lemmy_db_views = { workspace = true, features = ["full"] }
|
lemmy_db_views = { workspace = true, features = ["full"] }
|
||||||
lemmy_api_common = { workspace = true, features = ["full"] }
|
lemmy_api_common = { workspace = true, features = ["full"] }
|
||||||
|
lemmy_email = { workspace = true }
|
||||||
activitypub_federation = { workspace = true }
|
activitypub_federation = { workspace = true }
|
||||||
bcrypt = { workspace = true }
|
bcrypt = { workspace = true }
|
||||||
actix-web = { workspace = true }
|
actix-web = { workspace = true }
|
||||||
|
@ -5,13 +5,7 @@ use lemmy_api_common::{
|
|||||||
plugins::{plugin_hook_after, plugin_hook_before},
|
plugins::{plugin_hook_after, plugin_hook_before},
|
||||||
private_message::{CreatePrivateMessage, PrivateMessageResponse},
|
private_message::{CreatePrivateMessage, PrivateMessageResponse},
|
||||||
send_activity::{ActivityChannel, SendActivityData},
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{
|
utils::{check_private_messages_enabled, get_url_blocklist, process_markdown, slur_regex},
|
||||||
check_private_messages_enabled,
|
|
||||||
get_url_blocklist,
|
|
||||||
process_markdown,
|
|
||||||
send_email_to_user,
|
|
||||||
slur_regex,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
@ -21,9 +15,10 @@ use lemmy_db_schema::{
|
|||||||
traits::{Blockable, Crud},
|
traits::{Blockable, Crud},
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{LocalUserView, PrivateMessageView};
|
use lemmy_db_views::structs::{LocalUserView, PrivateMessageView};
|
||||||
|
use lemmy_email::notifications::send_private_message_email;
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
|
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
|
||||||
utils::{markdown::markdown_to_html, validation::is_valid_body_field},
|
utils::validation::is_valid_body_field,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub async fn create_private_message(
|
pub async fn create_private_message(
|
||||||
@ -72,16 +67,12 @@ pub async fn create_private_message(
|
|||||||
|
|
||||||
// Send email to the local recipient, if one exists
|
// Send email to the local recipient, if one exists
|
||||||
if view.recipient.local {
|
if view.recipient.local {
|
||||||
let recipient_id = data.recipient_id;
|
let local_recipient =
|
||||||
let local_recipient = LocalUserView::read_person(&mut context.pool(), recipient_id).await?;
|
LocalUserView::read_person(&mut context.pool(), data.recipient_id).await?;
|
||||||
let lang = &local_recipient.local_user.interface_i18n_language();
|
send_private_message_email(
|
||||||
let inbox_link = format!("{}/inbox", context.settings().get_protocol_and_hostname());
|
&local_user_view,
|
||||||
let sender_name = &local_user_view.person.name;
|
|
||||||
let content = markdown_to_html(&content);
|
|
||||||
send_email_to_user(
|
|
||||||
&local_recipient,
|
&local_recipient,
|
||||||
&lang.notification_private_message_subject(sender_name),
|
&content,
|
||||||
&lang.notification_private_message_body(inbox_link, &content, sender_name),
|
|
||||||
context.settings(),
|
context.settings(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
@ -13,8 +13,6 @@ use lemmy_api_common::{
|
|||||||
generate_inbox_url,
|
generate_inbox_url,
|
||||||
honeypot_check,
|
honeypot_check,
|
||||||
password_length_check,
|
password_length_check,
|
||||||
send_new_applicant_email_to_admins,
|
|
||||||
send_verification_email_if_required,
|
|
||||||
slur_regex,
|
slur_regex,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -36,6 +34,10 @@ use lemmy_db_schema::{
|
|||||||
RegistrationMode,
|
RegistrationMode,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||||
|
use lemmy_email::{
|
||||||
|
account::send_verification_email_if_required,
|
||||||
|
admin::send_new_applicant_email_to_admins,
|
||||||
|
};
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult},
|
error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult},
|
||||||
settings::structs::Settings,
|
settings::structs::Settings,
|
||||||
@ -131,7 +133,7 @@ pub async fn register(
|
|||||||
let tx_data = data.clone();
|
let tx_data = data.clone();
|
||||||
let tx_local_site = local_site.clone();
|
let tx_local_site = local_site.clone();
|
||||||
let tx_settings = context.settings();
|
let tx_settings = context.settings();
|
||||||
let (person, local_user) = conn
|
let user = conn
|
||||||
.transaction::<_, LemmyError, _>(|conn| {
|
.transaction::<_, LemmyError, _>(|conn| {
|
||||||
async move {
|
async move {
|
||||||
// We have to create both a person, and local_user
|
// We have to create both a person, and local_user
|
||||||
@ -167,7 +169,11 @@ pub async fn register(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((person, local_user))
|
Ok(LocalUserView {
|
||||||
|
person,
|
||||||
|
local_user,
|
||||||
|
instance_actions: None,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
.scope_boxed()
|
.scope_boxed()
|
||||||
})
|
})
|
||||||
@ -189,11 +195,16 @@ pub async fn register(
|
|||||||
if !local_site.site_setup
|
if !local_site.site_setup
|
||||||
|| (!require_registration_application && !local_site.require_email_verification)
|
|| (!require_registration_application && !local_site.require_email_verification)
|
||||||
{
|
{
|
||||||
let jwt = Claims::generate(local_user.id, req, &context).await?;
|
let jwt = Claims::generate(user.local_user.id, req, &context).await?;
|
||||||
login_response.jwt = Some(jwt);
|
login_response.jwt = Some(jwt);
|
||||||
} else {
|
} else {
|
||||||
login_response.verify_email_sent =
|
login_response.verify_email_sent = send_verification_email_if_required(
|
||||||
send_verification_email_if_required(&context, &local_site, &local_user, &person).await?;
|
&local_site,
|
||||||
|
&user,
|
||||||
|
&mut context.pool(),
|
||||||
|
context.settings(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
if require_registration_application {
|
if require_registration_application {
|
||||||
login_response.registration_created = true;
|
login_response.registration_created = true;
|
||||||
@ -348,7 +359,7 @@ pub async fn authenticate_with_oauth(
|
|||||||
let tx_data = data.clone();
|
let tx_data = data.clone();
|
||||||
let tx_local_site = local_site.clone();
|
let tx_local_site = local_site.clone();
|
||||||
let tx_settings = context.settings();
|
let tx_settings = context.settings();
|
||||||
let (person, local_user) = conn
|
let user = conn
|
||||||
.transaction::<_, LemmyError, _>(|conn| {
|
.transaction::<_, LemmyError, _>(|conn| {
|
||||||
async move {
|
async move {
|
||||||
// make sure the username is provided
|
// make sure the username is provided
|
||||||
@ -412,16 +423,25 @@ pub async fn authenticate_with_oauth(
|
|||||||
login_response.registration_created = true;
|
login_response.registration_created = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok((person, local_user))
|
Ok(LocalUserView {
|
||||||
|
person,
|
||||||
|
local_user,
|
||||||
|
instance_actions: None,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
.scope_boxed()
|
.scope_boxed()
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Check email is verified when required
|
// Check email is verified when required
|
||||||
login_response.verify_email_sent =
|
login_response.verify_email_sent = send_verification_email_if_required(
|
||||||
send_verification_email_if_required(&context, &local_site, &local_user, &person).await?;
|
&local_site,
|
||||||
local_user
|
&user,
|
||||||
|
&mut context.pool(),
|
||||||
|
context.settings(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
user.local_user
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,10 +24,7 @@ use diesel::{
|
|||||||
QueryDsl,
|
QueryDsl,
|
||||||
};
|
};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
use lemmy_utils::{
|
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||||
email::{lang_str_to_lang, translations::Lang},
|
|
||||||
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
|
|
||||||
};
|
|
||||||
|
|
||||||
impl LocalUser {
|
impl LocalUser {
|
||||||
pub async fn create(
|
pub async fn create(
|
||||||
@ -291,10 +288,6 @@ impl LocalUser {
|
|||||||
Err(LemmyErrorType::NotHigherMod)?
|
Err(LemmyErrorType::NotHigherMod)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn interface_i18n_language(&self) -> Lang {
|
|
||||||
lang_str_to_lang(&self.interface_language)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds some helper functions for an optional LocalUser
|
/// Adds some helper functions for an optional LocalUser
|
||||||
|
37
crates/email/Cargo.toml
Normal file
37
crates/email/Cargo.toml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
[package]
|
||||||
|
name = "lemmy_email"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
description.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
documentation.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "lemmy_email"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
doctest = false
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
lemmy_utils = { workspace = true, features = ["full"] }
|
||||||
|
lemmy_db_schema = { workspace = true, features = ["full"] }
|
||||||
|
lemmy_db_views = { workspace = true, features = ["full"] }
|
||||||
|
tracing = { workspace = true }
|
||||||
|
uuid = { workspace = true, features = ["v4"] }
|
||||||
|
rosetta-i18n = { workspace = true }
|
||||||
|
html2text = "0.14.0"
|
||||||
|
lettre = { version = "0.11.12", default-features = false, features = [
|
||||||
|
"builder",
|
||||||
|
"smtp-transport",
|
||||||
|
"tokio1-rustls-tls",
|
||||||
|
"pool",
|
||||||
|
] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
rosetta-build = { version = "0.1.3", default-features = false }
|
127
crates/email/src/account.rs
Normal file
127
crates/email/src/account.rs
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
use crate::{send_email, user_email, user_language};
|
||||||
|
use lemmy_db_schema::{
|
||||||
|
source::{
|
||||||
|
email_verification::{EmailVerification, EmailVerificationForm},
|
||||||
|
local_site::LocalSite,
|
||||||
|
password_reset_request::PasswordResetRequest,
|
||||||
|
},
|
||||||
|
utils::DbPool,
|
||||||
|
RegistrationMode,
|
||||||
|
};
|
||||||
|
use lemmy_db_views::structs::LocalUserView;
|
||||||
|
use lemmy_utils::{error::LemmyResult, settings::structs::Settings};
|
||||||
|
|
||||||
|
pub async fn send_password_reset_email(
|
||||||
|
user: &LocalUserView,
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
settings: &Settings,
|
||||||
|
) -> LemmyResult<()> {
|
||||||
|
// Generate a random token
|
||||||
|
let token = uuid::Uuid::new_v4().to_string();
|
||||||
|
|
||||||
|
let lang = user_language(user);
|
||||||
|
let subject = &lang.password_reset_subject(&user.person.name);
|
||||||
|
let protocol_and_hostname = settings.get_protocol_and_hostname();
|
||||||
|
let reset_link = format!("{}/password_change/{}", protocol_and_hostname, &token);
|
||||||
|
let email = user_email(user)?;
|
||||||
|
let body = &lang.password_reset_body(reset_link, &user.person.name);
|
||||||
|
send_email(subject, &email, &user.person.name, body, settings).await?;
|
||||||
|
|
||||||
|
// Insert the row after successful send, to avoid using daily reset limit while
|
||||||
|
// email sending is broken.
|
||||||
|
let local_user_id = user.local_user.id;
|
||||||
|
PasswordResetRequest::create(pool, local_user_id, token.clone()).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a verification email
|
||||||
|
pub async fn send_verification_email(
|
||||||
|
local_site: &LocalSite,
|
||||||
|
user: &LocalUserView,
|
||||||
|
new_email: &str,
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
settings: &Settings,
|
||||||
|
) -> LemmyResult<()> {
|
||||||
|
let form = EmailVerificationForm {
|
||||||
|
local_user_id: user.local_user.id,
|
||||||
|
email: new_email.to_string(),
|
||||||
|
verification_token: uuid::Uuid::new_v4().to_string(),
|
||||||
|
};
|
||||||
|
let verify_link = format!(
|
||||||
|
"{}/verify_email/{}",
|
||||||
|
settings.get_protocol_and_hostname(),
|
||||||
|
&form.verification_token
|
||||||
|
);
|
||||||
|
EmailVerification::create(pool, &form).await?;
|
||||||
|
|
||||||
|
let lang = user_language(user);
|
||||||
|
let subject = lang.verify_email_subject(&settings.hostname);
|
||||||
|
|
||||||
|
// If an application is required, use a translation that includes that warning.
|
||||||
|
let body = if local_site.registration_mode == RegistrationMode::RequireApplication {
|
||||||
|
lang.verify_email_body_with_application(&settings.hostname, &user.person.name, verify_link)
|
||||||
|
} else {
|
||||||
|
lang.verify_email_body(&settings.hostname, &user.person.name, verify_link)
|
||||||
|
};
|
||||||
|
|
||||||
|
send_email(&subject, new_email, &user.person.name, &body, settings).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if email was sent.
|
||||||
|
pub async fn send_verification_email_if_required(
|
||||||
|
local_site: &LocalSite,
|
||||||
|
user: &LocalUserView,
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
settings: &Settings,
|
||||||
|
) -> LemmyResult<bool> {
|
||||||
|
if !user.local_user.admin
|
||||||
|
&& local_site.require_email_verification
|
||||||
|
&& !user.local_user.email_verified
|
||||||
|
{
|
||||||
|
let email = user_email(user)?;
|
||||||
|
send_verification_email(local_site, user, &email, pool, settings).await?;
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_application_approved_email(
|
||||||
|
user: &LocalUserView,
|
||||||
|
settings: &Settings,
|
||||||
|
) -> LemmyResult<()> {
|
||||||
|
let lang = user_language(user);
|
||||||
|
let subject = lang.registration_approved_subject(&user.person.name);
|
||||||
|
let email = user_email(user)?;
|
||||||
|
let body = lang.registration_approved_body(&settings.hostname);
|
||||||
|
send_email(&subject, &email, &user.person.name, &body, settings).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_application_denied_email(
|
||||||
|
user: &LocalUserView,
|
||||||
|
deny_reason: Option<String>,
|
||||||
|
settings: &Settings,
|
||||||
|
) -> LemmyResult<()> {
|
||||||
|
let lang = user_language(user);
|
||||||
|
let subject = lang.registration_denied_subject(&user.person.name);
|
||||||
|
let email = user_email(user)?;
|
||||||
|
let body = lang.new_registration_denied_body(
|
||||||
|
&settings.hostname,
|
||||||
|
deny_reason.unwrap_or("unknown".to_string()),
|
||||||
|
);
|
||||||
|
send_email(&subject, &email, &user.person.name, &body, settings).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_email_verified_email(
|
||||||
|
user: &LocalUserView,
|
||||||
|
settings: &Settings,
|
||||||
|
) -> LemmyResult<()> {
|
||||||
|
let lang = user_language(user);
|
||||||
|
let subject = lang.email_verified_subject(&user.person.name);
|
||||||
|
let email = user_email(user)?;
|
||||||
|
let body = lang.email_verified_body();
|
||||||
|
send_email(&subject, &email, &user.person.name, body, settings).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
53
crates/email/src/admin.rs
Normal file
53
crates/email/src/admin.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
use crate::{send_email, user_language};
|
||||||
|
use lemmy_db_schema::utils::DbPool;
|
||||||
|
use lemmy_db_views::structs::LocalUserView;
|
||||||
|
use lemmy_utils::{error::LemmyResult, settings::structs::Settings};
|
||||||
|
|
||||||
|
/// Send a new applicant email notification to all admins
|
||||||
|
pub async fn send_new_applicant_email_to_admins(
|
||||||
|
applicant_username: &str,
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
settings: &Settings,
|
||||||
|
) -> LemmyResult<()> {
|
||||||
|
// Collect the admins with emails
|
||||||
|
let admins = LocalUserView::list_admins_with_emails(pool).await?;
|
||||||
|
|
||||||
|
let applications_link = &format!(
|
||||||
|
"{}/registration_applications",
|
||||||
|
settings.get_protocol_and_hostname(),
|
||||||
|
);
|
||||||
|
|
||||||
|
for admin in &admins {
|
||||||
|
if let Some(email) = &admin.local_user.email {
|
||||||
|
let lang = user_language(admin);
|
||||||
|
let subject = lang.new_application_subject(&settings.hostname, applicant_username);
|
||||||
|
let body = lang.new_application_body(applications_link);
|
||||||
|
send_email(&subject, email, &admin.person.name, &body, settings).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a report to all admins
|
||||||
|
pub async fn send_new_report_email_to_admins(
|
||||||
|
reporter_username: &str,
|
||||||
|
reported_username: &str,
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
settings: &Settings,
|
||||||
|
) -> LemmyResult<()> {
|
||||||
|
// Collect the admins with emails
|
||||||
|
let admins = LocalUserView::list_admins_with_emails(pool).await?;
|
||||||
|
|
||||||
|
let reports_link = &format!("{}/reports", settings.get_protocol_and_hostname(),);
|
||||||
|
|
||||||
|
for admin in &admins {
|
||||||
|
if let Some(email) = &admin.local_user.email {
|
||||||
|
let lang = user_language(admin);
|
||||||
|
let subject =
|
||||||
|
lang.new_report_subject(&settings.hostname, reported_username, reporter_username);
|
||||||
|
let body = lang.new_report_body(reports_link);
|
||||||
|
send_email(&subject, email, &admin.person.name, &body, settings).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -1,8 +1,9 @@
|
|||||||
use crate::{
|
use lemmy_db_schema::sensitive::SensitiveString;
|
||||||
|
use lemmy_db_views::structs::LocalUserView;
|
||||||
|
use lemmy_utils::{
|
||||||
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
|
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
|
||||||
settings::structs::Settings,
|
settings::structs::Settings,
|
||||||
};
|
};
|
||||||
use html2text;
|
|
||||||
use lettre::{
|
use lettre::{
|
||||||
message::{Mailbox, MultiPart},
|
message::{Mailbox, MultiPart},
|
||||||
transport::smtp::extension::ClientId,
|
transport::smtp::extension::ClientId,
|
||||||
@ -15,13 +16,20 @@ use std::{str::FromStr, sync::OnceLock};
|
|||||||
use translations::Lang;
|
use translations::Lang;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub mod translations {
|
pub mod account;
|
||||||
|
pub mod admin;
|
||||||
|
pub mod notifications;
|
||||||
|
mod translations {
|
||||||
rosetta_i18n::include_translations!();
|
rosetta_i18n::include_translations!();
|
||||||
}
|
}
|
||||||
|
|
||||||
type AsyncSmtpTransport = lettre::AsyncSmtpTransport<lettre::Tokio1Executor>;
|
type AsyncSmtpTransport = lettre::AsyncSmtpTransport<lettre::Tokio1Executor>;
|
||||||
|
|
||||||
pub async fn send_email(
|
fn inbox_link(settings: &Settings) -> String {
|
||||||
|
format!("{}/inbox", settings.get_protocol_and_hostname())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_email(
|
||||||
subject: &str,
|
subject: &str,
|
||||||
to_email: &str,
|
to_email: &str,
|
||||||
to_username: &str,
|
to_username: &str,
|
||||||
@ -74,10 +82,18 @@ pub async fn send_email(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::expect_used)]
|
#[allow(clippy::expect_used)]
|
||||||
pub fn lang_str_to_lang(lang: &str) -> Lang {
|
fn user_language(local_user_view: &LocalUserView) -> Lang {
|
||||||
let lang_id = LanguageId::new(lang);
|
let lang_id = LanguageId::new(&local_user_view.local_user.interface_language);
|
||||||
Lang::from_language_id(&lang_id).unwrap_or_else(|| {
|
Lang::from_language_id(&lang_id).unwrap_or_else(|| {
|
||||||
let en = LanguageId::new("en");
|
let en = LanguageId::new("en");
|
||||||
Lang::from_language_id(&en).expect("default language")
|
Lang::from_language_id(&en).expect("default language")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn user_email(local_user_view: &LocalUserView) -> LemmyResult<SensitiveString> {
|
||||||
|
local_user_view
|
||||||
|
.local_user
|
||||||
|
.email
|
||||||
|
.clone()
|
||||||
|
.ok_or(LemmyErrorType::EmailRequired.into())
|
||||||
|
}
|
135
crates/email/src/notifications.rs
Normal file
135
crates/email/src/notifications.rs
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
use crate::{inbox_link, send_email, user_language};
|
||||||
|
use lemmy_db_schema::{
|
||||||
|
newtypes::DbUrl,
|
||||||
|
source::{comment::Comment, person::Person, post::Post},
|
||||||
|
};
|
||||||
|
use lemmy_db_views::structs::LocalUserView;
|
||||||
|
use lemmy_utils::{
|
||||||
|
error::LemmyResult,
|
||||||
|
settings::structs::Settings,
|
||||||
|
utils::markdown::markdown_to_html,
|
||||||
|
};
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
|
pub async fn send_mention_email(
|
||||||
|
mention_user_view: &LocalUserView,
|
||||||
|
content: &str,
|
||||||
|
person: &Person,
|
||||||
|
link: DbUrl,
|
||||||
|
settings: &Settings,
|
||||||
|
) {
|
||||||
|
let inbox_link = inbox_link(settings);
|
||||||
|
let lang = user_language(mention_user_view);
|
||||||
|
let content = markdown_to_html(content);
|
||||||
|
send_email_to_user(
|
||||||
|
mention_user_view,
|
||||||
|
&lang.notification_mentioned_by_subject(&person.name),
|
||||||
|
&lang.notification_mentioned_by_body(&link, &content, &inbox_link, &person.name),
|
||||||
|
settings,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_comment_reply_email(
|
||||||
|
parent_user_view: &LocalUserView,
|
||||||
|
comment: &Comment,
|
||||||
|
person: &Person,
|
||||||
|
parent_comment: &Comment,
|
||||||
|
post: &Post,
|
||||||
|
settings: &Settings,
|
||||||
|
) -> LemmyResult<()> {
|
||||||
|
let inbox_link = inbox_link(settings);
|
||||||
|
let lang = user_language(parent_user_view);
|
||||||
|
let content = markdown_to_html(&comment.content);
|
||||||
|
send_email_to_user(
|
||||||
|
parent_user_view,
|
||||||
|
&lang.notification_comment_reply_subject(&person.name),
|
||||||
|
&lang.notification_comment_reply_body(
|
||||||
|
comment.local_url(settings)?,
|
||||||
|
&content,
|
||||||
|
&inbox_link,
|
||||||
|
&parent_comment.content,
|
||||||
|
&post.name,
|
||||||
|
&person.name,
|
||||||
|
),
|
||||||
|
settings,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_post_reply_email(
|
||||||
|
parent_user_view: &LocalUserView,
|
||||||
|
comment: &Comment,
|
||||||
|
person: &Person,
|
||||||
|
post: &Post,
|
||||||
|
settings: &Settings,
|
||||||
|
) -> LemmyResult<()> {
|
||||||
|
let inbox_link = inbox_link(settings);
|
||||||
|
let lang = user_language(parent_user_view);
|
||||||
|
let content = markdown_to_html(&comment.content);
|
||||||
|
send_email_to_user(
|
||||||
|
parent_user_view,
|
||||||
|
&lang.notification_post_reply_subject(&person.name),
|
||||||
|
&lang.notification_post_reply_body(
|
||||||
|
comment.local_url(settings)?,
|
||||||
|
&content,
|
||||||
|
&inbox_link,
|
||||||
|
&post.name,
|
||||||
|
&person.name,
|
||||||
|
),
|
||||||
|
settings,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_private_message_email(
|
||||||
|
sender: &LocalUserView,
|
||||||
|
local_recipient: &LocalUserView,
|
||||||
|
content: &str,
|
||||||
|
settings: &Settings,
|
||||||
|
) {
|
||||||
|
let inbox_link = inbox_link(settings);
|
||||||
|
let lang = user_language(local_recipient);
|
||||||
|
let sender_name = &sender.person.name;
|
||||||
|
let content = markdown_to_html(content);
|
||||||
|
send_email_to_user(
|
||||||
|
local_recipient,
|
||||||
|
&lang.notification_private_message_subject(sender_name),
|
||||||
|
&lang.notification_private_message_body(inbox_link, &content, sender_name),
|
||||||
|
settings,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_email_to_user(
|
||||||
|
local_user_view: &LocalUserView,
|
||||||
|
subject: &str,
|
||||||
|
body: &str,
|
||||||
|
settings: &Settings,
|
||||||
|
) {
|
||||||
|
let banned = local_user_view
|
||||||
|
.instance_actions
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|i| i.received_ban)
|
||||||
|
.is_some();
|
||||||
|
if banned || !local_user_view.local_user.send_notifications_to_email {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(user_email) = &local_user_view.local_user.email {
|
||||||
|
match send_email(
|
||||||
|
subject,
|
||||||
|
user_email,
|
||||||
|
&local_user_view.person.name,
|
||||||
|
body,
|
||||||
|
settings,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_o) => _o,
|
||||||
|
Err(e) => warn!("{}", e),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
1
crates/email/translations
Submodule
1
crates/email/translations
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 56581d60250680947e3e328bab21bc9e169df22c
|
@ -25,7 +25,6 @@ workspace = true
|
|||||||
full = [
|
full = [
|
||||||
"ts-rs",
|
"ts-rs",
|
||||||
"diesel",
|
"diesel",
|
||||||
"rosetta-i18n",
|
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"reqwest-middleware",
|
"reqwest-middleware",
|
||||||
"tracing",
|
"tracing",
|
||||||
@ -42,9 +41,6 @@ full = [
|
|||||||
"enum-map",
|
"enum-map",
|
||||||
"futures",
|
"futures",
|
||||||
"tokio",
|
"tokio",
|
||||||
"html2text",
|
|
||||||
"lettre",
|
|
||||||
"uuid",
|
|
||||||
"itertools",
|
"itertools",
|
||||||
"markdown-it",
|
"markdown-it",
|
||||||
"moka",
|
"moka",
|
||||||
@ -68,19 +64,10 @@ futures = { workspace = true, optional = true }
|
|||||||
diesel = { workspace = true, optional = true, features = ["chrono"] }
|
diesel = { workspace = true, optional = true, features = ["chrono"] }
|
||||||
http = { workspace = true, optional = true }
|
http = { workspace = true, optional = true }
|
||||||
doku = { workspace = true, features = ["url-2"], optional = true }
|
doku = { workspace = true, features = ["url-2"], optional = true }
|
||||||
uuid = { workspace = true, optional = true, features = ["v4"] }
|
|
||||||
rosetta-i18n = { workspace = true, optional = true }
|
|
||||||
tokio = { workspace = true, optional = true }
|
tokio = { workspace = true, optional = true }
|
||||||
urlencoding = { workspace = true, optional = true }
|
urlencoding = { workspace = true, optional = true }
|
||||||
html2text = { version = "0.14.0", optional = true }
|
|
||||||
deser-hjson = { version = "2.2.4", optional = true }
|
deser-hjson = { version = "2.2.4", optional = true }
|
||||||
smart-default = { version = "0.7.1", optional = true }
|
smart-default = { version = "0.7.1", optional = true }
|
||||||
lettre = { version = "0.11.12", default-features = false, features = [
|
|
||||||
"builder",
|
|
||||||
"smtp-transport",
|
|
||||||
"tokio1-rustls-tls",
|
|
||||||
"pool",
|
|
||||||
], optional = true }
|
|
||||||
markdown-it = { version = "0.6.1", optional = true }
|
markdown-it = { version = "0.6.1", optional = true }
|
||||||
ts-rs = { workspace = true, optional = true }
|
ts-rs = { workspace = true, optional = true }
|
||||||
enum-map = { version = "2.7", optional = true }
|
enum-map = { version = "2.7", optional = true }
|
||||||
@ -97,6 +84,3 @@ unicode-segmentation = "1.9.0"
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = { workspace = true }
|
pretty_assertions = { workspace = true }
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
rosetta-build = { version = "0.1.3", default-features = false }
|
|
||||||
|
@ -3,7 +3,6 @@ use cfg_if::cfg_if;
|
|||||||
cfg_if! {
|
cfg_if! {
|
||||||
if #[cfg(feature = "full")] {
|
if #[cfg(feature = "full")] {
|
||||||
pub mod cache_header;
|
pub mod cache_header;
|
||||||
pub mod email;
|
|
||||||
pub mod rate_limit;
|
pub mod rate_limit;
|
||||||
pub mod request;
|
pub mod request;
|
||||||
pub mod response;
|
pub mod response;
|
||||||
|
@ -166,10 +166,10 @@ pub struct EmailConfig {
|
|||||||
/// https://docs.rs/lettre/0.11.14/lettre/transport/smtp/struct.AsyncSmtpTransport.html#method.from_url
|
/// https://docs.rs/lettre/0.11.14/lettre/transport/smtp/struct.AsyncSmtpTransport.html#method.from_url
|
||||||
#[default("smtp://localhost:25")]
|
#[default("smtp://localhost:25")]
|
||||||
#[doku(example = "smtps://user:pass@hostname:port")]
|
#[doku(example = "smtps://user:pass@hostname:port")]
|
||||||
pub(crate) connection: String,
|
pub connection: String,
|
||||||
/// Address to send emails from, eg "noreply@your-instance.com"
|
/// Address to send emails from, eg "noreply@your-instance.com"
|
||||||
#[doku(example = "noreply@example.com")]
|
#[doku(example = "noreply@example.com")]
|
||||||
pub(crate) smtp_from_address: String,
|
pub smtp_from_address: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, Default, Document)]
|
#[derive(Debug, Deserialize, Serialize, Clone, Default, Document)]
|
||||||
|
@ -1 +0,0 @@
|
|||||||
Subproject commit dcb89f4725f69c2d57070650df63622cdfb90f8d
|
|
Loading…
x
Reference in New Issue
Block a user