Remove unused items (#5870)

* Remove some unused functions

* shear

* remove more

* full feature

* remove more

* some more

* mroe
This commit is contained in:
Nutomic 2025-07-18 14:21:43 +00:00 committed by GitHub
parent 72d254b4db
commit f65875c27a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 36 additions and 293 deletions

1
Cargo.lock generated
View file

@ -3962,7 +3962,6 @@ dependencies = [
"moka", "moka",
"pretty_assertions", "pretty_assertions",
"regex", "regex",
"reqwest-middleware",
"serde", "serde",
"serde_json", "serde_json",
"smart-default", "smart-default",

View file

@ -42,16 +42,11 @@ default = []
[workspace] [workspace]
members = [ members = [
"crates/api/api",
"crates/api/api_crud",
"crates/api/api_common",
"crates/api/api_utils",
"crates/apub",
"crates/apub_objects",
"crates/utils", "crates/utils",
"crates/db_schema", "crates/db_schema",
"crates/db_schema_file", "crates/db_schema_file",
"crates/db_schema_setup", "crates/db_schema_setup",
"crates/email",
"crates/db_views/private_message", "crates/db_views/private_message",
"crates/db_views/local_user", "crates/db_views/local_user",
"crates/db_views/local_image", "crates/db_views/local_image",
@ -73,9 +68,14 @@ members = [
"crates/db_views/report_combined", "crates/db_views/report_combined",
"crates/db_views/search_combined", "crates/db_views/search_combined",
"crates/db_views/site", "crates/db_views/site",
"crates/routes", "crates/api/api",
"crates/api/api_crud",
"crates/api/api_common",
"crates/api/api_utils",
"crates/apub",
"crates/apub_objects",
"crates/federate", "crates/federate",
"crates/email", "crates/routes",
] ]
[workspace.lints.clippy] [workspace.lints.clippy]

View file

@ -18,6 +18,9 @@ doctest = false
[lints] [lints]
workspace = true workspace = true
[features]
full = []
[dependencies] [dependencies]
lemmy_db_views_comment = { workspace = true, features = ["full"] } lemmy_db_views_comment = { workspace = true, features = ["full"] }
lemmy_db_views_community = { workspace = true, features = ["full"] } lemmy_db_views_community = { workspace = true, features = ["full"] }

View file

@ -18,6 +18,7 @@ doctest = false
workspace = true workspace = true
[features] [features]
full = []
ts-rs = [ ts-rs = [
"lemmy_utils/ts-rs", "lemmy_utils/ts-rs",
"lemmy_db_schema/ts-rs", "lemmy_db_schema/ts-rs",

View file

@ -13,6 +13,9 @@ rust-version.workspace = true
[lints] [lints]
workspace = true workspace = true
[features]
full = []
[dependencies] [dependencies]
lemmy_db_views_comment = { workspace = true, features = ["full"] } lemmy_db_views_comment = { workspace = true, features = ["full"] }
lemmy_db_views_community = { workspace = true, features = ["full"] } lemmy_db_views_community = { workspace = true, features = ["full"] }

View file

@ -332,12 +332,6 @@ impl PictrsFile {
self.file self.file
)) ))
} }
pub fn delete_url(&self, protocol_and_hostname: &str) -> Result<Url, url::ParseError> {
Url::parse(&format!(
"{protocol_and_hostname}/api/v4/image/{}",
self.file
))
}
} }
/// Stores extra details about a Pictrs image. /// Stores extra details about a Pictrs image.

View file

