Optimize stuff

This commit is contained in:
dullbananas 2023-06-11 17:42:56 +00:00
parent 29e46abf44
commit dbfbc8660d
19 changed files with 83 additions and 87 deletions

21
Cargo.lock generated
View file

@ -1662,6 +1662,26 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca" checksum = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca"
[[package]]
name = "enum-map"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "988f0d17a0fa38291e5f41f71ea8d46a5d5497b9054d5a759fae2cbb819f2356"
dependencies = [
"enum-map-derive",
]
[[package]]
name = "enum-map-derive"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a4da76b3b6116d758c7ba93f7ec6a35d2e2cf24feda76c6e38a375f4d5c59f2"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.103",
]
[[package]] [[package]]
name = "enum_delegate" name = "enum_delegate"
version = "0.2.0" version = "0.2.0"
@ -2764,6 +2784,7 @@ dependencies = [
"deser-hjson", "deser-hjson",
"diesel", "diesel",
"doku", "doku",
"enum-map",
"futures", "futures",
"html2text", "html2text",
"http", "http",

View file

@ -42,7 +42,7 @@ impl Perform for BanFromCommunity {
// Verify that only mods or admins can ban // Verify that only mods or admins can ban
is_mod_or_admin(context.pool(), local_user_view.person.id, community_id).await?; is_mod_or_admin(context.pool(), local_user_view.person.id, community_id).await?;
is_valid_body_field(&data.reason)?; is_valid_body_field(data.reason.as_deref())?;
let community_user_ban_form = CommunityPersonBanForm { let community_user_ban_form = CommunityPersonBanForm {
community_id: data.community_id, community_id: data.community_id,

View file

@ -30,7 +30,7 @@ impl Perform for BanPerson {
// Make sure user is an admin // Make sure user is an admin
is_admin(&local_user_view)?; is_admin(&local_user_view)?;
is_valid_body_field(&data.reason)?; is_valid_body_field(data.reason.as_deref())?;
let ban = data.ban; let ban = data.ban;
let banned_person_id = data.person_id; let banned_person_id = data.person_id;

View file

@ -45,11 +45,10 @@ impl PerformCrud for CreateComment {
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let local_site = LocalSite::read(context.pool()).await?; let local_site = LocalSite::read(context.pool()).await?;
let content_slurs_removed = remove_slurs( let content_slurs_removed = remove_slurs(&data.content, &local_site_to_slur_regex(&local_site));
&data.content.clone(), is_valid_body_field(Some(&content_slurs_removed))?;
&local_site_to_slur_regex(&local_site),
); let mentions = scrape_text_for_mentions(&content_slurs_removed);
is_valid_body_field(&Some(content_slurs_removed.clone()))?;
// Check for a community ban // Check for a community ban
let post_id = data.post_id; let post_id = data.post_id;
@ -96,7 +95,7 @@ impl PerformCrud for CreateComment {
.await?; .await?;
let comment_form = CommentInsertForm::builder() let comment_form = CommentInsertForm::builder()
.content(content_slurs_removed.clone()) .content(content_slurs_removed.into_owned())
.post_id(data.post_id) .post_id(data.post_id)
.creator_id(local_user_view.person.id) .creator_id(local_user_view.person.id)
.language_id(Some(language_id)) .language_id(Some(language_id))
@ -126,7 +125,6 @@ impl PerformCrud for CreateComment {
.map_err(|e| LemmyError::from_error_message(e, "couldnt_create_comment"))?; .map_err(|e| LemmyError::from_error_message(e, "couldnt_create_comment"))?;
// Scan the comment for user mentions, add those rows // Scan the comment for user mentions, add those rows
let mentions = scrape_text_for_mentions(&content_slurs_removed);
let recipient_ids = send_local_notifs( let recipient_ids = send_local_notifs(
mentions, mentions,
&updated_comment, &updated_comment,

View file

@ -64,11 +64,11 @@ impl PerformCrud for EditComment {
.as_ref() .as_ref()
.map(|c| remove_slurs(c, &local_site_to_slur_regex(&local_site))); .map(|c| remove_slurs(c, &local_site_to_slur_regex(&local_site)));
is_valid_body_field(&content_slurs_removed)?; is_valid_body_field(content_slurs_removed.as_deref())?;
let comment_id = data.comment_id; let comment_id = data.comment_id;
let form = CommentUpdateForm::builder() let form = CommentUpdateForm::builder()
.content(content_slurs_removed) .content(content_slurs_removed.map(|s| s.into_owned()))
.language_id(data.language_id) .language_id(data.language_id)
.updated(Some(Some(naive_now()))) .updated(Some(Some(naive_now())))
.build(); .build();

View file

@ -67,7 +67,7 @@ impl PerformCrud for CreateCommunity {
check_slurs_opt(&data.description, &slur_regex)?; check_slurs_opt(&data.description, &slur_regex)?;
is_valid_actor_name(&data.name, local_site.actor_name_max_length as usize)?; is_valid_actor_name(&data.name, local_site.actor_name_max_length as usize)?;
is_valid_body_field(&data.description)?; is_valid_body_field(data.description.as_deref())?;
// Double check for duplicate community actor_ids // Double check for duplicate community actor_ids
let community_actor_id = generate_local_apub_endpoint( let community_actor_id = generate_local_apub_endpoint(

View file

@ -38,7 +38,7 @@ impl PerformCrud for EditCommunity {
let slur_regex = local_site_to_slur_regex(&local_site); let slur_regex = local_site_to_slur_regex(&local_site);
check_slurs_opt(&data.title, &slur_regex)?; check_slurs_opt(&data.title, &slur_regex)?;
check_slurs_opt(&data.description, &slur_regex)?; check_slurs_opt(&data.description, &slur_regex)?;
is_valid_body_field(&data.description)?; is_valid_body_field(data.description.as_deref())?;
// Verify its a mod (only mods can edit it) // Verify its a mod (only mods can edit it)
let community_id = data.community_id; let community_id = data.community_id;

View file

@ -57,7 +57,7 @@ impl PerformCrud for CreatePost {
let url = data_url.map(clean_url_params).map(Into::into); // TODO no good way to handle a "clear" let url = data_url.map(clean_url_params).map(Into::into); // TODO no good way to handle a "clear"
is_valid_post_title(&data.name)?; is_valid_post_title(&data.name)?;
is_valid_body_field(&data.body)?; is_valid_body_field(data.body.as_deref())?;
check_community_ban(local_user_view.person.id, data.community_id, context.pool()).await?; check_community_ban(local_user_view.person.id, data.community_id, context.pool()).await?;
check_community_deleted_or_removed(data.community_id, context.pool()).await?; check_community_deleted_or_removed(data.community_id, context.pool()).await?;

View file

@ -49,7 +49,7 @@ impl PerformCrud for EditPost {
is_valid_post_title(name)?; is_valid_post_title(name)?;
} }
is_valid_body_field(&data.body)?; is_valid_body_field(data.body.as_deref())?;
let post_id = data.post_id; let post_id = data.post_id;
let orig_post = Post::read(context.pool(), post_id).await?; let orig_post = Post::read(context.pool(), post_id).await?;

View file

@ -39,16 +39,13 @@ impl PerformCrud for CreatePrivateMessage {
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let local_site = LocalSite::read(context.pool()).await?; let local_site = LocalSite::read(context.pool()).await?;
let content_slurs_removed = remove_slurs( let content_slurs_removed = remove_slurs(&data.content, &local_site_to_slur_regex(&local_site));
&data.content.clone(), is_valid_body_field(Some(&content_slurs_removed))?;
&local_site_to_slur_regex(&local_site),
);
is_valid_body_field(&Some(content_slurs_removed.clone()))?;
check_person_block(local_user_view.person.id, data.recipient_id, context.pool()).await?; check_person_block(local_user_view.person.id, data.recipient_id, context.pool()).await?;
let private_message_form = PrivateMessageInsertForm::builder() let private_message_form = PrivateMessageInsertForm::builder()
.content(content_slurs_removed.clone()) .content(content_slurs_removed.clone().into_owned())
.creator_id(local_user_view.person.id) .creator_id(local_user_view.person.id)
.recipient_id(data.recipient_id) .recipient_id(data.recipient_id)
.build(); .build();

View file

@ -41,14 +41,14 @@ impl PerformCrud for EditPrivateMessage {
// Doing the update // Doing the update
let content_slurs_removed = remove_slurs(&data.content, &local_site_to_slur_regex(&local_site)); let content_slurs_removed = remove_slurs(&data.content, &local_site_to_slur_regex(&local_site));
is_valid_body_field(&Some(content_slurs_removed.clone()))?; is_valid_body_field(Some(&content_slurs_removed))?;
let private_message_id = data.private_message_id; let private_message_id = data.private_message_id;
PrivateMessage::update( PrivateMessage::update(
context.pool(), context.pool(),
private_message_id, private_message_id,
&PrivateMessageUpdateForm::builder() &PrivateMessageUpdateForm::builder()
.content(Some(content_slurs_removed)) .content(Some(content_slurs_removed.into_owned()))
.updated(Some(Some(naive_now()))) .updated(Some(Some(naive_now())))
.build(), .build(),
) )

View file

@ -66,7 +66,7 @@ impl PerformCrud for CreateSite {
site_description_length_check(desc)?; site_description_length_check(desc)?;
} }
is_valid_body_field(&data.sidebar)?; is_valid_body_field(data.sidebar.as_deref())?;
let application_question = diesel_option_overwrite(&data.application_question); let application_question = diesel_option_overwrite(&data.application_question);
check_application_question( check_application_question(

View file

@ -57,7 +57,7 @@ impl PerformCrud for EditSite {
site_description_length_check(desc)?; site_description_length_check(desc)?;
} }
is_valid_body_field(&data.sidebar)?; is_valid_body_field(data.sidebar.as_deref())?;
let application_question = diesel_option_overwrite(&data.application_question); let application_question = diesel_option_overwrite(&data.application_question);
check_application_question( check_application_question(

View file

@ -167,7 +167,7 @@ impl Object for ApubComment {
let form = CommentInsertForm { let form = CommentInsertForm {
creator_id: creator.id, creator_id: creator.id,
post_id: post.id, post_id: post.id,
content: content_slurs_removed, content: content_slurs_removed.into_owned(),
removed: None, removed: None,
published: note.published.map(|u| u.naive_local()), published: note.published.map(|u| u.naive_local()),
updated: note.updated.map(|u| u.naive_local()), updated: note.updated.map(|u| u.naive_local()),

View file

@ -211,15 +211,14 @@ impl Object for ApubPost {
let local_site = LocalSite::read(context.pool()).await.ok(); let local_site = LocalSite::read(context.pool()).await.ok();
let slur_regex = &local_site_opt_to_slur_regex(&local_site); let slur_regex = &local_site_opt_to_slur_regex(&local_site);
let body_slurs_removed = let body = read_from_string_or_source_opt(&page.content, &page.media_type, &page.source);
read_from_string_or_source_opt(&page.content, &page.media_type, &page.source) let body_slurs_removed = body.as_deref().map(|s| remove_slurs(s, slur_regex));
.map(|s| remove_slurs(&s, slur_regex));
let language_id = LanguageTag::to_language_id_single(page.language, context.pool()).await?; let language_id = LanguageTag::to_language_id_single(page.language, context.pool()).await?;
PostInsertForm { PostInsertForm {
name, name,
url: url.map(Into::into), url: url.map(Into::into),
body: body_slurs_removed, body: body_slurs_removed.map(|s| s.into_owned()),
creator_id: creator.id, creator_id: creator.id,
community_id: community.id, community_id: community.id,
removed: None, removed: None,

View file

@ -45,6 +45,7 @@ jsonwebtoken = "8.1.1"
lettre = "0.10.1" lettre = "0.10.1"
comrak = { version = "0.14.0", default-features = false } comrak = { version = "0.14.0", default-features = false }
totp-rs = { version = "4.2.0", features = ["gen_secret", "otpauth"] } totp-rs = { version = "4.2.0", features = ["gen_secret", "otpauth"] }
enum-map = "2.5"
[dev-dependencies] [dev-dependencies]
reqwest = { workspace = true } reqwest = { workspace = true }

View file

@ -1,6 +1,6 @@
use crate::IpAddr; use crate::IpAddr;
use enum_map::{enum_map, EnumMap};
use std::{collections::HashMap, time::Instant}; use std::{collections::HashMap, time::Instant};
use strum::IntoEnumIterator;
use tracing::debug; use tracing::debug;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -9,7 +9,7 @@ struct RateLimitBucket {
allowance: f64, allowance: f64,
} }
#[derive(Eq, PartialEq, Hash, Debug, EnumIter, Copy, Clone, AsRefStr)] #[derive(Eq, PartialEq, Hash, Debug, enum_map::Enum, Copy, Clone, AsRefStr)]
pub(crate) enum RateLimitType { pub(crate) enum RateLimitType {
Message, Message,
Register, Register,
@ -22,30 +22,10 @@ pub(crate) enum RateLimitType {
/// Rate limiting based on rate type and IP addr /// Rate limiting based on rate type and IP addr
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct RateLimitStorage { pub struct RateLimitStorage {
buckets: HashMap<RateLimitType, HashMap<IpAddr, RateLimitBucket>>, buckets: HashMap<IpAddr, EnumMap<RateLimitType, RateLimitBucket>>,
} }
impl RateLimitStorage { impl RateLimitStorage {
fn insert_ip(&mut self, ip: &IpAddr) {
for rate_limit_type in RateLimitType::iter() {
if self.buckets.get(&rate_limit_type).is_none() {
self.buckets.insert(rate_limit_type, HashMap::new());
}
if let Some(bucket) = self.buckets.get_mut(&rate_limit_type) {
if bucket.get(ip).is_none() {
bucket.insert(
ip.clone(),
RateLimitBucket {
last_checked: Instant::now(),
allowance: -2f64,
},
);
}
}
}
}
/// Rate limiting Algorithm described here: https://stackoverflow.com/a/668327/1655478 /// Rate limiting Algorithm described here: https://stackoverflow.com/a/668327/1655478
/// ///
/// Returns true if the request passed the rate limit, false if it failed and should be rejected. /// Returns true if the request passed the rate limit, false if it failed and should be rejected.
@ -57,10 +37,15 @@ impl RateLimitStorage {
rate: i32, rate: i32,
per: i32, per: i32,
) -> bool { ) -> bool {
self.insert_ip(ip);
if let Some(bucket) = self.buckets.get_mut(&type_) {
if let Some(rate_limit) = bucket.get_mut(ip) {
let current = Instant::now(); let current = Instant::now();
let ip_buckets = self.buckets.entry(ip.clone()).or_insert(enum_map! {
_ => RateLimitBucket {
last_checked: current,
allowance: -2f64,
},
});
#[allow(clippy::indexing_slicing)] // `EnumMap` has no `get` funciton
let rate_limit = &mut ip_buckets[type_];
let time_passed = current.duration_since(rate_limit.last_checked).as_secs() as f64; let time_passed = current.duration_since(rate_limit.last_checked).as_secs() as f64;
// The initial value // The initial value
@ -87,11 +72,5 @@ impl RateLimitStorage {
rate_limit.allowance -= 1.0; rate_limit.allowance -= 1.0;
true true
} }
} else {
true
}
} else {
true
}
} }
} }

View file

@ -1,11 +1,12 @@
use crate::error::LemmyError; use crate::error::LemmyError;
use regex::{Regex, RegexBuilder}; use regex::{Regex, RegexBuilder};
use std::borrow::Cow;
pub fn remove_slurs(test: &str, slur_regex: &Option<Regex>) -> String { pub fn remove_slurs<'a>(test: &'a str, slur_regex: &Option<Regex>) -> Cow<'a, str> {
if let Some(slur_regex) = slur_regex { if let Some(slur_regex) = slur_regex {
slur_regex.replace_all(test, "*removed*").to_string() slur_regex.replace_all(test, "*removed*")
} else { } else {
test.to_string() Cow::Borrowed(test)
} }
} }

View file

@ -68,7 +68,7 @@ pub fn is_valid_post_title(title: &str) -> LemmyResult<()> {
} }
/// This could be post bodies, comments, or any description field /// This could be post bodies, comments, or any description field
pub fn is_valid_body_field(body: &Option<String>) -> LemmyResult<()> { pub fn is_valid_body_field(body: Option<&str>) -> LemmyResult<()> {
if let Some(body) = body { if let Some(body) = body {
let check = body.chars().count() <= BODY_MAX_LENGTH; let check = body.chars().count() <= BODY_MAX_LENGTH;
if !check { if !check {