Some fixes from suggestions.

This commit is contained in:
Dessalines 2022-10-13 18:06:09 -04:00
parent c969675b9b
commit 8b4996c10c
41 changed files with 484 additions and 564 deletions

View file

@ -44,7 +44,7 @@ lemmy_api_common = { version = "=0.16.5", path = "crates/api_common" }
lemmy_websocket = { version = "=0.16.5", path = "./crates/websocket" }
lemmy_routes = { version = "=0.16.5", path = "./crates/routes" }
activitypub_federation = "0.2.0"
diesel = { version = "2.0.0", features = ["64-column-tables"] }
diesel = "2.0.0"
diesel_migrations = "2.0.0"
serde = { version = "1.0.145", features = ["derive"] }
actix = "0.13.0"

View file

@ -23,6 +23,18 @@
api_key: "string"
}
# Email sending configuration. All options except login/password are mandatory
email: {
# Hostname and port of the smtp server
smtp_server: "localhost:25"
# Login name for smtp server
smtp_login: "string"
# Password to login to the smtp server
smtp_password: "string"
# Address to send emails from, eg noreply@your-instance.com
smtp_from_address: "noreply@example.com"
# Whether or not smtp connections should use tls. Can be none, tls, or starttls
tls_type: "none"
}
# Parameters for automatic configuration of new instance (only used at first start)
setup: {
# Username for the admin user

View file

@ -2,9 +2,8 @@ use crate::Perform;
use actix_web::web::Data;
use lemmy_api_common::{
person::{PasswordReset, PasswordResetResponse},
utils::{blocking, local_site_to_email_config, send_password_reset_email},
utils::{blocking, send_password_reset_email},
};
use lemmy_db_schema::source::local_site::LocalSite;
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
@ -21,8 +20,6 @@ impl Perform for PasswordReset {
) -> Result<PasswordResetResponse, LemmyError> {
let data: &PasswordReset = self;
let local_site = blocking(context.pool(), LocalSite::read).await??;
// Fetch that email
let email = data.email.to_lowercase();
let local_user_view = blocking(context.pool(), move |conn| {
@ -32,14 +29,7 @@ impl Perform for PasswordReset {
.map_err(|e| LemmyError::from_error_message(e, "couldnt_find_that_username_or_email"))?;
// Email the pure token to the user.
let email_config = local_site_to_email_config(&local_site)?;
send_password_reset_email(
&local_user_view,
context.pool(),
context.settings(),
&email_config,
)
.await?;
send_password_reset_email(&local_user_view, context.pool(), context.settings()).await?;
Ok(PasswordResetResponse {})
}
}

View file

@ -2,12 +2,7 @@ use crate::Perform;
use actix_web::web::Data;
use lemmy_api_common::{
person::{LoginResponse, SaveUserSettings},
utils::{
blocking,
get_local_user_view_from_jwt,
local_site_to_email_config,
send_verification_email,
},
utils::{blocking, get_local_user_view_from_jwt, send_verification_email},
};
use lemmy_db_schema::{
source::{
@ -55,14 +50,7 @@ impl Perform for SaveUserSettings {
let previous_email = local_user_view.local_user.email.clone().unwrap_or_default();
// Only send the verification email if there was an email change
if previous_email.ne(email) {
let email_config = local_site_to_email_config(&local_site)?;
send_verification_email(
&local_user_view,
email,
context.pool(),
context.settings(),
&email_config,
)
send_verification_email(&local_user_view, email, context.pool(), context.settings())
.await?;
}
}

View file

@ -2,12 +2,11 @@ use crate::Perform;
use actix_web::web::Data;
use lemmy_api_common::{
person::{VerifyEmail, VerifyEmailResponse},
utils::{blocking, local_site_to_email_config, send_email_verification_success},
utils::{blocking, send_email_verification_success},
};
use lemmy_db_schema::{
source::{
email_verification::EmailVerification,
local_site::LocalSite,
local_user::{LocalUser, LocalUserUpdateForm},
},
traits::Crud,
@ -49,9 +48,7 @@ impl Perform for VerifyEmail {
})
.await??;
let local_site = blocking(context.pool(), LocalSite::read).await??;
let email_config = local_site_to_email_config(&local_site)?;
send_email_verification_success(&local_user_view, context.settings(), &email_config)?;
send_email_verification_success(&local_user_view, context.settings())?;
blocking(context.pool(), move |conn| {
EmailVerification::delete_old_tokens_for_local_user(conn, local_user_id)

View file

@ -2,17 +2,10 @@ use crate::Perform;
use actix_web::web::Data;
use lemmy_api_common::{
site::{ApproveRegistrationApplication, RegistrationApplicationResponse},
utils::{
blocking,
get_local_user_view_from_jwt,
is_admin,
local_site_to_email_config,
send_application_approved_email,
},
utils::{blocking, get_local_user_view_from_jwt, is_admin, send_application_approved_email},
};
use lemmy_db_schema::{
source::{
local_site::LocalSite,
local_user::{LocalUser, LocalUserUpdateForm},
registration_application::{RegistrationApplication, RegistrationApplicationUpdateForm},
},
@ -71,13 +64,7 @@ impl Perform for ApproveRegistrationApplication {
.await??;
if approved_local_user_view.local_user.email.is_some() {
let local_site = blocking(context.pool(), LocalSite::read).await??;
let email_config = local_site_to_email_config(&local_site)?;
send_application_approved_email(
&approved_local_user_view,
context.settings(),
&email_config,
)?;
send_application_approved_email(&approved_local_user_view, context.settings())?;
}
}

View file

@ -148,16 +148,10 @@ pub struct CreateSite {
pub federation_strict_allowlist: Option<bool>,
pub federation_http_fetch_retry_limit: Option<i32>,
pub federation_worker_count: Option<i32>,
pub email_enabled: Option<bool>,
pub email_smtp_server: Option<String>,
pub email_smtp_login: Option<String>,
pub email_smtp_password: Option<String>,
pub email_smtp_from_address: Option<String>,
pub email_tls_type: Option<String>,
pub captcha_enabled: Option<bool>,
pub captcha_difficulty: Option<String>,
pub allowed_instances: Option<String>,
pub blocked_instances: Option<String>,
pub allowed_instances: Option<Vec<String>>,
pub blocked_instances: Option<Vec<String>>,
}
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
@ -200,16 +194,10 @@ pub struct EditSite {
pub federation_strict_allowlist: Option<bool>,
pub federation_http_fetch_retry_limit: Option<i32>,
pub federation_worker_count: Option<i32>,
pub email_enabled: Option<bool>,
pub email_smtp_server: Option<String>,
pub email_smtp_login: Option<String>,
pub email_smtp_password: Option<String>,
pub email_smtp_from_address: Option<String>,
pub email_tls_type: Option<String>,
pub captcha_enabled: Option<bool>,
pub captcha_difficulty: Option<String>,
pub allowed_instances: Option<String>,
pub blocked_instances: Option<String>,
pub allowed_instances: Option<Vec<String>>,
pub blocked_instances: Option<Vec<String>>,
pub auth: Sensitive<String>,
}

View file

@ -9,6 +9,7 @@ use lemmy_db_schema::{
email_verification::{EmailVerification, EmailVerificationForm},
instance::Instance,
local_site::LocalSite,
local_site_rate_limit::LocalSiteRateLimit,
password_reset_request::PasswordResetRequest,
person::{Person, PersonUpdateForm},
person_block::PersonBlock,
@ -31,7 +32,7 @@ use lemmy_db_views_actor::structs::{
};
use lemmy_utils::{
claims::Claims,
email::{send_email, translations::Lang, EmailConfig},
email::{send_email, translations::Lang},
error::LemmyError,
rate_limit::RateLimitConfig,
settings::structs::Settings,
@ -342,8 +343,7 @@ pub fn send_email_to_user(
local_user_view: &LocalUserView,
subject: &str,
body: &str,
hostname: &str,
config: &EmailConfig,
settings: &Settings,
) {
if local_user_view.person.banned || !local_user_view.local_user.send_notifications_to_email {
return;
@ -355,8 +355,7 @@ pub fn send_email_to_user(
user_email,
&local_user_view.person.name,
body,
hostname,
config,
settings,
) {
Ok(_o) => _o,
Err(e) => warn!("{}", e),
@ -368,7 +367,6 @@ pub async fn send_password_reset_email(
user: &LocalUserView,
pool: &DbPool,
settings: &Settings,
config: &EmailConfig,
) -> Result<(), LemmyError> {
// Generate a random token
let token = generate_random_string();
@ -387,14 +385,7 @@ pub async fn send_password_reset_email(
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.hostname,
config,
)
send_email(subject, email, &user.person.name, body, settings)
}
/// Send a verification email
@ -403,7 +394,6 @@ pub async fn send_verification_email(
new_email: &str,
pool: &DbPool,
settings: &Settings,
config: &EmailConfig,
) -> Result<(), LemmyError> {
let form = EmailVerificationForm {
local_user_id: user.local_user.id,
@ -420,14 +410,7 @@ pub async fn send_verification_email(
let lang = get_interface_language(user);
let subject = lang.verify_email_subject(&settings.hostname);
let body = lang.verify_email_body(&settings.hostname, &user.person.name, verify_link);
send_email(
&subject,
new_email,
&user.person.name,
&body,
&settings.hostname,
config,
)?;
send_email(&subject, new_email, &user.person.name, &body, settings)?;
Ok(())
}
@ -435,20 +418,12 @@ pub async fn send_verification_email(
pub fn send_email_verification_success(
user: &LocalUserView,
settings: &Settings,
config: &EmailConfig,
) -> Result<(), LemmyError> {
let email = &user.local_user.email.to_owned().expect("email");
let lang = get_interface_language(user);
let subject = &lang.email_verified_subject(&user.person.actor_id);
let body = &lang.email_verified_body();
send_email(
subject,
email,
&user.person.name,
body,
&settings.hostname,
config,
)
send_email(subject, email, &user.person.name, body, settings)
}
pub fn get_interface_language(user: &LocalUserView) -> Lang {
@ -467,39 +442,23 @@ fn lang_str_to_lang(lang: &str) -> Lang {
})
}
pub fn local_site_to_email_config(local_site: &LocalSite) -> Result<EmailConfig, LemmyError> {
let ls = local_site.to_owned();
let err_msg = LemmyError::from_message("no_email_setup");
if ls.email_enabled {
Ok(EmailConfig {
smtp_server: ls.email_smtp_server.as_ref().ok_or(err_msg)?.to_string(),
smtp_login: ls.email_smtp_login,
smtp_password: ls.email_smtp_password,
smtp_from_address: ls.email_smtp_from_address,
tls_type: ls.email_tls_type,
})
} else {
Err(err_msg)
}
}
pub fn local_site_to_rate_limit_config(local_site: &LocalSite) -> RateLimitConfig {
let l = local_site;
pub fn local_site_rate_limit_to_rate_limit_config(
local_site_rate_limit: &LocalSiteRateLimit,
) -> RateLimitConfig {
let l = local_site_rate_limit;
RateLimitConfig {
message: l.rate_limit_message,
message_per_second: l.rate_limit_message_per_second,
post: l.rate_limit_post,
post_per_second: l.rate_limit_post_per_second,
register: l.rate_limit_register,
register_per_second: l.rate_limit_register_per_second,
image: l.rate_limit_image,
image_per_second: l.rate_limit_image_per_second,
comment: l.rate_limit_comment,
comment_per_second: l.rate_limit_comment_per_second,
search: l.rate_limit_search,
search_per_second: l.rate_limit_search_per_second,
message: l.message,
message_per_second: l.message_per_second,
post: l.post,
post_per_second: l.post_per_second,
register: l.register,
register_per_second: l.register_per_second,
image: l.image,
image_per_second: l.image_per_second,
comment: l.comment,
comment_per_second: l.comment_per_second,
search: l.search,
search_per_second: l.search_per_second,
}
}
@ -510,20 +469,12 @@ pub fn local_site_to_slur_regex(local_site: &LocalSite) -> Option<Regex> {
pub fn send_application_approved_email(
user: &LocalUserView,
settings: &Settings,
config: &EmailConfig,
) -> Result<(), LemmyError> {
let email = &user.local_user.email.to_owned().expect("email");
let lang = get_interface_language(user);
let subject = lang.registration_approved_subject(&user.person.actor_id);
let body = lang.registration_approved_body(&settings.hostname);
send_email(
&subject,
email,
&user.person.name,
&body,
&settings.hostname,
config,
)
send_email(&subject, email, &user.person.name, &body, settings)
}
/// Send a new applicant email notification to all admins
@ -531,7 +482,6 @@ pub async fn send_new_applicant_email_to_admins(
applicant_username: &str,
pool: &DbPool,
settings: &Settings,
config: &EmailConfig,
) -> Result<(), LemmyError> {
// Collect the admins with emails
let admins = blocking(pool, move |conn| {
@ -549,14 +499,7 @@ pub async fn send_new_applicant_email_to_admins(
let lang = get_interface_language_from_settings(admin);
let subject = lang.new_application_subject(applicant_username, &settings.hostname);
let body = lang.new_application_body(applications_link);
send_email(
&subject,
email,
&admin.person.name,
&body,
&settings.hostname,
config,
)?;
send_email(&subject, email, &admin.person.name, &body, settings)?;
}
Ok(())
}

View file

@ -146,7 +146,6 @@ impl PerformCrud for CreateComment {
&updated_comment,
&local_user_view.person,
&post,
&local_site,
true,
context,
)

View file

@ -9,7 +9,6 @@ use lemmy_db_schema::{
source::{
comment::{Comment, CommentUpdateForm},
community::Community,
local_site::LocalSite,
post::Post,
},
traits::Crud,
@ -35,7 +34,6 @@ impl PerformCrud for DeleteComment {
let data: &DeleteComment = self;
let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
let local_site = blocking(context.pool(), LocalSite::read).await??;
let comment_id = data.comment_id;
let orig_comment = blocking(context.pool(), move |conn| {
@ -79,7 +77,6 @@ impl PerformCrud for DeleteComment {
&updated_comment,
&local_user_view.person,
&post,
&local_site,
false,
context,
)

View file

@ -9,7 +9,6 @@ use lemmy_db_schema::{
source::{
comment::{Comment, CommentUpdateForm},
community::Community,
local_site::LocalSite,
moderator::{ModRemoveComment, ModRemoveCommentForm},
post::Post,
},
@ -36,7 +35,6 @@ impl PerformCrud for RemoveComment {
let data: &RemoveComment = self;
let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
let local_site = blocking(context.pool(), LocalSite::read).await??;
let comment_id = data.comment_id;
let orig_comment = blocking(context.pool(), move |conn| {
@ -90,7 +88,6 @@ impl PerformCrud for RemoveComment {
&updated_comment,
&local_user_view.person.clone(),
&post,
&local_site,
false,
context,
)

View file

@ -114,7 +114,6 @@ impl PerformCrud for EditComment {
&updated_comment,
&local_user_view.person,
&orig_comment.post,
&local_site,
false,
context,
)

View file

@ -7,7 +7,6 @@ use lemmy_api_common::{
check_person_block,
get_interface_language,
get_local_user_view_from_jwt,
local_site_to_email_config,
local_site_to_slur_regex,
send_email_to_user,
},
@ -128,8 +127,7 @@ impl PerformCrud for CreatePrivateMessage {
&content_slurs_removed,
&local_recipient.person.name,
),
&context.settings().hostname,
&local_site_to_email_config(&local_site)?,
context.settings(),
);
}

View file

@ -16,6 +16,7 @@ use lemmy_db_schema::{
newtypes::DbUrl,
source::{
local_site::{LocalSite, LocalSiteUpdateForm},
local_site_rate_limit::{LocalSiteRateLimit, LocalSiteRateLimitUpdateForm},
site::{Site, SiteUpdateForm},
},
traits::Crud,
@ -111,29 +112,11 @@ impl PerformCrud for CreateSite {
.updated(Some(Some(naive_now())))
.slur_filter_regex(diesel_option_overwrite(&data.slur_filter_regex))
.actor_name_max_length(data.actor_name_max_length)
.rate_limit_message(data.rate_limit_message)
.rate_limit_message_per_second(data.rate_limit_message_per_second)
.rate_limit_post(data.rate_limit_post)
.rate_limit_post_per_second(data.rate_limit_post_per_second)
.rate_limit_register(data.rate_limit_register)
.rate_limit_register_per_second(data.rate_limit_register_per_second)
.rate_limit_image(data.rate_limit_image)
.rate_limit_image_per_second(data.rate_limit_image_per_second)
.rate_limit_comment(data.rate_limit_comment)
.rate_limit_comment_per_second(data.rate_limit_comment_per_second)
.rate_limit_search(data.rate_limit_search)
.rate_limit_search_per_second(data.rate_limit_search_per_second)
.federation_enabled(data.federation_enabled)
.federation_debug(data.federation_debug)
.federation_strict_allowlist(data.federation_strict_allowlist)
.federation_http_fetch_retry_limit(data.federation_http_fetch_retry_limit)
.federation_worker_count(data.federation_worker_count)
.email_enabled(data.email_enabled)
.email_smtp_server(diesel_option_overwrite(&data.email_smtp_server))
.email_smtp_login(diesel_option_overwrite(&data.email_smtp_login))
.email_smtp_password(diesel_option_overwrite(&data.email_smtp_password))
.email_smtp_from_address(diesel_option_overwrite(&data.email_smtp_from_address))
.email_tls_type(data.email_tls_type.to_owned())
.captcha_enabled(data.captcha_enabled)
.captcha_difficulty(data.captcha_difficulty.to_owned())
.build();
@ -142,6 +125,26 @@ impl PerformCrud for CreateSite {
})
.await??;
let local_site_rate_limit_form = LocalSiteRateLimitUpdateForm::builder()
.message(data.rate_limit_message)
.message_per_second(data.rate_limit_message_per_second)
.post(data.rate_limit_post)
.post_per_second(data.rate_limit_post_per_second)
.register(data.rate_limit_register)
.register_per_second(data.rate_limit_register_per_second)
.image(data.rate_limit_image)
.image_per_second(data.rate_limit_image_per_second)
.comment(data.rate_limit_comment)
.comment_per_second(data.rate_limit_comment_per_second)
.search(data.rate_limit_search)
.search_per_second(data.rate_limit_search_per_second)
.build();
blocking(context.pool(), move |conn| {
LocalSiteRateLimit::update(conn, &local_site_rate_limit_form)
})
.await??;
let site_view = blocking(context.pool(), SiteView::read_local).await??;
Ok(SiteResponse { site_view })

View file

@ -13,9 +13,10 @@ use lemmy_api_common::{
use lemmy_db_schema::{
source::{
actor_language::SiteLanguage,
allowlist::AllowList,
blocklist::BlockList,
federation_allowlist::FederationAllowList,
federation_blocklist::FederationBlockList,
local_site::{LocalSite, LocalSiteUpdateForm},
local_site_rate_limit::{LocalSiteRateLimit, LocalSiteRateLimitUpdateForm},
local_user::LocalUser,
site::{Site, SiteUpdateForm},
},
@ -113,29 +114,11 @@ impl PerformCrud for EditSite {
.updated(Some(Some(naive_now())))
.slur_filter_regex(diesel_option_overwrite(&data.slur_filter_regex))
.actor_name_max_length(data.actor_name_max_length)
.rate_limit_message(data.rate_limit_message)
.rate_limit_message_per_second(data.rate_limit_message_per_second)
.rate_limit_post(data.rate_limit_post)
.rate_limit_post_per_second(data.rate_limit_post_per_second)
.rate_limit_register(data.rate_limit_register)
.rate_limit_register_per_second(data.rate_limit_register_per_second)
.rate_limit_image(data.rate_limit_image)
.rate_limit_image_per_second(data.rate_limit_image_per_second)
.rate_limit_comment(data.rate_limit_comment)
.rate_limit_comment_per_second(data.rate_limit_comment_per_second)
.rate_limit_search(data.rate_limit_search)
.rate_limit_search_per_second(data.rate_limit_search_per_second)
.federation_enabled(data.federation_enabled)
.federation_debug(data.federation_debug)
.federation_strict_allowlist(data.federation_strict_allowlist)
.federation_http_fetch_retry_limit(data.federation_http_fetch_retry_limit)
.federation_worker_count(data.federation_worker_count)
.email_enabled(data.email_enabled)
.email_smtp_server(diesel_option_overwrite(&data.email_smtp_server))
.email_smtp_login(diesel_option_overwrite(&data.email_smtp_login))
.email_smtp_password(diesel_option_overwrite(&data.email_smtp_password))
.email_smtp_from_address(diesel_option_overwrite(&data.email_smtp_from_address))
.email_tls_type(data.email_tls_type.to_owned())
.captcha_enabled(data.captcha_enabled)
.captcha_difficulty(data.captcha_difficulty.to_owned())
.build();
@ -146,15 +129,35 @@ impl PerformCrud for EditSite {
.await?
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_site"))?;
// Replace the blocked and allowed instances
let allowed = diesel_option_overwrite(&data.allowed_instances);
let local_site_rate_limit_form = LocalSiteRateLimitUpdateForm::builder()
.message(data.rate_limit_message)
.message_per_second(data.rate_limit_message_per_second)
.post(data.rate_limit_post)
.post_per_second(data.rate_limit_post_per_second)
.register(data.rate_limit_register)
.register_per_second(data.rate_limit_register_per_second)
.image(data.rate_limit_image)
.image_per_second(data.rate_limit_image_per_second)
.comment(data.rate_limit_comment)
.comment_per_second(data.rate_limit_comment_per_second)
.search(data.rate_limit_search)
.search_per_second(data.rate_limit_search_per_second)
.build();
blocking(context.pool(), move |conn| {
AllowList::replace(conn, allowed)
LocalSiteRateLimit::update(conn, &local_site_rate_limit_form)
})
.await??;
let blocked = diesel_option_overwrite(&data.blocked_instances);
// Replace the blocked and allowed instances
let allowed = data.allowed_instances.to_owned();
blocking(context.pool(), move |conn| {
BlockList::replace(conn, blocked)
FederationAllowList::replace(conn, allowed)
})
.await??;
let blocked = data.blocked_instances.to_owned();
blocking(context.pool(), move |conn| {
FederationBlockList::replace(conn, blocked)
})
.await??;

View file

@ -6,7 +6,6 @@ use lemmy_api_common::{
utils::{
blocking,
honeypot_check,
local_site_to_email_config,
local_site_to_slur_regex,
password_length_check,
send_new_applicant_email_to_admins,
@ -185,13 +184,7 @@ impl PerformCrud for Register {
// Email the admins
if local_site.application_email_admins {
let email_config = local_site_to_email_config(&local_site)?;
send_new_applicant_email_to_admins(
&data.username,
context.pool(),
context.settings(),
&email_config,
)
send_new_applicant_email_to_admins(&data.username, context.pool(), context.settings())
.await?;
}
@ -225,14 +218,7 @@ impl PerformCrud for Register {
.clone()
.expect("email was provided");
let email_config = local_site_to_email_config(&local_site)?;
send_verification_email(
&local_user_view,
&email,
context.pool(),
context.settings(),
&email_config,
)
send_verification_email(&local_user_view, &email, context.pool(), context.settings())
.await?;
login_response.verify_email_sent = true;
}

View file

@ -3,7 +3,7 @@ use activitypub_federation::core::object_id::ObjectId;
use lemmy_api_common::utils::blocking;
use lemmy_db_schema::{
newtypes::LocalUserId,
source::{comment::Comment, local_site::LocalSite, post::Post},
source::{comment::Comment, post::Post},
traits::Crud,
};
use lemmy_utils::{error::LemmyError, utils::scrape_text_for_mentions};
@ -27,22 +27,11 @@ async fn get_comment_notif_recipients(
.dereference(context, local_instance(context), request_counter)
.await?;
let local_site = blocking(context.pool(), LocalSite::read).await??;
// Note:
// Although mentions could be gotten from the post tags (they are included there), or the ccs,
// Its much easier to scrape them from the comment body, since the API has to do that
// anyway.
// TODO: for compatibility with other projects, it would be much better to read this from cc or tags
let mentions = scrape_text_for_mentions(&comment.content);
send_local_notifs(
mentions,
comment,
&*actor,
&post,
&local_site,
do_send_email,
context,
)
.await
send_local_notifs(mentions, comment, &*actor, &post, do_send_email, context).await
}

View file

@ -1,90 +0,0 @@
use crate::{
schema::allowlist,
source::{
allowlist::{AllowList, AllowListForm},
instance::{Instance, InstanceForm},
},
};
use diesel::{dsl::*, result::Error, *};
impl AllowList {
pub fn replace(
conn: &mut PgConnection,
list_opt_str: Option<Option<String>>,
) -> Result<(), Error> {
conn.build_transaction().read_write().run(|conn| {
if let Some(list_str) = list_opt_str {
Self::clear(conn)?;
if let Some(list_replace_str) = list_str {
let remove_whitespace = list_replace_str.split_whitespace().collect::<String>();
let list = remove_whitespace.split(',').collect::<Vec<&str>>();
for domain in list {
// Upsert all of these as instances
let instance_form = InstanceForm {
domain: domain.to_string(),
updated: None,
};
let instance = Instance::create(conn, &instance_form)?;
let form = AllowListForm {
instance_id: instance.id,
updated: None,
};
insert_into(allowlist::table)
.values(form)
.get_result::<Self>(conn)?;
}
Ok(())
} else {
Ok(())
}
} else {
Ok(())
}
})
}
pub fn clear(conn: &mut PgConnection) -> Result<usize, Error> {
diesel::delete(allowlist::table).execute(conn)
}
}
#[cfg(test)]
mod tests {
use crate::{
source::{allowlist::AllowList, instance::Instance},
utils::establish_unpooled_connection,
};
use serial_test::serial;
#[test]
#[serial]
fn test_allowlist_insert_and_clear() {
let conn = &mut establish_unpooled_connection();
let allowed = Some(Some("tld1.xyz, tld2.xyz,tld3.xyz".to_string()));
AllowList::replace(conn, allowed).unwrap();
let allows = Instance::allowlist(conn).unwrap();
assert_eq!(3, allows.len());
assert_eq!(
vec![
"tld1.xyz".to_string(),
"tld2.xyz".to_string(),
"tld3.xyz".to_string()
],
allows
);
// Now test clearing them via Some(none)
let clear_allows = Some(None);
AllowList::replace(conn, clear_allows).unwrap();
let allows = Instance::allowlist(conn).unwrap();
assert_eq!(0, allows.len());
Instance::delete_all(conn).unwrap();
}
}

View file

@ -1,51 +0,0 @@
use crate::{
schema::blocklist,
source::{
blocklist::{BlockList, BlockListForm},
instance::{Instance, InstanceForm},
},
};
use diesel::{dsl::*, result::Error, *};
impl BlockList {
pub fn replace(
conn: &mut PgConnection,
list_opt_str: Option<Option<String>>,
) -> Result<(), Error> {
conn.build_transaction().read_write().run(|conn| {
if let Some(list_str) = list_opt_str {
Self::clear(conn)?;
if let Some(list_replace_str) = list_str {
let remove_whitespace = list_replace_str.split_whitespace().collect::<String>();
let list = remove_whitespace.split(',').collect::<Vec<&str>>();
for domain in list {
// Upsert all of these as instances
let instance_form = InstanceForm {
domain: domain.to_string(),
updated: None,
};
let instance = Instance::create(conn, &instance_form)?;
let form = BlockListForm {
instance_id: instance.id,
updated: None,
};
insert_into(blocklist::table)
.values(form)
.get_result::<Self>(conn)?;
}
Ok(())
} else {
Ok(())
}
} else {
Ok(())
}
})
}
pub fn clear(conn: &mut PgConnection) -> Result<usize, Error> {
diesel::delete(blocklist::table).execute(conn)
}
}

View file

@ -0,0 +1,85 @@
use crate::{
schema::federation_allowlist,
source::{
federation_allowlist::{FederationAllowList, FederationAllowListForm},
instance::{Instance, InstanceForm},
},
};
use diesel::{dsl::*, result::Error, *};
impl FederationAllowList {
pub fn replace(conn: &mut PgConnection, list_opt: Option<Vec<String>>) -> Result<(), Error> {
conn.build_transaction().read_write().run(|conn| {
if let Some(list) = list_opt {
Self::clear(conn)?;
for domain in list {
// Upsert all of these as instances
let instance_form = InstanceForm {
domain: domain.to_string(),
updated: None,
};
let instance = Instance::create(conn, &instance_form)?;
let form = FederationAllowListForm {
instance_id: instance.id,
updated: None,
};
insert_into(federation_allowlist::table)
.values(form)
.get_result::<Self>(conn)?;
}
Ok(())
} else {
Ok(())
}
})
}
pub fn clear(conn: &mut PgConnection) -> Result<usize, Error> {
diesel::delete(federation_allowlist::table).execute(conn)
}
}
#[cfg(test)]
mod tests {
use crate::{
source::{federation_allowlist::FederationAllowList, instance::Instance},
utils::establish_unpooled_connection,
};
use serial_test::serial;
#[test]
#[serial]
fn test_allowlist_insert_and_clear() {
let conn = &mut establish_unpooled_connection();
let allowed = Some(vec![
"tld1.xyz".to_string(),
"tld2.xyz".to_string(),
"tld3.xyz".to_string(),
]);
FederationAllowList::replace(conn, allowed).unwrap();
let allows = Instance::allowlist(conn).unwrap();
assert_eq!(3, allows.len());
assert_eq!(
vec![
"tld1.xyz".to_string(),
"tld2.xyz".to_string(),
"tld3.xyz".to_string()
],
allows
);
// Now test clearing them via Some(empty vec)
let clear_allows = Some(Vec::new());
FederationAllowList::replace(conn, clear_allows).unwrap();
let allows = Instance::allowlist(conn).unwrap();
assert_eq!(0, allows.len());
Instance::delete_all(conn).unwrap();
}
}

View file

@ -0,0 +1,42 @@
use crate::{
schema::federation_blocklist,
source::{
federation_blocklist::{FederationBlockList, FederationBlockListForm},
instance::{Instance, InstanceForm},
},
};
use diesel::{dsl::*, result::Error, *};
impl FederationBlockList {
pub fn replace(conn: &mut PgConnection, list_opt: Option<Vec<String>>) -> Result<(), Error> {
conn.build_transaction().read_write().run(|conn| {
if let Some(list) = list_opt {
Self::clear(conn)?;
for domain in list {
// Upsert all of these as instances
let instance_form = InstanceForm {
domain: domain.to_string(),
updated: None,
};
let instance = Instance::create(conn, &instance_form)?;
let form = FederationBlockListForm {
instance_id: instance.id,
updated: None,
};
insert_into(federation_blocklist::table)
.values(form)
.get_result::<Self>(conn)?;
}
Ok(())
} else {
Ok(())
}
})
}
pub fn clear(conn: &mut PgConnection) -> Result<usize, Error> {
diesel::delete(federation_blocklist::table).execute(conn)
}
}

View file

@ -1,6 +1,6 @@
use crate::{
newtypes::InstanceId,
schema::{allowlist, blocklist, instance},
schema::{federation_allowlist, federation_blocklist, instance},
source::instance::{Instance, InstanceForm},
};
use diesel::{dsl::*, result::Error, *};
@ -23,22 +23,22 @@ impl Instance {
}
pub fn allowlist(conn: &mut PgConnection) -> Result<Vec<String>, Error> {
instance::table
.inner_join(allowlist::table)
.inner_join(federation_allowlist::table)
.select(instance::domain)
.load::<String>(conn)
}
pub fn blocklist(conn: &mut PgConnection) -> Result<Vec<String>, Error> {
instance::table
.inner_join(blocklist::table)
.inner_join(federation_blocklist::table)
.select(instance::domain)
.load::<String>(conn)
}
pub fn linked(conn: &mut PgConnection) -> Result<Vec<String>, Error> {
instance::table
.left_join(blocklist::table)
.filter(blocklist::id.is_null())
.left_join(federation_blocklist::table)
.filter(federation_blocklist::id.is_null())
.select(instance::domain)
.load::<String>(conn)
}

View file

@ -0,0 +1,25 @@
use crate::{schema::local_site_rate_limit, source::local_site_rate_limit::*};
use diesel::{dsl::*, result::Error, *};
impl LocalSiteRateLimit {
pub fn read(conn: &mut PgConnection) -> Result<Self, Error> {
local_site_rate_limit::table.first::<Self>(conn)
}
pub fn create(
conn: &mut PgConnection,
form: &LocalSiteRateLimitInsertForm,
) -> Result<Self, Error> {
insert_into(local_site_rate_limit::table)
.values(form)
.get_result::<Self>(conn)
}
pub fn update(
conn: &mut PgConnection,
form: &LocalSiteRateLimitUpdateForm,
) -> Result<Self, Error> {
diesel::update(local_site_rate_limit::table)
.set(form)
.get_result::<Self>(conn)
}
}

View file

@ -1,16 +1,17 @@
pub mod activity;
pub mod actor_language;
pub mod allowlist;
pub mod blocklist;
pub mod comment;
pub mod comment_reply;
pub mod comment_report;
pub mod community;
pub mod community_block;
pub mod email_verification;
pub mod federation_allowlist;
pub mod federation_blocklist;
pub mod instance;
pub mod language;
pub mod local_site;
pub mod local_site_rate_limit;
pub mod local_user;
pub mod moderator;
pub mod password_reset_request;

View file

@ -651,7 +651,7 @@ table! {
}
table! {
allowlist(id) {
federation_allowlist(id) {
id -> Int4,
instance_id -> Int4,
published -> Timestamp,
@ -660,7 +660,7 @@ table! {
}
table! {
blocklist(id) {
federation_blocklist(id) {
id -> Int4,
instance_id -> Int4,
published -> Timestamp,
@ -688,29 +688,11 @@ table! {
application_email_admins -> Bool,
slur_filter_regex -> Nullable<Text>,
actor_name_max_length -> Int4,
rate_limit_message -> Int4,
rate_limit_message_per_second-> Int4,
rate_limit_post -> Int4,
rate_limit_post_per_second -> Int4,
rate_limit_register -> Int4,
rate_limit_register_per_second -> Int4,
rate_limit_image -> Int4,
rate_limit_image_per_second -> Int4,
rate_limit_comment -> Int4,
rate_limit_comment_per_second -> Int4,
rate_limit_search -> Int4,
rate_limit_search_per_second -> Int4,
federation_enabled -> Bool,
federation_debug -> Bool,
federation_strict_allowlist -> Bool,
federation_http_fetch_retry_limit -> Int4,
federation_worker_count -> Int4,
email_enabled -> Bool,
email_smtp_server -> Nullable<Text>,
email_smtp_login -> Nullable<Text>,
email_smtp_password -> Nullable<Text>,
email_smtp_from_address -> Nullable<Text>,
email_tls_type -> Text,
captcha_enabled -> Bool,
captcha_difficulty -> Text,
published -> Timestamp,
@ -718,6 +700,27 @@ table! {
}
}
table! {
local_site_rate_limit(id) {
id -> Int4,
local_site_id -> Int4,
message -> Int4,
message_per_second-> Int4,
post -> Int4,
post_per_second -> Int4,
register -> Int4,
register_per_second -> Int4,
image -> Int4,
image_per_second -> Int4,
comment -> Int4,
comment_per_second -> Int4,
search -> Int4,
search_per_second -> Int4,
published -> Timestamp,
updated -> Nullable<Timestamp>,
}
}
joinable!(person_block -> person (person_id));
joinable!(comment -> person (creator_id));
@ -797,9 +800,10 @@ joinable!(admin_purge_post -> person (admin_person_id));
joinable!(site -> instance (instance_id));
joinable!(person -> instance (instance_id));
joinable!(community -> instance (instance_id));
joinable!(allowlist -> instance (instance_id));
joinable!(blocklist -> instance (instance_id));
joinable!(federation_allowlist -> instance (instance_id));
joinable!(federation_blocklist -> instance (instance_id));
joinable!(local_site -> site (site_id));
joinable!(local_site_rate_limit -> local_site (local_site_id));
allow_tables_to_appear_in_same_query!(
activity,
@ -855,7 +859,8 @@ allow_tables_to_appear_in_same_query!(
site_language,
community_language,
instance,
allowlist,
blocklist,
federation_allowlist,
federation_blocklist,
local_site,
local_site_rate_limit,
);

View file

@ -1,4 +1,4 @@
use crate::{newtypes::InstanceId, schema::allowlist};
use crate::{newtypes::InstanceId, schema::federation_allowlist};
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
@ -8,8 +8,8 @@ use std::fmt::Debug;
feature = "full",
diesel(belongs_to(crate::source::instance::Instance))
)]
#[cfg_attr(feature = "full", diesel(table_name = allowlist))]
pub struct AllowList {
#[cfg_attr(feature = "full", diesel(table_name = federation_allowlist))]
pub struct FederationAllowList {
pub id: i32,
pub instance_id: InstanceId,
pub published: chrono::NaiveDateTime,
@ -18,8 +18,8 @@ pub struct AllowList {
#[derive(Clone, Default)]
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = allowlist))]
pub struct AllowListForm {
#[cfg_attr(feature = "full", diesel(table_name = federation_allowlist))]
pub struct FederationAllowListForm {
pub instance_id: InstanceId,
pub updated: Option<chrono::NaiveDateTime>,
}

View file

@ -1,4 +1,4 @@
use crate::{newtypes::InstanceId, schema::blocklist};
use crate::{newtypes::InstanceId, schema::federation_blocklist};
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
@ -8,8 +8,8 @@ use std::fmt::Debug;
feature = "full",
diesel(belongs_to(crate::source::instance::Instance))
)]
#[cfg_attr(feature = "full", diesel(table_name = blocklist))]
pub struct BlockList {
#[cfg_attr(feature = "full", diesel(table_name = federation_blocklist))]
pub struct FederationBlockList {
pub id: i32,
pub instance_id: InstanceId,
pub published: chrono::NaiveDateTime,
@ -18,8 +18,8 @@ pub struct BlockList {
#[derive(Clone, Default)]
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = blocklist))]
pub struct BlockListForm {
#[cfg_attr(feature = "full", diesel(table_name = federation_blocklist))]
pub struct FederationBlockListForm {
pub instance_id: InstanceId,
pub updated: Option<chrono::NaiveDateTime>,
}

View file

@ -28,29 +28,11 @@ pub struct LocalSite {
pub application_email_admins: bool,
pub slur_filter_regex: Option<String>,
pub actor_name_max_length: i32,
pub rate_limit_message: i32,
pub rate_limit_message_per_second: i32,
pub rate_limit_post: i32,
pub rate_limit_post_per_second: i32,
pub rate_limit_register: i32,
pub rate_limit_register_per_second: i32,
pub rate_limit_image: i32,
pub rate_limit_image_per_second: i32,
pub rate_limit_comment: i32,
pub rate_limit_comment_per_second: i32,
pub rate_limit_search: i32,
pub rate_limit_search_per_second: i32,
pub federation_enabled: bool,
pub federation_debug: bool,
pub federation_strict_allowlist: bool,
pub federation_http_fetch_retry_limit: i32,
pub federation_worker_count: i32,
pub email_enabled: bool,
pub email_smtp_server: Option<String>,
pub email_smtp_login: Option<String>,
pub email_smtp_password: Option<String>,
pub email_smtp_from_address: Option<String>,
pub email_tls_type: String,
pub captcha_enabled: bool,
pub captcha_difficulty: String,
pub published: chrono::NaiveDateTime,
@ -80,29 +62,11 @@ pub struct LocalSiteInsertForm {
pub application_email_admins: Option<bool>,
pub slur_filter_regex: Option<String>,
pub actor_name_max_length: Option<i32>,
pub rate_limit_message: Option<i32>,
pub rate_limit_message_per_second: Option<i32>,
pub rate_limit_post: Option<i32>,
pub rate_limit_post_per_second: Option<i32>,
pub rate_limit_register: Option<i32>,
pub rate_limit_register_per_second: Option<i32>,
pub rate_limit_image: Option<i32>,
pub rate_limit_image_per_second: Option<i32>,
pub rate_limit_comment: Option<i32>,
pub rate_limit_comment_per_second: Option<i32>,
pub rate_limit_search: Option<i32>,
pub rate_limit_search_per_second: Option<i32>,
pub federation_enabled: Option<bool>,
pub federation_debug: Option<bool>,
pub federation_strict_allowlist: Option<bool>,
pub federation_http_fetch_retry_limit: Option<i32>,
pub federation_worker_count: Option<i32>,
pub email_enabled: Option<bool>,
pub email_smtp_server: Option<String>,
pub email_smtp_login: Option<String>,
pub email_smtp_password: Option<String>,
pub email_smtp_from_address: Option<String>,
pub email_tls_type: Option<String>,
pub captcha_enabled: Option<bool>,
pub captcha_difficulty: Option<String>,
}
@ -128,29 +92,11 @@ pub struct LocalSiteUpdateForm {
pub application_email_admins: Option<bool>,
pub slur_filter_regex: Option<Option<String>>,
pub actor_name_max_length: Option<i32>,
pub rate_limit_message: Option<i32>,
pub rate_limit_message_per_second: Option<i32>,
pub rate_limit_post: Option<i32>,
pub rate_limit_post_per_second: Option<i32>,
pub rate_limit_register: Option<i32>,
pub rate_limit_register_per_second: Option<i32>,
pub rate_limit_image: Option<i32>,
pub rate_limit_image_per_second: Option<i32>,
pub rate_limit_comment: Option<i32>,
pub rate_limit_comment_per_second: Option<i32>,
pub rate_limit_search: Option<i32>,
pub rate_limit_search_per_second: Option<i32>,
pub federation_enabled: Option<bool>,
pub federation_debug: Option<bool>,
pub federation_strict_allowlist: Option<bool>,
pub federation_http_fetch_retry_limit: Option<i32>,
pub federation_worker_count: Option<i32>,
pub email_enabled: Option<bool>,
pub email_smtp_server: Option<Option<String>>,
pub email_smtp_login: Option<Option<String>>,
pub email_smtp_password: Option<Option<String>>,
pub email_smtp_from_address: Option<Option<String>>,
pub email_tls_type: Option<String>,
pub captcha_enabled: Option<bool>,
pub captcha_difficulty: Option<String>,
pub updated: Option<Option<chrono::NaiveDateTime>>,

View file

@ -0,0 +1,73 @@
use crate::newtypes::LocalSiteId;
use serde::{Deserialize, Serialize};
use typed_builder::TypedBuilder;
#[cfg(feature = "full")]
use crate::schema::local_site_rate_limit;
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
#[cfg_attr(feature = "full", diesel(table_name = local_site_rate_limit))]
#[cfg_attr(
feature = "full",
diesel(belongs_to(crate::source::local_site::LocalSite))
)]
pub struct LocalSiteRateLimit {
pub id: i32,
pub local_site_id: LocalSiteId,
pub message: i32,
pub message_per_second: i32,
pub post: i32,
pub post_per_second: i32,
pub register: i32,
pub register_per_second: i32,
pub image: i32,
pub image_per_second: i32,
pub comment: i32,
pub comment_per_second: i32,
pub search: i32,
pub search_per_second: i32,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
}
#[derive(Clone, TypedBuilder)]
#[builder(field_defaults(default))]
#[cfg_attr(feature = "full", derive(Insertable))]
#[cfg_attr(feature = "full", diesel(table_name = local_site_rate_limit))]
pub struct LocalSiteRateLimitInsertForm {
#[builder(!default)]
pub local_site_id: LocalSiteId,
pub message: Option<i32>,
pub message_per_second: Option<i32>,
pub post: Option<i32>,
pub post_per_second: Option<i32>,
pub register: Option<i32>,
pub register_per_second: Option<i32>,
pub image: Option<i32>,
pub image_per_second: Option<i32>,
pub comment: Option<i32>,
pub comment_per_second: Option<i32>,
pub search: Option<i32>,
pub search_per_second: Option<i32>,
}
#[derive(Clone, TypedBuilder)]
#[builder(field_defaults(default))]
#[cfg_attr(feature = "full", derive(AsChangeset))]
#[cfg_attr(feature = "full", diesel(table_name = local_site_rate_limit))]
pub struct LocalSiteRateLimitUpdateForm {
pub message: Option<i32>,
pub message_per_second: Option<i32>,
pub post: Option<i32>,
pub post_per_second: Option<i32>,
pub register: Option<i32>,
pub register_per_second: Option<i32>,
pub image: Option<i32>,
pub image_per_second: Option<i32>,
pub comment: Option<i32>,
pub comment_per_second: Option<i32>,
pub search: Option<i32>,
pub search_per_second: Option<i32>,
pub updated: Option<Option<chrono::NaiveDateTime>>,
}

View file

@ -1,17 +1,18 @@
#[cfg(feature = "full")]
pub mod activity;
pub mod actor_language;
pub mod allowlist;
pub mod blocklist;
pub mod comment;
pub mod comment_reply;
pub mod comment_report;
pub mod community;
pub mod community_block;
pub mod email_verification;
pub mod federation_allowlist;
pub mod federation_blocklist;
pub mod instance;
pub mod language;
pub mod local_site;
pub mod local_site_rate_limit;
pub mod local_user;
pub mod moderator;
pub mod password_reset_request;

View file

@ -34,7 +34,6 @@ pub struct SiteInsertForm {
pub name: String,
pub sidebar: Option<String>,
pub updated: Option<chrono::NaiveDateTime>,
// when you want to null out a column, you have to send Some(None)), since sending None means you just don't want to update that column.
pub icon: Option<DbUrl>,
pub banner: Option<DbUrl>,
pub description: Option<String>,

View file

@ -11,6 +11,7 @@ pub trait Crud {
fn read(conn: &mut PgConnection, id: Self::IdType) -> Result<Self, Error>
where
Self: Sized;
/// when you want to null out a column, you have to send Some(None)), since sending None means you just don't want to update that column.
fn update(
conn: &mut PgConnection,
id: Self::IdType,

View file

@ -2,26 +2,31 @@ use crate::structs::SiteView;
use diesel::{result::Error, *};
use lemmy_db_schema::{
aggregates::structs::SiteAggregates,
schema::{local_site, site, site_aggregates},
source::{local_site::LocalSite, site::Site},
schema::{local_site, local_site_rate_limit, site, site_aggregates},
source::{local_site::LocalSite, local_site_rate_limit::LocalSiteRateLimit, site::Site},
};
impl SiteView {
pub fn read_local(conn: &mut PgConnection) -> Result<Self, Error> {
let (mut site, local_site, counts) = site::table
let (mut site, local_site, local_site_rate_limit, counts) = site::table
.inner_join(local_site::table)
.inner_join(
local_site_rate_limit::table.on(local_site::id.eq(local_site_rate_limit::local_site_id)),
)
.inner_join(site_aggregates::table)
.select((
site::all_columns,
local_site::all_columns,
local_site_rate_limit::all_columns,
site_aggregates::all_columns,
))
.first::<(Site, LocalSite, SiteAggregates)>(conn)?;
.first::<(Site, LocalSite, LocalSiteRateLimit, SiteAggregates)>(conn)?;
site.private_key = None;
Ok(SiteView {
site,
local_site,
local_site_rate_limit,
counts,
})
}

View file

@ -6,6 +6,7 @@ use lemmy_db_schema::{
community::CommunitySafe,
language::Language,
local_site::LocalSite,
local_site_rate_limit::LocalSiteRateLimit,
local_user::{LocalUser, LocalUserSettings},
person::{Person, PersonSafe},
post::Post,
@ -117,6 +118,7 @@ pub struct RegistrationApplicationView {
pub struct SiteView {
pub site: Site,
pub local_site: LocalSite,
pub local_site_rate_limit: LocalSiteRateLimit,
pub counts: SiteAggregates,
}

View file

@ -1,4 +1,4 @@
use crate::error::LemmyError;
use crate::{error::LemmyError, settings::structs::Settings};
use html2text;
use lettre::{
message::{Mailbox, MultiPart},
@ -8,7 +8,6 @@ use lettre::{
SmtpTransport,
Transport,
};
use serde::{Deserialize, Serialize};
use std::str::FromStr;
use uuid::Uuid;
@ -16,33 +15,21 @@ pub mod translations {
rosetta_i18n::include_translations!();
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct EmailConfig {
/// Hostname and port of the smtp server
/// example = "localhost:25"
pub smtp_server: String,
/// Login name for smtp server
pub smtp_login: Option<String>,
/// Password to login to the smtp server
pub smtp_password: Option<String>,
/// example = "noreply@example.com"
/// Address to send emails from, eg "noreply@your-instance.com"
pub smtp_from_address: Option<String>,
/// Whether or not smtp connections should use tls. Can be none, tls, or starttls
/// example = "none"
pub tls_type: String,
}
pub fn send_email(
subject: &str,
to_email: &str,
to_username: &str,
html: &str,
hostname: &str,
config: &EmailConfig,
settings: &Settings,
) -> Result<(), LemmyError> {
let email_config = settings
.email
.to_owned()
.ok_or_else(|| LemmyError::from_message("no_email_setup"))?;
let domain = settings.hostname.to_owned();
let (smtp_server, smtp_port) = {
let email_and_port = config.smtp_server.split(':').collect::<Vec<&str>>();
let email_and_port = email_config.smtp_server.split(':').collect::<Vec<&str>>();
if email_and_port.len() == 1 {
return Err(LemmyError::from_message(
"email.smtp_server needs a port, IE smtp.xxx.com:465",
@ -62,10 +49,8 @@ pub fn send_email(
let email = Message::builder()
.from(
config
email_config
.smtp_from_address
.as_ref()
.expect("email from address isn't valid")
.parse()
.expect("email from address isn't valid"),
)
@ -73,7 +58,7 @@ pub fn send_email(
Some(to_username.to_string()),
Address::from_str(to_email).expect("email to address isn't valid"),
))
.message_id(Some(format!("{}@{}", Uuid::new_v4(), hostname)))
.message_id(Some(format!("{}@{}", Uuid::new_v4(), settings.hostname)))
.subject(subject)
.multipart(MultiPart::alternative_plain_html(
plain_text,
@ -87,23 +72,18 @@ pub fn send_email(
// Set the TLS
let builder_dangerous = SmtpTransport::builder_dangerous(smtp_server).port(smtp_port);
let mut builder = match config.tls_type.as_str() {
let mut builder = match email_config.tls_type.as_str() {
"starttls" => SmtpTransport::starttls_relay(smtp_server)?,
"tls" => SmtpTransport::relay(smtp_server)?,
_ => builder_dangerous,
};
// Set the creds if they exist
if let (Some(username), Some(password)) = (
config.smtp_login.to_owned(),
config.smtp_password.to_owned(),
) {
if let (Some(username), Some(password)) = (email_config.smtp_login, email_config.smtp_password) {
builder = builder.credentials(Credentials::new(username, password));
}
let mailer = builder
.hello_name(ClientId::Domain(hostname.to_owned()))
.build();
let mailer = builder.hello_name(ClientId::Domain(domain)).build();
let result = mailer.send(&email);

View file

@ -14,6 +14,9 @@ pub struct Settings {
#[default(Some(Default::default()))]
pub(crate) pictrs: Option<PictrsConfig>,
/// Email sending configuration. All options except login/password are mandatory
#[default(None)]
#[doku(example = "Some(Default::default())")]
pub email: Option<EmailConfig>,
/// Parameters for automatic configuration of new instance (only used at first start)
#[default(None)]
#[doku(example = "Some(Default::default())")]
@ -21,7 +24,7 @@ pub struct Settings {
/// the domain name of your instance (mandatory)
#[default("unset")]
#[doku(example = "example.com")]
pub hostname: String, // TODO this is duplicated in the instance / local_site table now tho?
pub hostname: String,
/// Address where lemmy should listen for incoming requests
#[default(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)))]
#[doku(as = "String")]
@ -74,6 +77,24 @@ pub struct DatabaseConfig {
pub pool_size: u32,
}
#[derive(Debug, Deserialize, Serialize, Clone, Document, SmartDefault)]
pub struct EmailConfig {
/// Hostname and port of the smtp server
#[doku(example = "localhost:25")]
pub smtp_server: String,
/// Login name for smtp server
pub smtp_login: Option<String>,
/// Password to login to the smtp server
pub smtp_password: Option<String>,
#[doku(example = "noreply@example.com")]
/// Address to send emails from, eg "noreply@your-instance.com"
pub smtp_from_address: String,
/// Whether or not smtp connections should use tls. Can be none, tls, or starttls
#[default("none")]
#[doku(example = "none")]
pub tls_type: String,
}
#[derive(Debug, Deserialize, Serialize, Clone, SmartDefault, Document)]
pub struct SetupConfig {
/// Username for the admin user

View file

@ -8,20 +8,13 @@ use lemmy_api_common::{
community::CommunityResponse,
post::PostResponse,
private_message::PrivateMessageResponse,
utils::{
blocking,
check_person_block,
get_interface_language,
local_site_to_email_config,
send_email_to_user,
},
utils::{blocking, check_person_block, get_interface_language, send_email_to_user},
};
use lemmy_db_schema::{
newtypes::{CommentId, CommunityId, LocalUserId, PersonId, PostId, PrivateMessageId},
source::{
comment::Comment,
comment_reply::{CommentReply, CommentReplyInsertForm},
local_site::LocalSite,
person::Person,
person_mention::{PersonMention, PersonMentionInsertForm},
post::Post,
@ -181,19 +174,16 @@ pub async fn send_local_notifs(
comment: &Comment,
person: &Person,
post: &Post,
local_site: &LocalSite,
do_send_email: bool,
context: &LemmyContext,
) -> Result<Vec<LocalUserId>, LemmyError> {
let mut recipient_ids = Vec::new();
let inbox_link = format!("{}/inbox", context.settings().get_protocol_and_hostname());
let hostname = &context.settings().hostname;
// Send the local mentions
for mention in mentions
.iter()
.filter(|m| m.is_local(hostname) && m.name.ne(&person.name))
.filter(|m| m.is_local(&context.settings().hostname) && m.name.ne(&person.name))
.collect::<Vec<&MentionData>>()
{
let mention_name = mention.name.clone();
@ -201,7 +191,6 @@ pub async fn send_local_notifs(
LocalUserView::read_from_name(conn, &mention_name)
})
.await?;
let hostname = &context.settings().hostname;
if let Ok(mention_user_view) = user_view {
// TODO
// At some point, make it so you can't tag the parent creator either
@ -229,8 +218,7 @@ pub async fn send_local_notifs(
&mention_user_view,
&lang.notification_mentioned_by_subject(&person.name),
&lang.notification_mentioned_by_body(&comment.content, &inbox_link, &person.name),
hostname,
&local_site_to_email_config(local_site)?,
context.settings(),
)
}
}
@ -280,8 +268,7 @@ pub async fn send_local_notifs(
&parent_user_view,
&lang.notification_comment_reply_subject(&person.name),
&lang.notification_comment_reply_body(&comment.content, &inbox_link, &person.name),
hostname,
&local_site_to_email_config(local_site)?,
context.settings(),
)
}
}
@ -322,8 +309,7 @@ pub async fn send_local_notifs(
&parent_user_view,
&lang.notification_post_reply_subject(&person.name),
&lang.notification_post_reply_body(&comment.content, &inbox_link, &person.name),
hostname,
&local_site_to_email_config(local_site)?,
context.settings(),
)
}
}

View file

@ -56,7 +56,8 @@ alter table site drop column instance_id;
alter table person drop column instance_id;
alter table community drop column instance_id;
drop table local_site_rate_limit;
drop table local_site;
drop table allowlist;
drop table blocklist;
drop table federation_allowlist;
drop table federation_blocklist;
drop table instance;

View file

@ -48,14 +48,14 @@ alter table person alter column instance_id set not null;
alter table community alter column instance_id set not null;
-- Create allowlist and blocklist tables
create table allowlist (
create table federation_allowlist (
id serial primary key,
instance_id int references instance on update cascade on delete cascade not null unique,
published timestamp not null default now(),
updated timestamp null
);
create table blocklist (
create table federation_blocklist (
id serial primary key,
instance_id int references instance on update cascade on delete cascade not null unique,
published timestamp not null default now(),
@ -87,29 +87,11 @@ create table local_site (
-- Fields from lemmy.hjson
slur_filter_regex text,
actor_name_max_length int default 20 not null,
rate_limit_message int default 180 not null,
rate_limit_message_per_second int default 60 not null,
rate_limit_post int default 6 not null,
rate_limit_post_per_second int default 600 not null,
rate_limit_register int default 3 not null,
rate_limit_register_per_second int default 3600 not null,
rate_limit_image int default 6 not null,
rate_limit_image_per_second int default 3600 not null,
rate_limit_comment int default 6 not null,
rate_limit_comment_per_second int default 600 not null,
rate_limit_search int default 60 not null,
rate_limit_search_per_second int default 600 not null,
federation_enabled boolean default true not null,
federation_debug boolean default false not null,
federation_strict_allowlist boolean default true not null,
federation_http_fetch_retry_limit int default 25 not null,
federation_worker_count int default 64 not null,
email_enabled boolean default false not null,
email_smtp_server varchar(255),
email_smtp_login varchar(255),
email_smtp_password varchar(255),
email_smtp_from_address varchar(255),
email_tls_type varchar(255) default 'none' not null,
captcha_enabled boolean default false not null,
captcha_difficulty varchar(255) default 'medium' not null,
@ -118,6 +100,26 @@ create table local_site (
updated timestamp without time zone
);
-- local_site_rate_limit is its own table, so as to not go over 32 columns, and force diesel to use the 64-column-tables feature
create table local_site_rate_limit (
id serial primary key,
local_site_id int references local_site on update cascade on delete cascade not null unique,
message int default 180 not null,
message_per_second int default 60 not null,
post int default 6 not null,
post_per_second int default 600 not null,
register int default 3 not null,
register_per_second int default 3600 not null,
image int default 6 not null,
image_per_second int default 3600 not null,
comment int default 6 not null,
comment_per_second int default 600 not null,
search int default 60 not null,
search_per_second int default 600 not null,
published timestamp without time zone default now() not null,
updated timestamp without time zone
);
-- Insert the data into local_site
insert into local_site (
site_id,
@ -159,6 +161,13 @@ select
from site
order by id limit 1;
-- Default here
insert into local_site_rate_limit (
local_site_id
)
select id from local_site
order by id limit 1;
-- Drop all those columns from site
alter table site
drop column enable_downvotes,

View file

@ -19,6 +19,7 @@ use lemmy_db_schema::{
community::{Community, CommunityUpdateForm},
instance::{Instance, InstanceForm},
local_site::{LocalSite, LocalSiteInsertForm},
local_site_rate_limit::{LocalSiteRateLimit, LocalSiteRateLimitInsertForm},
local_user::{LocalUser, LocalUserInsertForm},
person::{Person, PersonInsertForm, PersonUpdateForm},
post::{Post, PostUpdateForm},
@ -458,7 +459,13 @@ fn initialize_local_site_2022_10_10(
.site_id(site.id)
.site_setup(Some(settings.setup.is_some()))
.build();
LocalSite::create(conn, &local_site_form)?;
let local_site = LocalSite::create(conn, &local_site_form)?;
// Create the rate limit table
let local_site_rate_limit_form = LocalSiteRateLimitInsertForm::builder()
.local_site_id(local_site.id)
.build();
LocalSiteRateLimit::create(conn, &local_site_rate_limit_form)?;
Ok(())
}

View file

@ -12,18 +12,16 @@ use diesel_migrations::EmbeddedMigrations;
use doku::json::{AutoComments, Formatting};
use lemmy_api::match_websocket_operation;
use lemmy_api_common::{
lemmy_db_views::structs::SiteView,
request::build_user_agent,
utils::{
blocking,
check_private_instance_and_federation_enabled,
local_site_to_rate_limit_config,
local_site_rate_limit_to_rate_limit_config,
},
};
use lemmy_api_crud::match_websocket_operation_crud;
use lemmy_db_schema::{
source::{local_site::LocalSite, secret::Secret},
utils::get_database_url_from_env,
};
use lemmy_db_schema::{source::secret::Secret, utils::get_database_url_from_env};
use lemmy_routes::{feeds, images, nodeinfo, webfinger};
use lemmy_server::{
api_routes,
@ -105,7 +103,8 @@ async fn main() -> Result<(), LemmyError> {
let secret = Secret::init(conn).expect("Couldn't initialize secrets.");
// Make sure the local site is set up.
let local_site = LocalSite::read(conn).expect("local site not set up");
let site_view = SiteView::read_local(conn).expect("local site not set up");
let local_site = site_view.local_site;
let federation_enabled = local_site.federation_enabled;
if federation_enabled {
@ -115,7 +114,8 @@ async fn main() -> Result<(), LemmyError> {
check_private_instance_and_federation_enabled(&local_site)?;
// Set up the rate limiter
let rate_limit_config = local_site_to_rate_limit_config(&local_site);
let rate_limit_config =
local_site_rate_limit_to_rate_limit_config(&site_view.local_site_rate_limit);
let rate_limiter = RateLimit {
rate_limiter: Arc::new(Mutex::new(RateLimiter::default())),
rate_limit_config,
@ -179,17 +179,13 @@ async fn main() -> Result<(), LemmyError> {
.configure(|cfg| api_routes::config(cfg, &rate_limiter))
.configure(|cfg| {
if federation_enabled {
lemmy_apub::http::routes::config(cfg)
lemmy_apub::http::routes::config(cfg);
webfinger::config(cfg);
}
})
.configure(feeds::config)
.configure(|cfg| images::config(cfg, pictrs_client.clone(), &rate_limiter))
.configure(nodeinfo::config)
.configure(|cfg| {
if federation_enabled {
webfinger::config(cfg)
}
})
})
.bind((settings_bind.bind, settings_bind.port))?
.run()