@ -206,19 +206,6 @@ pub fn check_local_user_deleted(local_user_view: &LocalUserView) -> LemmyResult<
} }
} }
pub fn check_person_valid(person_view: &PersonView) -> LemmyResult<()> {
// Check for a site ban
if person_view.creator_banned {
Err(LemmyErrorType::SiteBan)?
}
// check for account deletion
else if person_view.person.deleted {
Err(LemmyErrorType::Deleted)?
} else {
Ok(())
}
}
/// Check if the user's email is verified if email verification is turned on /// Check if the user's email is verified if email verification is turned on
/// However, skip checking verification if the user is an admin /// However, skip checking verification if the user is an admin
pub fn check_email_verified( pub fn check_email_verified(

View file

@ -18,7 +18,7 @@ pub mod search;
/// ///
/// In case the requesting user is logged in and the object was not found locally, it is attempted /// In case the requesting user is logged in and the object was not found locally, it is attempted
/// to fetch via webfinger from the original instance. /// to fetch via webfinger from the original instance.
pub async fn resolve_ap_identifier<ActorType, DbActor>( pub(crate) async fn resolve_ap_identifier<ActorType, DbActor>(
identifier: &str, identifier: &str,
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
local_user_view: &Option<LocalUserView>, local_user_view: &Option<LocalUserView>,

View file

@ -45,7 +45,7 @@ pub(crate) struct CommunityPath {
} }
#[derive(Deserialize, Clone)] #[derive(Deserialize, Clone)]
pub struct CommunityIsFollowerQuery { pub(crate) struct CommunityIsFollowerQuery {
is_follower: Option<ObjectId<SiteOrMultiOrCommunityOrUser>>, is_follower: Option<ObjectId<SiteOrMultiOrCommunityOrUser>>,
} }

View file

@ -84,13 +84,13 @@ impl ReceiveActivityHook<SharedInboxActivities, UserOrCommunity, LemmyContext> f
} }
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct ActivityQuery { struct ActivityQuery {
type_: String, type_: String,
id: String, id: String,
} }
/// Return the ActivityPub json representation of a local activity over HTTP. /// Return the ActivityPub json representation of a local activity over HTTP.
pub(crate) async fn get_activity( async fn get_activity(
info: web::Path<ActivityQuery>, info: web::Path<ActivityQuery>,
context: Data<LemmyContext>, context: Data<LemmyContext>,
) -> LemmyResult<HttpResponse> { ) -> LemmyResult<HttpResponse> {

View file

@ -15,7 +15,7 @@ use lemmy_utils::{
use serde::Deserialize; use serde::Deserialize;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct PersonQuery { pub(crate) struct PersonQuery {
user_name: String, user_name: String,
} }

View file

@ -251,7 +251,7 @@ impl InCommunity for Page {
} }
/// Only allows deserialization if the field is missing or null. If it is present, throws an error. /// Only allows deserialization if the field is missing or null. If it is present, throws an error.
pub fn deserialize_not_present<'de, D>(deserializer: D) -> Result<Option<String>, D::Error> fn deserialize_not_present<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where where
D: Deserializer<'de>, D: Deserializer<'de>,
{ {

View file

@ -34,7 +34,7 @@ pub struct Mention {
pub kind: MentionType, pub kind: MentionType,
} }
pub struct MentionsAndAddresses { pub(crate) struct MentionsAndAddresses {
pub ccs: Vec<Url>, pub ccs: Vec<Url>,
pub tags: Vec<MentionOrValue>, pub tags: Vec<MentionOrValue>,
} }
@ -42,7 +42,7 @@ pub struct MentionsAndAddresses {
/// This takes a comment, and builds a list of to_addresses, inboxes, /// This takes a comment, and builds a list of to_addresses, inboxes,
/// and mention tags, so they know where to be sent to. /// and mention tags, so they know where to be sent to.
/// Addresses are the persons / addresses that go in the cc field. /// Addresses are the persons / addresses that go in the cc field.
pub async fn collect_non_local_mentions( pub(crate) async fn collect_non_local_mentions(
comment: &ApubComment, comment: &ApubComment,
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
) -> LemmyResult<MentionsAndAddresses> { ) -> LemmyResult<MentionsAndAddresses> {

View file

@ -349,38 +349,6 @@ impl CommunityActions {
.with_lemmy_type(LemmyErrorType::NotFound) .with_lemmy_type(LemmyErrorType::NotFound)
} }
/// Checks to make sure the acting moderator was added earlier than the target moderator
pub async fn is_higher_mod_check(
pool: &mut DbPool<'_>,
for_community_id: CommunityId,
mod_person_id: PersonId,
target_person_ids: Vec<PersonId>,
) -> LemmyResult<()> {
let conn = &mut get_conn(pool).await?;
// Build the list of persons
let mut persons = target_person_ids;
persons.push(mod_person_id);
persons.dedup();
let res = community_actions::table
.filter(community_actions::became_moderator_at.is_not_null())
.filter(community_actions::community_id.eq(for_community_id))
.filter(community_actions::person_id.eq_any(persons))
.order_by(community_actions::became_moderator_at)
.select(community_actions::person_id)
// This does a limit 1 select first
.first::<PersonId>(conn)
.await?;
// If the first result sorted by published is the acting mod
if res == mod_person_id {
Ok(())
} else {
Err(LemmyErrorType::NotHigherMod)?
}
}
/// Check if we should accept activity in remote community. This requires either: /// Check if we should accept activity in remote community. This requires either:
/// - Local follower of the community /// - Local follower of the community
/// - Local post or comment in the community /// - Local post or comment in the community
@ -848,16 +816,6 @@ mod tests {
let moderator_person_ids = vec![inserted_bobby.id, inserted_artemis.id]; let moderator_person_ids = vec![inserted_bobby.id, inserted_artemis.id];
// Make sure bobby is marked as a higher mod than artemis, and vice versa // Make sure bobby is marked as a higher mod than artemis, and vice versa
let bobby_higher_check = CommunityActions::is_higher_mod_check(
pool,
inserted_community.id,
inserted_bobby.id,
moderator_person_ids.clone(),
)
.await;
assert!(bobby_higher_check.is_ok());
// Also check the other is_higher_mod_or_admin function just in case
let bobby_higher_check_2 = LocalUser::is_higher_mod_or_admin_check( let bobby_higher_check_2 = LocalUser::is_higher_mod_or_admin_check(
pool, pool,
inserted_community.id, inserted_community.id,
@ -868,7 +826,7 @@ mod tests {
assert!(bobby_higher_check_2.is_ok()); assert!(bobby_higher_check_2.is_ok());
// This should throw an error, since artemis was added later // This should throw an error, since artemis was added later
let artemis_higher_check = CommunityActions::is_higher_mod_check( let artemis_higher_check = LocalUser::is_higher_mod_or_admin_check(
pool, pool,
inserted_community.id, inserted_community.id,
inserted_artemis.id, inserted_artemis.id,

View file

@ -517,25 +517,6 @@ impl PostActions {
.await .await
.with_lemmy_type(LemmyErrorType::CouldntUpdateReadComments) .with_lemmy_type(LemmyErrorType::CouldntUpdateReadComments)
} }
pub async fn remove_read_comments(
pool: &mut DbPool<'_>,
person_id: PersonId,
post_id: PostId,
) -> LemmyResult<UpleteCount> {
let conn = &mut get_conn(pool).await?;
uplete(
post_actions::table
.filter(post_actions::post_id.eq(post_id))
.filter(post_actions::person_id.eq(person_id)),
)
.set_null(post_actions::read_comments_amount)
.set_null(post_actions::read_comments_at)
.get_result(conn)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdateReadComments)
}
} }
impl PostActions { impl PostActions {

View file

@ -33,10 +33,7 @@ pub mod utils;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use strum::{Display, EnumString}; use strum::{Display, EnumString};
#[cfg(feature = "full")] #[cfg(feature = "full")]
use { use {diesel::query_source::AliasedField, lemmy_db_schema_file::schema::person};
diesel::query_source::AliasedField,
lemmy_db_schema_file::schema::{community_actions, instance_actions, person},
};
#[derive( #[derive(
EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default, Hash, EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default, Hash,
@ -236,47 +233,3 @@ pub type Person2AliasAllColumnsTuple = (
AliasedField<aliases::Person2, person::comment_count>, AliasedField<aliases::Person2, person::comment_count>,
AliasedField<aliases::Person2, person::comment_score>, AliasedField<aliases::Person2, person::comment_score>,
); );
#[cfg(feature = "full")]
/// A helper tuple for creator community actions
pub type CreatorCommunityActionsAllColumnsTuple = (
AliasedField<aliases::CreatorCommunityActions, community_actions::community_id>,
AliasedField<aliases::CreatorCommunityActions, community_actions::person_id>,
AliasedField<aliases::CreatorCommunityActions, community_actions::followed_at>,
AliasedField<aliases::CreatorCommunityActions, community_actions::follow_state>,
AliasedField<aliases::CreatorCommunityActions, community_actions::follow_approver_id>,
AliasedField<aliases::CreatorCommunityActions, community_actions::blocked_at>,
AliasedField<aliases::CreatorCommunityActions, community_actions::became_moderator_at>,
AliasedField<aliases::CreatorCommunityActions, community_actions::received_ban_at>,
AliasedField<aliases::CreatorCommunityActions, community_actions::ban_expires_at>,
);
#[cfg(feature = "full")]
/// A helper tuple for creator home instance actions.
pub type CreatorHomeInstanceActionsAllColumnsTuple = (
AliasedField<aliases::CreatorHomeInstanceActions, instance_actions::person_id>,
AliasedField<aliases::CreatorHomeInstanceActions, instance_actions::instance_id>,
AliasedField<aliases::CreatorHomeInstanceActions, instance_actions::blocked_at>,
AliasedField<aliases::CreatorHomeInstanceActions, instance_actions::received_ban_at>,
AliasedField<aliases::CreatorHomeInstanceActions, instance_actions::ban_expires_at>,
);
#[cfg(feature = "full")]
/// A helper tuple for creator local instance actions.
pub type CreatorLocalInstanceActionsAllColumnsTuple = (
AliasedField<aliases::CreatorLocalInstanceActions, instance_actions::person_id>,
AliasedField<aliases::CreatorLocalInstanceActions, instance_actions::instance_id>,
AliasedField<aliases::CreatorLocalInstanceActions, instance_actions::blocked_at>,
AliasedField<aliases::CreatorLocalInstanceActions, instance_actions::received_ban_at>,
AliasedField<aliases::CreatorLocalInstanceActions, instance_actions::ban_expires_at>,
);
#[cfg(feature = "full")]
/// A helper tuple for creator home instance actions.
pub type CreatorCommunityInstanceActionsAllColumnsTuple = (
AliasedField<aliases::CreatorCommunityInstanceActions, instance_actions::person_id>,
AliasedField<aliases::CreatorCommunityInstanceActions, instance_actions::instance_id>,
AliasedField<aliases::CreatorCommunityInstanceActions, instance_actions::blocked_at>,
AliasedField<aliases::CreatorCommunityInstanceActions, instance_actions::received_ban_at>,
AliasedField<aliases::CreatorCommunityInstanceActions, instance_actions::ban_expires_at>,
);

View file

@ -5,7 +5,6 @@ use chrono::TimeDelta;
use deadpool::Runtime; use deadpool::Runtime;
use diesel::{ use diesel::{
dsl, dsl,
expression::AsExpression,
helper_types::AsExprOf, helper_types::AsExprOf,
pg::{data_types::PgInterval, Pg}, pg::{data_types::PgInterval, Pg},
query_builder::{Query, QueryFragment}, query_builder::{Query, QueryFragment},
@ -36,7 +35,6 @@ use lemmy_utils::{
settings::{structs::Settings, SETTINGS}, settings::{structs::Settings, SETTINGS},
utils::validation::clean_url, utils::validation::clean_url,
}; };
use regex::Regex;
use rustls::{ use rustls::{
client::danger::{ client::danger::{
DangerousClientConfigBuilder, DangerousClientConfigBuilder,
@ -52,7 +50,7 @@ use rustls::{
}; };
use std::{ use std::{
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
sync::{Arc, LazyLock, OnceLock}, sync::{Arc, OnceLock},
time::Duration, time::Duration,
}; };
use tracing::error; use tracing::error;
@ -238,7 +236,7 @@ impl<T> Commented<T> {
} }
/// Adds `text` to the comment if `condition` is true /// Adds `text` to the comment if `condition` is true
pub fn text_if(mut self, text: &str, condition: bool) -> Self { fn text_if(mut self, text: &str, condition: bool) -> Self {
if condition { if condition {
if !self.comment.is_empty() { if !self.comment.is_empty() {
self.comment.push_str(", "); self.comment.push_str(", ");
@ -304,10 +302,6 @@ pub fn limit_fetch(limit: Option<i64>) -> LemmyResult<i64> {
}) })
} }
pub fn is_email_regex(test: &str) -> bool {
EMAIL_REGEX.is_match(test)
}
/// Takes an API optional text input, and converts it to an optional diesel DB update. /// Takes an API optional text input, and converts it to an optional diesel DB update.
pub fn diesel_string_update(opt: Option<&str>) -> Option<Option<String>> { pub fn diesel_string_update(opt: Option<&str>) -> Option<Option<String>> {
match opt { match opt {
@ -520,12 +514,6 @@ pub fn build_db_pool_for_tests() -> ActualDbPool {
build_db_pool().expect("db pool missing") build_db_pool().expect("db pool missing")
} }
#[allow(clippy::expect_used)]
static EMAIL_REGEX: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"^[a-zA-Z0-9.!#$%&*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$")
.expect("compile email regex")
});
pub mod functions { pub mod functions {
use diesel::sql_types::{BigInt, Text, Timestamptz}; use diesel::sql_types::{BigInt, Text, Timestamptz};
@ -539,13 +527,6 @@ pub mod functions {
fn scaled_rank(score: BigInt, time: Timestamptz, interactions_month: BigInt) -> Double; fn scaled_rank(score: BigInt, time: Timestamptz, interactions_month: BigInt) -> Double;
} }
define_sql_function! {
#[sql_name = "r.controversy_rank"]
fn controversy_rank(upvotes: BigInt, downvotes: BigInt, score: BigInt) -> Double;
}
define_sql_function!(fn reverse_timestamp_sort(time: Timestamptz) -> BigInt);
define_sql_function!(fn lower(x: Text) -> Text); define_sql_function!(fn lower(x: Text) -> Text);
define_sql_function!(fn random() -> Text); define_sql_function!(fn random() -> Text);
@ -574,31 +555,11 @@ pub fn seconds_to_pg_interval(seconds: i32) -> PgInterval {
PgInterval::from_microseconds(i64::from(seconds) * 1_000_000) PgInterval::from_microseconds(i64::from(seconds) * 1_000_000)
} }
/// Trait alias for a type that can be converted to an SQL tuple using `IntoSql::into_sql`
pub trait AsRecord: Expression + AsExpression<sql_types::Record<Self::SqlType>>
where
Self::SqlType: 'static,
{
}
impl<T: Expression + AsExpression<sql_types::Record<T::SqlType>>> AsRecord for T where
T::SqlType: 'static
{
}
/// Output of `IntoSql::into_sql` for a type that implements `AsRecord` /// Output of `IntoSql::into_sql` for a type that implements `AsRecord`
pub type AsRecordOutput<T> = dsl::AsExprOf<T, sql_types::Record<<T as Expression>::SqlType>>; pub type AsRecordOutput<T> = dsl::AsExprOf<T, sql_types::Record<<T as Expression>::SqlType>>;
pub type ResultFuture<'a, T> = BoxFuture<'a, Result<T, DieselError>>; pub type ResultFuture<'a, T> = BoxFuture<'a, Result<T, DieselError>>;
pub trait ReadFn<'a, T, Args>: Fn(DbConn<'a>, Args) -> ResultFuture<'a, T> {}
impl<'a, T, Args, F: Fn(DbConn<'a>, Args) -> ResultFuture<'a, T>> ReadFn<'a, T, Args> for F {}
pub trait ListFn<'a, T, Args>: Fn(DbConn<'a>, Args) -> ResultFuture<'a, Vec<T>> {}
impl<'a, T, Args, F: Fn(DbConn<'a>, Args) -> ResultFuture<'a, Vec<T>>> ListFn<'a, T, Args> for F {}
pub fn paginate<Q, C>( pub fn paginate<Q, C>(
query: Q, query: Q,
sort_direction: SortDirection, sort_direction: SortDirection,
@ -664,12 +625,6 @@ mod tests {
); );
} }
#[test]
fn test_email() {
assert!(is_email_regex("gush@gmail.com"));
assert!(!is_email_regex("nada_neutho"));
}
#[test] #[test]
fn test_diesel_option_overwrite() { fn test_diesel_option_overwrite() {
assert_eq!(diesel_string_update(None), None); assert_eq!(diesel_string_update(None), None);

View file

@ -63,22 +63,6 @@ impl CommunityFollowerView {
.with_lemmy_type(LemmyErrorType::NotFound) .with_lemmy_type(LemmyErrorType::NotFound)
} }
pub async fn get_community_follower_inboxes(
pool: &mut DbPool<'_>,
community_id: CommunityId,
) -> LemmyResult<Vec<DbUrl>> {
let conn = &mut get_conn(pool).await?;
let res = Self::joins()
.filter(community_actions::community_id.eq(community_id))
.filter(not(person::local))
.select(person::inbox_url)
.distinct()
.load::<DbUrl>(conn)
.await?;
Ok(res)
}
pub async fn count_community_followers( pub async fn count_community_followers(
pool: &mut DbPool<'_>, pool: &mut DbPool<'_>,
community_id: CommunityId, community_id: CommunityId,

View file

@ -18,6 +18,9 @@ doctest = false
[lints] [lints]
workspace = true workspace = true
[features]
full = []
[dependencies] [dependencies]
lemmy_utils = { workspace = true, features = ["full"] } lemmy_utils = { workspace = true, features = ["full"] }
lemmy_db_schema = { workspace = true, features = ["full"] } lemmy_db_schema = { workspace = true, features = ["full"] }

View file

@ -26,7 +26,7 @@ const CLEANUP_INTERVAL_SECS: u32 = 120;
/// Smaller than `std::time::Instant` because it uses a smaller integer for seconds and doesn't /// Smaller than `std::time::Instant` because it uses a smaller integer for seconds and doesn't
/// store nanoseconds /// store nanoseconds
#[derive(PartialEq, Debug, Clone, Copy, Hash)] #[derive(PartialEq, Debug, Clone, Copy, Hash)]
pub struct InstantSecs { struct InstantSecs {
pub secs: u32, pub secs: u32,
} }

View file

@ -26,7 +26,6 @@ workspace = true
full = [ full = [
"diesel", "diesel",
"actix-web", "actix-web",
"reqwest-middleware",
"tracing", "tracing",
"actix-web", "actix-web",
"serde_json", "serde_json",
@ -60,7 +59,6 @@ serde_json = { workspace = true, optional = true }
url = { workspace = true, optional = true } url = { workspace = true, optional = true }
actix-web = { workspace = true, optional = true } actix-web = { workspace = true, optional = true }
anyhow = { workspace = true, optional = true } anyhow = { workspace = true, optional = true }
reqwest-middleware = { workspace = true, optional = true }
strum = { workspace = true } strum = { workspace = true }
futures = { workspace = true, optional = true } futures = { workspace = true, optional = true }
diesel = { workspace = true, optional = true, features = ["chrono"] } diesel = { workspace = true, optional = true, features = ["chrono"] }

View file

@ -7,7 +7,7 @@ use actix_web::middleware::DefaultHeaders;
/// * 3 days = 60s * 60m * 24h * 3d = `259200` seconds /// * 3 days = 60s * 60m * 24h * 3d = `259200` seconds
/// ///
/// Mastodon & other activitypub server defaults to 3d /// Mastodon & other activitypub server defaults to 3d
pub fn cache_header(seconds: usize) -> DefaultHeaders { fn cache_header(seconds: usize) -> DefaultHeaders {
DefaultHeaders::new().add(("Cache-Control", format!("public, max-age={seconds}"))) DefaultHeaders::new().add(("Cache-Control", format!("public, max-age={seconds}")))
} }

View file

@ -5,7 +5,6 @@ cfg_if! {
if #[cfg(feature = "full")] { if #[cfg(feature = "full")] {
pub mod cache_header; pub mod cache_header;
pub mod rate_limit; pub mod rate_limit;
pub mod request;
pub mod response; pub mod response;
pub mod settings; pub mod settings;
pub mod utils; pub mod utils;

View file

@ -1,36 +0,0 @@
use std::future::Future;
pub async fn retry<F, Fut, T>(f: F) -> Result<T, reqwest_middleware::Error>
where
F: Fn() -> Fut,
Fut: Future<Output = Result<T, reqwest_middleware::Error>>,
{
retry_custom(|| async { Ok((f)().await) }).await
}
#[allow(clippy::expect_used)]
async fn retry_custom<F, Fut, T>(f: F) -> Result<T, reqwest_middleware::Error>
where
F: Fn() -> Fut,
Fut: Future<Output = Result<Result<T, reqwest_middleware::Error>, reqwest_middleware::Error>>,
{
let mut response: Option<Result<T, reqwest_middleware::Error>> = None;
for _ in 0u8..3 {
match (f)().await? {
Ok(t) => return Ok(t),
Err(reqwest_middleware::Error::Reqwest(e)) => {
if e.is_timeout() {
response = Some(Err(reqwest_middleware::Error::Reqwest(e)));
continue;
}
return Err(reqwest_middleware::Error::Reqwest(e));
}
Err(otherwise) => {
return Err(otherwise);
}
}
}
response.expect("retry http request")
}

View file

@ -1,7 +1,6 @@
use crate::{error::LemmyResult, location_info}; use crate::{error::LemmyResult, location_info};
use anyhow::{anyhow, Context}; use anyhow::{anyhow, Context};
use deser_hjson::from_str; use deser_hjson::from_str;
use regex::Regex;
use std::{env, fs, sync::LazyLock}; use std::{env, fs, sync::LazyLock};
use structs::{PictrsConfig, Settings}; use structs::{PictrsConfig, Settings};
use url::Url; use url::Url;
@ -23,15 +22,6 @@ pub static SETTINGS: LazyLock<Settings> = LazyLock::new(|| {
} }
}); });
#[allow(clippy::expect_used)]
static WEBFINGER_REGEX: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(&format!(
"^acct:([a-zA-Z0-9_]{{3,}})@{}$",
SETTINGS.hostname
))
.expect("compile webfinger regex")
});
impl Settings { impl Settings {
/// Reads config from configuration file. /// Reads config from configuration file.
/// ///
@ -59,7 +49,7 @@ impl Settings {
} }
/// Returns either "http" or "https", depending on tls_enabled setting /// Returns either "http" or "https", depending on tls_enabled setting
pub fn get_protocol_string(&self) -> &'static str { fn get_protocol_string(&self) -> &'static str {
if self.tls_enabled { if self.tls_enabled {
"https" "https"
} else { } else {
@ -88,10 +78,6 @@ impl Settings {
) )
} }
pub fn webfinger_regex(&self) -> Regex {
WEBFINGER_REGEX.clone()
}
pub fn pictrs(&self) -> LemmyResult<PictrsConfig> { pub fn pictrs(&self) -> LemmyResult<PictrsConfig> {
self self
.pictrs .pictrs

View file

@ -91,7 +91,7 @@ fn find_urls<T: NodeValue + UrlAndTitle>(src: &str) -> Vec<(usize, usize)> {
links_offsets links_offsets
} }
pub trait UrlAndTitle { trait UrlAndTitle {
fn url_len(&self) -> usize; fn url_len(&self) -> usize;
fn title_len(&self) -> usize; fn title_len(&self) -> usize;
} }

View file

@ -20,19 +20,6 @@ static MARKDOWN_PARSER: LazyLock<MarkdownIt> = LazyLock::new(|| {
parser parser
}); });
/// Replace special HTML characters in API parameters to prevent XSS attacks.
///
/// Taken from https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.md#output-encoding-for-html-contexts
///
/// `>` is left in place because it is interpreted as markdown quote.
pub fn sanitize_html(text: &str) -> String {
text
.replace('&', "&amp;")
.replace('<', "&lt;")
.replace('\"', "&quot;")
.replace('\'', "&#x27;")
}
pub fn markdown_to_html(text: &str) -> String { pub fn markdown_to_html(text: &str) -> String {
MARKDOWN_PARSER.parse(text).xrender() MARKDOWN_PARSER.parse(text).xrender()
} }
@ -231,16 +218,4 @@ mod tests {
Ok(()) Ok(())
} }
#[test]
fn test_sanitize_html() {
let sanitized = sanitize_html("<script>alert('xss');</script> hello &\"'");
let expected = "&lt;script>alert(&#x27;xss&#x27;);&lt;/script> hello &amp;&quot;&#x27;";
assert_eq!(expected, sanitized);
let sanitized =
sanitize_html("Polling the group: what do y'all know about the Orion browser from Kagi?");
let expected = "Polling the group: what do y&#x27;all know about the Orion browser from Kagi?";
assert_eq!(expected, sanitized);
}
} }

View file

@ -286,7 +286,7 @@ pub fn check_blocking_keywords_are_valid(blocking_keywords: &Vec<String>) -> Lem
Ok(()) Ok(())
} }
pub fn build_url_str_without_scheme(url_str: &str) -> LemmyResult<String> { fn build_url_str_without_scheme(url_str: &str) -> LemmyResult<String> {
// Parse and check for errors // Parse and check for errors
let mut url = Url::parse(url_str).or_else(|e| { let mut url = Url::parse(url_str).or_else(|e| {
if e == ParseError::RelativeUrlWithoutBase { if e == ParseError::RelativeUrlWithoutBase {
@ -317,7 +317,7 @@ pub fn build_url_str_without_scheme(url_str: &str) -> LemmyResult<String> {
// Shorten a string to n chars, being mindful of unicode grapheme // Shorten a string to n chars, being mindful of unicode grapheme
// boundaries // boundaries
pub fn truncate_for_db(text: &str, len: usize) -> String { fn truncate_for_db(text: &str, len: usize) -> String {
if text.chars().count() <= len { if text.chars().count() <= len {
text.to_string() text.to_string()
} else { } else {