From 79f79a4e9a67a8e578a608fdd3ac0748dcf86224 Mon Sep 17 00:00:00 2001 From: Nutomic Date: Tue, 1 Apr 2025 08:24:05 +0000 Subject: [PATCH] Send out email after registration denied, email confirmed (fixes #5547) (#5553) * Send out email after registration denied, email confirmed (fixes #5547) * wip * wip2 * all compiling * cleanup * move to subfolders * update * more fixes * clippy * remove line --- .gitmodules | 2 +- Cargo.lock | 24 ++- Cargo.toml | 2 + crates/api/Cargo.toml | 1 + .../local_user/resend_verification_email.rs | 14 +- crates/api/src/local_user/reset_password.rs | 3 +- crates/api/src/local_user/save_settings.rs | 6 +- crates/api/src/local_user/verify_email.rs | 10 +- .../api/src/reports/comment_report/create.rs | 8 +- .../src/reports/community_report/create.rs | 3 +- crates/api/src/reports/post_report/create.rs | 8 +- .../reports/private_message_report/create.rs | 3 +- .../site/registration_applications/approve.rs | 21 +- .../site/registration_applications/tests.rs | 5 +- crates/api_common/Cargo.toml | 4 +- crates/api_common/src/build_response.rs | 59 ++---- crates/api_common/src/utils.rs | 193 +----------------- crates/api_crud/Cargo.toml | 1 + crates/api_crud/src/private_message/create.rs | 25 +-- crates/api_crud/src/user/create.rs | 44 ++-- crates/db_schema/src/impls/local_user.rs | 9 +- crates/email/Cargo.toml | 37 ++++ crates/{utils => email}/build.rs | 0 crates/email/src/account.rs | 127 ++++++++++++ crates/email/src/admin.rs | 53 +++++ .../{utils/src/email.rs => email/src/lib.rs} | 28 ++- crates/email/src/notifications.rs | 135 ++++++++++++ crates/email/translations | 1 + crates/utils/Cargo.toml | 16 -- crates/utils/src/lib.rs | 1 - crates/utils/src/settings/structs.rs | 4 +- crates/utils/translations | 1 - 32 files changed, 506 insertions(+), 342 deletions(-) create mode 100644 crates/email/Cargo.toml rename crates/{utils => email}/build.rs (100%) create mode 100644 crates/email/src/account.rs create mode 100644 crates/email/src/admin.rs rename crates/{utils/src/email.rs => email/src/lib.rs} (76%) create mode 100644 crates/email/src/notifications.rs create mode 160000 crates/email/translations delete mode 160000 crates/utils/translations diff --git a/.gitmodules b/.gitmodules index f673c7acb..64d19837d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "crates/utils/translations"] - path = crates/utils/translations + path = crates/email/translations url = https://github.com/LemmyNet/lemmy-translations.git branch = main diff --git a/Cargo.lock b/Cargo.lock index 6a106a554..e94a2bf00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3119,6 +3119,7 @@ dependencies = [ "lemmy_api_crud", "lemmy_db_schema", "lemmy_db_views", + "lemmy_email", "lemmy_utils", "pretty_assertions", "regex", @@ -3148,6 +3149,7 @@ dependencies = [ "jsonwebtoken", "lemmy_db_schema", "lemmy_db_views", + "lemmy_email", "lemmy_utils", "mime", "mime_guess", @@ -3166,7 +3168,6 @@ dependencies = [ "ts-rs", "url", "urlencoding", - "uuid", "webmention", "webpage", ] @@ -3186,6 +3187,7 @@ dependencies = [ "lemmy_api_common", "lemmy_db_schema", "lemmy_db_views", + "lemmy_email", "lemmy_utils", "regex", "serde", @@ -3309,6 +3311,21 @@ dependencies = [ "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]] name = "lemmy_federate" version = "1.0.0-alpha.4" @@ -3411,10 +3428,8 @@ dependencies = [ "enum-map", "futures", "git-version", - "html2text", "http 1.2.0", "itertools 0.14.0", - "lettre", "markdown-it", "markdown-it-block-spoiler", "markdown-it-footnote", @@ -3425,8 +3440,6 @@ dependencies = [ "pretty_assertions", "regex", "reqwest-middleware", - "rosetta-build", - "rosetta-i18n", "serde", "serde_json", "smart-default", @@ -3437,7 +3450,6 @@ dependencies = [ "unicode-segmentation", "url", "urlencoding", - "uuid", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 405810daf..b3766db62 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ members = [ "crates/db_views", "crates/routes", "crates/federate", + "crates/email", ] [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_db_views = { version = "=1.0.0-alpha.4", path = "./crates/db_views" } 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 = [ "actix-web", ] } diff --git a/crates/api/Cargo.toml b/crates/api/Cargo.toml index 1afcaa5d3..cfb392585 100644 --- a/crates/api/Cargo.toml +++ b/crates/api/Cargo.toml @@ -22,6 +22,7 @@ lemmy_utils = { workspace = true } lemmy_db_schema = { workspace = true, features = ["full"] } lemmy_db_views = { workspace = true, features = ["full"] } lemmy_api_common = { workspace = true, features = ["full"] } +lemmy_email = { workspace = true } activitypub_federation = { workspace = true } bcrypt = { workspace = true } actix-web = { workspace = true } diff --git a/crates/api/src/local_user/resend_verification_email.rs b/crates/api/src/local_user/resend_verification_email.rs index 72122ef61..cd4b5e709 100644 --- a/crates/api/src/local_user/resend_verification_email.rs +++ b/crates/api/src/local_user/resend_verification_email.rs @@ -1,11 +1,7 @@ use actix_web::web::{Data, Json}; -use lemmy_api_common::{ - context::LemmyContext, - person::ResendVerificationEmail, - utils::send_verification_email_if_required, - SuccessResponse, -}; +use lemmy_api_common::{context::LemmyContext, person::ResendVerificationEmail, SuccessResponse}; use lemmy_db_views::structs::{LocalUserView, SiteView}; +use lemmy_email::account::send_verification_email_if_required; use lemmy_utils::error::LemmyResult; 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?; send_verification_email_if_required( - &context, &site_view.local_site, - &local_user_view.local_user, - &local_user_view.person, + &local_user_view, + &mut context.pool(), + context.settings(), ) .await?; diff --git a/crates/api/src/local_user/reset_password.rs b/crates/api/src/local_user/reset_password.rs index 032c20be7..7f4e7de4a 100644 --- a/crates/api/src/local_user/reset_password.rs +++ b/crates/api/src/local_user/reset_password.rs @@ -2,10 +2,11 @@ use actix_web::web::{Data, Json}; use lemmy_api_common::{ context::LemmyContext, person::PasswordReset, - utils::{check_email_verified, send_password_reset_email}, + utils::check_email_verified, SuccessResponse, }; use lemmy_db_views::structs::{LocalUserView, SiteView}; +use lemmy_email::account::send_password_reset_email; use lemmy_utils::error::LemmyResult; use tracing::error; diff --git a/crates/api/src/local_user/save_settings.rs b/crates/api/src/local_user/save_settings.rs index dab9dfb9d..a75507189 100644 --- a/crates/api/src/local_user/save_settings.rs +++ b/crates/api/src/local_user/save_settings.rs @@ -3,7 +3,7 @@ use actix_web::web::Json; use lemmy_api_common::{ context::LemmyContext, person::SaveUserSettings, - utils::{get_url_blocklist, process_markdown_opt, send_verification_email, slur_regex}, + utils::{get_url_blocklist, process_markdown_opt, slur_regex}, SuccessResponse, }; use lemmy_db_schema::{ @@ -16,6 +16,7 @@ use lemmy_db_schema::{ utils::{diesel_opt_number_update, diesel_string_update}, }; use lemmy_db_views::structs::{LocalUserView, SiteView}; +use lemmy_email::account::send_verification_email; use lemmy_utils::{ error::{LemmyErrorType, LemmyResult}, 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?; send_verification_email( &site_view.local_site, - &local_user_view.local_user, - &local_user_view.person, + &local_user_view, email, &mut context.pool(), context.settings(), diff --git a/crates/api/src/local_user/verify_email.rs b/crates/api/src/local_user/verify_email.rs index 813b68364..2e12895c4 100644 --- a/crates/api/src/local_user/verify_email.rs +++ b/crates/api/src/local_user/verify_email.rs @@ -1,15 +1,11 @@ use actix_web::web::{Data, Json}; -use lemmy_api_common::{ - context::LemmyContext, - person::VerifyEmail, - utils::send_new_applicant_email_to_admins, - SuccessResponse, -}; +use lemmy_api_common::{context::LemmyContext, person::VerifyEmail, SuccessResponse}; use lemmy_db_schema::source::{ email_verification::EmailVerification, local_user::{LocalUser, LocalUserUpdateForm}, }; 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; pub async fn verify_email( @@ -48,5 +44,7 @@ pub async fn verify_email( .await?; } + send_email_verified_email(&local_user_view, context.settings()).await?; + Ok(Json(SuccessResponse::default())) } diff --git a/crates/api/src/reports/comment_report/create.rs b/crates/api/src/reports/comment_report/create.rs index aa1718d65..c914f8f8b 100644 --- a/crates/api/src/reports/comment_report/create.rs +++ b/crates/api/src/reports/comment_report/create.rs @@ -5,18 +5,14 @@ use lemmy_api_common::{ context::LemmyContext, reports::comment::{CommentReportResponse, CreateCommentReport}, send_activity::{ActivityChannel, SendActivityData}, - utils::{ - check_comment_deleted_or_removed, - check_community_user_action, - send_new_report_email_to_admins, - slur_regex, - }, + utils::{check_comment_deleted_or_removed, check_community_user_action, slur_regex}, }; use lemmy_db_schema::{ source::comment_report::{CommentReport, CommentReportForm}, traits::Reportable, }; use lemmy_db_views::structs::{CommentReportView, CommentView, LocalUserView, SiteView}; +use lemmy_email::admin::send_new_report_email_to_admins; use lemmy_utils::error::LemmyResult; /// Creates a comment report and notifies the moderators of the community diff --git a/crates/api/src/reports/community_report/create.rs b/crates/api/src/reports/community_report/create.rs index eaf49b6b0..c74a6dd68 100644 --- a/crates/api/src/reports/community_report/create.rs +++ b/crates/api/src/reports/community_report/create.rs @@ -3,7 +3,7 @@ use actix_web::web::{Data, Json}; use lemmy_api_common::{ context::LemmyContext, reports::community::{CommunityReportResponse, CreateCommunityReport}, - utils::{send_new_report_email_to_admins, slur_regex}, + utils::slur_regex, }; use lemmy_db_schema::{ source::{ @@ -13,6 +13,7 @@ use lemmy_db_schema::{ traits::{Crud, Reportable}, }; use lemmy_db_views::structs::{CommunityReportView, LocalUserView, SiteView}; +use lemmy_email::admin::send_new_report_email_to_admins; use lemmy_utils::error::LemmyResult; pub async fn create_community_report( diff --git a/crates/api/src/reports/post_report/create.rs b/crates/api/src/reports/post_report/create.rs index 00af98d6d..9ace376a4 100644 --- a/crates/api/src/reports/post_report/create.rs +++ b/crates/api/src/reports/post_report/create.rs @@ -5,18 +5,14 @@ use lemmy_api_common::{ context::LemmyContext, reports::post::{CreatePostReport, PostReportResponse}, send_activity::{ActivityChannel, SendActivityData}, - utils::{ - check_community_user_action, - check_post_deleted_or_removed, - send_new_report_email_to_admins, - slur_regex, - }, + utils::{check_community_user_action, check_post_deleted_or_removed, slur_regex}, }; use lemmy_db_schema::{ source::post_report::{PostReport, PostReportForm}, traits::Reportable, }; use lemmy_db_views::structs::{LocalUserView, PostReportView, PostView, SiteView}; +use lemmy_email::admin::send_new_report_email_to_admins; use lemmy_utils::error::LemmyResult; /// Creates a post report and notifies the moderators of the community diff --git a/crates/api/src/reports/private_message_report/create.rs b/crates/api/src/reports/private_message_report/create.rs index 5b86d37ab..f99c22341 100644 --- a/crates/api/src/reports/private_message_report/create.rs +++ b/crates/api/src/reports/private_message_report/create.rs @@ -3,7 +3,7 @@ use actix_web::web::{Data, Json}; use lemmy_api_common::{ context::LemmyContext, reports::private_message::{CreatePrivateMessageReport, PrivateMessageReportResponse}, - utils::{send_new_report_email_to_admins, slur_regex}, + utils::slur_regex, }; use lemmy_db_schema::{ source::{ @@ -13,6 +13,7 @@ use lemmy_db_schema::{ traits::{Crud, Reportable}, }; use lemmy_db_views::structs::{LocalUserView, PrivateMessageReportView, SiteView}; +use lemmy_email::admin::send_new_report_email_to_admins; use lemmy_utils::error::{LemmyErrorType, LemmyResult}; pub async fn create_pm_report( diff --git a/crates/api/src/site/registration_applications/approve.rs b/crates/api/src/site/registration_applications/approve.rs index 1129d8bf8..a64a29697 100644 --- a/crates/api/src/site/registration_applications/approve.rs +++ b/crates/api/src/site/registration_applications/approve.rs @@ -4,7 +4,7 @@ use diesel_async::{scoped_futures::ScopedFutureExt, AsyncConnection}; use lemmy_api_common::{ context::LemmyContext, site::{ApproveRegistrationApplication, RegistrationApplicationResponse}, - utils::{is_admin, send_application_approved_email}, + utils::is_admin, }; use lemmy_db_schema::{ source::{ @@ -15,6 +15,7 @@ use lemmy_db_schema::{ utils::{diesel_string_update, get_conn}, }; 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}; pub async fn approve_registration_application( @@ -58,14 +59,20 @@ pub async fn approve_registration_application( }) .await?; - if data.approve { - let approved_local_user_view = - LocalUserView::read(&mut context.pool(), approved_user_id).await?; - if approved_local_user_view.local_user.email.is_some() { - // Email sending may fail, but this won't revert the application approval + let approved_local_user_view = LocalUserView::read(&mut context.pool(), approved_user_id).await?; + if approved_local_user_view.local_user.email.is_some() { + // Email sending may fail, but this won't revert the application approval + if data.approve { 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 let registration_application = diff --git a/crates/api/src/site/registration_applications/tests.rs b/crates/api/src/site/registration_applications/tests.rs index 455abc606..8658fcc41 100644 --- a/crates/api/src/site/registration_applications/tests.rs +++ b/crates/api/src/site/registration_applications/tests.rs @@ -299,7 +299,7 @@ async fn test_application_approval() -> LemmyResult<()> { expected_total_applications, ); - approve_registration_application( + let deny = approve_registration_application( Json(ApproveRegistrationApplication { id: app_with_email_2.id, approve: false, @@ -308,7 +308,8 @@ async fn test_application_approval() -> LemmyResult<()> { context.reset_request_count(), admin_local_user_view.clone(), ) - .await?; + .await; + assert!(deny.is_err_and(|e| e.error_type == LemmyErrorType::NoEmailSetup)); expected_unread_applications -= 1; diff --git a/crates/api_common/Cargo.toml b/crates/api_common/Cargo.toml index 4e68dc09d..0f7e9f778 100644 --- a/crates/api_common/Cargo.toml +++ b/crates/api_common/Cargo.toml @@ -21,13 +21,13 @@ full = [ "tracing", "lemmy_db_views/full", "lemmy_utils/full", + "lemmy_email", "activitypub_federation", "encoding_rs", "reqwest-middleware", "webpage", "ts-rs", "tokio", - "uuid", "reqwest", "actix-web", "futures", @@ -46,6 +46,7 @@ full = [ lemmy_db_views = { workspace = true } lemmy_db_schema = { workspace = true } lemmy_utils = { workspace = true } +lemmy_email = { workspace = true, optional = true } activitypub_federation = { workspace = true, optional = true } serde = { workspace = true } serde_with = { workspace = true } @@ -55,7 +56,6 @@ tracing = { workspace = true, optional = true } reqwest-middleware = { workspace = true, optional = true } regex = { workspace = true } futures = { workspace = true, optional = true } -uuid = { workspace = true, optional = true } tokio = { workspace = true, optional = true } reqwest = { workspace = true, optional = true } ts-rs = { workspace = true, optional = true } diff --git a/crates/api_common/src/build_response.rs b/crates/api_common/src/build_response.rs index e279b8baf..821ecdc36 100644 --- a/crates/api_common/src/build_response.rs +++ b/crates/api_common/src/build_response.rs @@ -3,7 +3,7 @@ use crate::{ community::CommunityResponse, context::LemmyContext, 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 lemmy_db_schema::{ @@ -21,10 +21,12 @@ use lemmy_db_schema::{ traits::Crud, }; use lemmy_db_views::structs::{CommentView, CommunityView, LocalUserView, PostView}; -use lemmy_utils::{ - error::LemmyResult, - utils::{markdown::markdown_to_html, mention::MentionData}, +use lemmy_email::notifications::{ + send_comment_reply_email, + send_mention_email, + send_post_reply_email, }; +use lemmy_utils::{error::LemmyResult, utils::mention::MentionData}; pub async fn build_comment_response( 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 for mention in mentions .iter() @@ -187,15 +187,14 @@ pub async fn send_local_notifs( // Send an email to those local users that have notifications on if do_send_email { - let lang = &mention_user_view.local_user.interface_i18n_language(); - let content = markdown_to_html(&comment_content_or_post_body); - send_email_to_user( + send_mention_email( &mention_user_view, - &lang.notification_mentioned_by_subject(&person.name), - &lang.notification_mentioned_by_body(&link, &content, &inbox_link, &person.name), + &comment_content_or_post_body, + person, + link, context.settings(), ) - .await + .await; } } } @@ -240,22 +239,15 @@ pub async fn send_local_notifs( .ok(); if do_send_email { - let lang = &parent_user_view.local_user.interface_i18n_language(); - let content = markdown_to_html(&comment.content); - send_email_to_user( + send_comment_reply_email( &parent_user_view, - &lang.notification_comment_reply_subject(&person.name), - &lang.notification_comment_reply_body( - comment.local_url(context.settings())?, - &content, - &inbox_link, - &parent_comment.content, - &post.name, - &person.name, - ), + comment, + person, + &parent_comment, + &post, context.settings(), ) - .await + .await?; } } } @@ -293,21 +285,14 @@ pub async fn send_local_notifs( .ok(); if do_send_email { - let lang = &parent_user_view.local_user.interface_i18n_language(); - let content = markdown_to_html(&comment.content); - send_email_to_user( + send_post_reply_email( &parent_user_view, - &lang.notification_post_reply_subject(&person.name), - &lang.notification_post_reply_body( - comment.local_url(context.settings())?, - &content, - &inbox_link, - &post.name, - &person.name, - ), + comment, + person, + &post, context.settings(), ) - .await + .await?; } } } diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs index f0a4a5e15..bf3280d69 100644 --- a/crates/api_common/src/utils.rs +++ b/crates/api_common/src/utils.rs @@ -17,13 +17,11 @@ use lemmy_db_schema::{ source::{ comment::{Comment, CommentActions, CommentUpdateForm}, community::{Community, CommunityActions, CommunityUpdateForm}, - email_verification::{EmailVerification, EmailVerificationForm}, images::{ImageDetails, RemoteImage}, instance::{Instance, InstanceActions}, local_site::LocalSite, local_site_rate_limit::LocalSiteRateLimit, local_site_url_blocklist::LocalSiteUrlBlocklist, - local_user::LocalUser, mod_log::moderator::{ ModRemoveComment, ModRemoveCommentForm, @@ -31,7 +29,6 @@ use lemmy_db_schema::{ ModRemovePostForm, }, oauth_account::OAuthAccount, - password_reset_request::PasswordResetRequest, person::{Person, PersonActions, PersonUpdateForm}, post::{Post, PostActions, PostReadCommentsForm}, private_message::PrivateMessage, @@ -57,13 +54,9 @@ use lemmy_db_views::{ }, }; use lemmy_utils::{ - email::send_email, error::{LemmyError, LemmyErrorExt, LemmyErrorExt2, LemmyErrorType, LemmyResult}, rate_limit::{ActionType, BucketConfig}, - settings::{ - structs::{PictrsImageMode, Settings}, - SETTINGS, - }, + settings::{structs::PictrsImageMode, SETTINGS}, spawn_try_task, utils::{ markdown::{image_links::markdown_rewrite_image_links, markdown_check_for_blocked_urls}, @@ -76,7 +69,7 @@ use lemmy_utils::{ use moka::future::Cache; use regex::{escape, Regex, RegexSet}; use std::sync::LazyLock; -use tracing::{warn, Instrument}; +use tracing::Instrument; use url::{ParseError, Url}; use urlencoding::encode; use webmention::{Webmention, WebmentionError}; @@ -412,121 +405,6 @@ pub fn honeypot_check(honeypot: &Option) -> 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 { - 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( l: &LocalSiteRateLimit, ) -> EnumMap { @@ -592,73 +470,6 @@ pub async fn get_url_blocklist(context: &LemmyContext) -> LemmyResult ) } -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, local_site: Option<&LocalSite>) -> LemmyResult<()> { let is_nsfw = nsfw.unwrap_or_default(); let nsfw_disallowed = local_site.is_some_and(|s| s.disallow_nsfw_content); diff --git a/crates/api_crud/Cargo.toml b/crates/api_crud/Cargo.toml index 8369fcfa1..f1dd1386b 100644 --- a/crates/api_crud/Cargo.toml +++ b/crates/api_crud/Cargo.toml @@ -17,6 +17,7 @@ lemmy_utils = { workspace = true, features = ["full"] } lemmy_db_schema = { workspace = true, features = ["full"] } lemmy_db_views = { workspace = true, features = ["full"] } lemmy_api_common = { workspace = true, features = ["full"] } +lemmy_email = { workspace = true } activitypub_federation = { workspace = true } bcrypt = { workspace = true } actix-web = { workspace = true } diff --git a/crates/api_crud/src/private_message/create.rs b/crates/api_crud/src/private_message/create.rs index 63a59805c..6b14e4160 100644 --- a/crates/api_crud/src/private_message/create.rs +++ b/crates/api_crud/src/private_message/create.rs @@ -5,13 +5,7 @@ use lemmy_api_common::{ plugins::{plugin_hook_after, plugin_hook_before}, private_message::{CreatePrivateMessage, PrivateMessageResponse}, send_activity::{ActivityChannel, SendActivityData}, - utils::{ - check_private_messages_enabled, - get_url_blocklist, - process_markdown, - send_email_to_user, - slur_regex, - }, + utils::{check_private_messages_enabled, get_url_blocklist, process_markdown, slur_regex}, }; use lemmy_db_schema::{ source::{ @@ -21,9 +15,10 @@ use lemmy_db_schema::{ traits::{Blockable, Crud}, }; use lemmy_db_views::structs::{LocalUserView, PrivateMessageView}; +use lemmy_email::notifications::send_private_message_email; use lemmy_utils::{ 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( @@ -72,16 +67,12 @@ pub async fn create_private_message( // Send email to the local recipient, if one exists if view.recipient.local { - let recipient_id = data.recipient_id; - let local_recipient = LocalUserView::read_person(&mut context.pool(), recipient_id).await?; - let lang = &local_recipient.local_user.interface_i18n_language(); - let inbox_link = format!("{}/inbox", context.settings().get_protocol_and_hostname()); - let sender_name = &local_user_view.person.name; - let content = markdown_to_html(&content); - send_email_to_user( + let local_recipient = + LocalUserView::read_person(&mut context.pool(), data.recipient_id).await?; + send_private_message_email( + &local_user_view, &local_recipient, - &lang.notification_private_message_subject(sender_name), - &lang.notification_private_message_body(inbox_link, &content, sender_name), + &content, context.settings(), ) .await; diff --git a/crates/api_crud/src/user/create.rs b/crates/api_crud/src/user/create.rs index d8caf6504..893e31da4 100644 --- a/crates/api_crud/src/user/create.rs +++ b/crates/api_crud/src/user/create.rs @@ -13,8 +13,6 @@ use lemmy_api_common::{ generate_inbox_url, honeypot_check, password_length_check, - send_new_applicant_email_to_admins, - send_verification_email_if_required, slur_regex, }, }; @@ -36,6 +34,10 @@ use lemmy_db_schema::{ RegistrationMode, }; 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::{ error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult}, settings::structs::Settings, @@ -131,7 +133,7 @@ pub async fn register( let tx_data = data.clone(); let tx_local_site = local_site.clone(); let tx_settings = context.settings(); - let (person, local_user) = conn + let user = conn .transaction::<_, LemmyError, _>(|conn| { async move { // 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() }) @@ -189,11 +195,16 @@ pub async fn register( if !local_site.site_setup || (!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); } else { - login_response.verify_email_sent = - send_verification_email_if_required(&context, &local_site, &local_user, &person).await?; + login_response.verify_email_sent = send_verification_email_if_required( + &local_site, + &user, + &mut context.pool(), + context.settings(), + ) + .await?; if require_registration_application { login_response.registration_created = true; @@ -348,7 +359,7 @@ pub async fn authenticate_with_oauth( let tx_data = data.clone(); let tx_local_site = local_site.clone(); let tx_settings = context.settings(); - let (person, local_user) = conn + let user = conn .transaction::<_, LemmyError, _>(|conn| { async move { // make sure the username is provided @@ -412,16 +423,25 @@ pub async fn authenticate_with_oauth( login_response.registration_created = true; } } - Ok((person, local_user)) + Ok(LocalUserView { + person, + local_user, + instance_actions: None, + }) } .scope_boxed() }) .await?; // Check email is verified when required - login_response.verify_email_sent = - send_verification_email_if_required(&context, &local_site, &local_user, &person).await?; - local_user + login_response.verify_email_sent = send_verification_email_if_required( + &local_site, + &user, + &mut context.pool(), + context.settings(), + ) + .await?; + user.local_user } }; diff --git a/crates/db_schema/src/impls/local_user.rs b/crates/db_schema/src/impls/local_user.rs index 864a5e202..1129487a0 100644 --- a/crates/db_schema/src/impls/local_user.rs +++ b/crates/db_schema/src/impls/local_user.rs @@ -24,10 +24,7 @@ use diesel::{ QueryDsl, }; use diesel_async::RunQueryDsl; -use lemmy_utils::{ - email::{lang_str_to_lang, translations::Lang}, - error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, -}; +use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult}; impl LocalUser { pub async fn create( @@ -291,10 +288,6 @@ impl LocalUser { 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 diff --git a/crates/email/Cargo.toml b/crates/email/Cargo.toml new file mode 100644 index 000000000..ab7a081bf --- /dev/null +++ b/crates/email/Cargo.toml @@ -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 } diff --git a/crates/utils/build.rs b/crates/email/build.rs similarity index 100% rename from crates/utils/build.rs rename to crates/email/build.rs diff --git a/crates/email/src/account.rs b/crates/email/src/account.rs new file mode 100644 index 000000000..e45d813af --- /dev/null +++ b/crates/email/src/account.rs @@ -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 { + 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, + 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(()) +} diff --git a/crates/email/src/admin.rs b/crates/email/src/admin.rs new file mode 100644 index 000000000..9f70607c8 --- /dev/null +++ b/crates/email/src/admin.rs @@ -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(()) +} diff --git a/crates/utils/src/email.rs b/crates/email/src/lib.rs similarity index 76% rename from crates/utils/src/email.rs rename to crates/email/src/lib.rs index 5f08d9ecb..028b54b5b 100644 --- a/crates/utils/src/email.rs +++ b/crates/email/src/lib.rs @@ -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}, settings::structs::Settings, }; -use html2text; use lettre::{ message::{Mailbox, MultiPart}, transport::smtp::extension::ClientId, @@ -15,13 +16,20 @@ use std::{str::FromStr, sync::OnceLock}; use translations::Lang; use uuid::Uuid; -pub mod translations { +pub mod account; +pub mod admin; +pub mod notifications; +mod translations { rosetta_i18n::include_translations!(); } type AsyncSmtpTransport = lettre::AsyncSmtpTransport; -pub async fn send_email( +fn inbox_link(settings: &Settings) -> String { + format!("{}/inbox", settings.get_protocol_and_hostname()) +} + +async fn send_email( subject: &str, to_email: &str, to_username: &str, @@ -74,10 +82,18 @@ pub async fn send_email( } #[allow(clippy::expect_used)] -pub fn lang_str_to_lang(lang: &str) -> Lang { - let lang_id = LanguageId::new(lang); +fn user_language(local_user_view: &LocalUserView) -> Lang { + let lang_id = LanguageId::new(&local_user_view.local_user.interface_language); Lang::from_language_id(&lang_id).unwrap_or_else(|| { let en = LanguageId::new("en"); Lang::from_language_id(&en).expect("default language") }) } + +fn user_email(local_user_view: &LocalUserView) -> LemmyResult { + local_user_view + .local_user + .email + .clone() + .ok_or(LemmyErrorType::EmailRequired.into()) +} diff --git a/crates/email/src/notifications.rs b/crates/email/src/notifications.rs new file mode 100644 index 000000000..df3687ba3 --- /dev/null +++ b/crates/email/src/notifications.rs @@ -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), + }; + } +} diff --git a/crates/email/translations b/crates/email/translations new file mode 160000 index 000000000..56581d602 --- /dev/null +++ b/crates/email/translations @@ -0,0 +1 @@ +Subproject commit 56581d60250680947e3e328bab21bc9e169df22c diff --git a/crates/utils/Cargo.toml b/crates/utils/Cargo.toml index e575914aa..5e301d5d7 100644 --- a/crates/utils/Cargo.toml +++ b/crates/utils/Cargo.toml @@ -25,7 +25,6 @@ workspace = true full = [ "ts-rs", "diesel", - "rosetta-i18n", "actix-web", "reqwest-middleware", "tracing", @@ -42,9 +41,6 @@ full = [ "enum-map", "futures", "tokio", - "html2text", - "lettre", - "uuid", "itertools", "markdown-it", "moka", @@ -68,19 +64,10 @@ futures = { workspace = true, optional = true } diesel = { workspace = true, optional = true, features = ["chrono"] } http = { workspace = true, 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 } urlencoding = { workspace = true, optional = true } -html2text = { version = "0.14.0", optional = true } deser-hjson = { version = "2.2.4", 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 } ts-rs = { workspace = true, optional = true } enum-map = { version = "2.7", optional = true } @@ -97,6 +84,3 @@ unicode-segmentation = "1.9.0" [dev-dependencies] pretty_assertions = { workspace = true } - -[build-dependencies] -rosetta-build = { version = "0.1.3", default-features = false } diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index 4b74db0d3..ff8fd991c 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -3,7 +3,6 @@ use cfg_if::cfg_if; cfg_if! { if #[cfg(feature = "full")] { pub mod cache_header; - pub mod email; pub mod rate_limit; pub mod request; pub mod response; diff --git a/crates/utils/src/settings/structs.rs b/crates/utils/src/settings/structs.rs index 2f12c5b6b..e7ce886cc 100644 --- a/crates/utils/src/settings/structs.rs +++ b/crates/utils/src/settings/structs.rs @@ -166,10 +166,10 @@ pub struct EmailConfig { /// https://docs.rs/lettre/0.11.14/lettre/transport/smtp/struct.AsyncSmtpTransport.html#method.from_url #[default("smtp://localhost:25")] #[doku(example = "smtps://user:pass@hostname:port")] - pub(crate) connection: String, + pub connection: String, /// Address to send emails from, eg "noreply@your-instance.com" #[doku(example = "noreply@example.com")] - pub(crate) smtp_from_address: String, + pub smtp_from_address: String, } #[derive(Debug, Deserialize, Serialize, Clone, Default, Document)] diff --git a/crates/utils/translations b/crates/utils/translations deleted file mode 160000 index dcb89f472..000000000 --- a/crates/utils/translations +++ /dev/null @@ -1 +0,0 @@ -Subproject commit dcb89f4725f69c2d57070650df63622cdfb90f8d