Removing a few expects from production and test code. (#5193)
* Removing a few expects from production and test code. - Fixes #5192 * Using if let filter for admin emails. * Fixing unused error. * Adding expect_used = deny to clippy lints. * Update src/lib.rs Co-authored-by: Nutomic <me@nutomic.com> * Update crates/utils/src/settings/structs.rs Co-authored-by: Nutomic <me@nutomic.com> * Update crates/utils/src/settings/mod.rs Co-authored-by: Nutomic <me@nutomic.com> * Some more cleanup. * Fix clippy --------- Co-authored-by: Nutomic <me@nutomic.com>
This commit is contained in:
parent
231cce9350
commit
fa4825b524
42 changed files with 252 additions and 173 deletions
|
@ -79,6 +79,7 @@ unused_self = "deny"
|
|||
unwrap_used = "deny"
|
||||
unimplemented = "deny"
|
||||
unused_async = "deny"
|
||||
expect_used = "deny"
|
||||
|
||||
[workspace.dependencies]
|
||||
lemmy_api = { version = "=0.19.6-beta.7", path = "./crates/api" }
|
||||
|
|
|
@ -10,7 +10,7 @@ use lemmy_db_schema::source::{
|
|||
login_token::LoginToken,
|
||||
password_reset_request::PasswordResetRequest,
|
||||
};
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn change_password_after_reset(
|
||||
|
@ -32,9 +32,7 @@ pub async fn change_password_after_reset(
|
|||
|
||||
// Update the user with the new password
|
||||
let password = data.password.clone();
|
||||
LocalUser::update_password(&mut context.pool(), local_user_id, &password)
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CouldntUpdateUser)?;
|
||||
LocalUser::update_password(&mut context.pool(), local_user_id, &password).await?;
|
||||
|
||||
LoginToken::invalidate_all(&mut context.pool(), local_user_id).await?;
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ use captcha::{gen, Difficulty};
|
|||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
person::{CaptchaResponse, GetCaptchaResponse},
|
||||
LemmyErrorType,
|
||||
};
|
||||
use lemmy_db_schema::source::{
|
||||
captcha_answer::{CaptchaAnswer, CaptchaAnswerForm},
|
||||
|
@ -37,7 +38,9 @@ pub async fn get_captcha(context: Data<LemmyContext>) -> LemmyResult<HttpRespons
|
|||
|
||||
let answer = captcha.chars_as_string();
|
||||
|
||||
let png = captcha.as_base64().expect("failed to generate captcha");
|
||||
let png = captcha
|
||||
.as_base64()
|
||||
.ok_or(LemmyErrorType::CouldntCreateImageCaptcha)?;
|
||||
|
||||
let wav = captcha_as_wav_base64(&captcha)?;
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ impl LemmyContext {
|
|||
/// Initialize a context for use in tests which blocks federation network calls.
|
||||
///
|
||||
/// Do not use this in production code.
|
||||
#[allow(clippy::expect_used)]
|
||||
pub async fn init_test_federation_config() -> FederationConfig<LemmyContext> {
|
||||
// call this to run migrations
|
||||
let pool = build_db_pool_for_tests();
|
||||
|
|
|
@ -521,7 +521,7 @@ mod tests {
|
|||
|
||||
// root relative url
|
||||
let html_bytes = b"<!DOCTYPE html><html><head><meta property='og:image' content='/image.jpg'></head><body></body></html>";
|
||||
let metadata = extract_opengraph_data(html_bytes, &url).expect("Unable to parse metadata");
|
||||
let metadata = extract_opengraph_data(html_bytes, &url)?;
|
||||
assert_eq!(
|
||||
metadata.image,
|
||||
Some(Url::parse("https://example.com/image.jpg")?.into())
|
||||
|
@ -529,7 +529,7 @@ mod tests {
|
|||
|
||||
// base relative url
|
||||
let html_bytes = b"<!DOCTYPE html><html><head><meta property='og:image' content='image.jpg'></head><body></body></html>";
|
||||
let metadata = extract_opengraph_data(html_bytes, &url).expect("Unable to parse metadata");
|
||||
let metadata = extract_opengraph_data(html_bytes, &url)?;
|
||||
assert_eq!(
|
||||
metadata.image,
|
||||
Some(Url::parse("https://example.com/one/image.jpg")?.into())
|
||||
|
@ -537,7 +537,7 @@ mod tests {
|
|||
|
||||
// absolute url
|
||||
let html_bytes = b"<!DOCTYPE html><html><head><meta property='og:image' content='https://cdn.host.com/image.jpg'></head><body></body></html>";
|
||||
let metadata = extract_opengraph_data(html_bytes, &url).expect("Unable to parse metadata");
|
||||
let metadata = extract_opengraph_data(html_bytes, &url)?;
|
||||
assert_eq!(
|
||||
metadata.image,
|
||||
Some(Url::parse("https://cdn.host.com/image.jpg")?.into())
|
||||
|
@ -545,7 +545,7 @@ mod tests {
|
|||
|
||||
// protocol relative url
|
||||
let html_bytes = b"<!DOCTYPE html><html><head><meta property='og:image' content='//example.com/image.jpg'></head><body></body></html>";
|
||||
let metadata = extract_opengraph_data(html_bytes, &url).expect("Unable to parse metadata");
|
||||
let metadata = extract_opengraph_data(html_bytes, &url)?;
|
||||
assert_eq!(
|
||||
metadata.image,
|
||||
Some(Url::parse("https://example.com/image.jpg")?.into())
|
||||
|
|
|
@ -514,6 +514,7 @@ pub struct ReadableFederationState {
|
|||
next_retry: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
impl From<FederationQueueState> for ReadableFederationState {
|
||||
fn from(internal_state: FederationQueueState) -> Self {
|
||||
ReadableFederationState {
|
||||
|
|
|
@ -442,7 +442,11 @@ pub async fn send_password_reset_email(
|
|||
// Generate a random token
|
||||
let token = uuid::Uuid::new_v4().to_string();
|
||||
|
||||
let email = &user.local_user.email.clone().expect("email");
|
||||
let email = &user
|
||||
.local_user
|
||||
.email
|
||||
.clone()
|
||||
.ok_or(LemmyErrorType::EmailRequired)?;
|
||||
let lang = get_interface_language(user);
|
||||
let subject = &lang.password_reset_subject(&user.person.name);
|
||||
let protocol_and_hostname = settings.get_protocol_and_hostname();
|
||||
|
@ -492,6 +496,7 @@ pub fn get_interface_language_from_settings(user: &LocalUserView) -> Lang {
|
|||
lang_str_to_lang(&user.local_user.interface_language)
|
||||
}
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
fn lang_str_to_lang(lang: &str) -> Lang {
|
||||
let lang_id = LanguageId::new(lang);
|
||||
Lang::from_language_id(&lang_id).unwrap_or_else(|| {
|
||||
|
@ -518,11 +523,11 @@ pub fn local_site_rate_limit_to_rate_limit_config(
|
|||
})
|
||||
}
|
||||
|
||||
pub fn local_site_to_slur_regex(local_site: &LocalSite) -> Option<Regex> {
|
||||
pub fn local_site_to_slur_regex(local_site: &LocalSite) -> Option<LemmyResult<Regex>> {
|
||||
build_slur_regex(local_site.slur_filter_regex.as_deref())
|
||||
}
|
||||
|
||||
pub fn local_site_opt_to_slur_regex(local_site: &Option<LocalSite>) -> Option<Regex> {
|
||||
pub fn local_site_opt_to_slur_regex(local_site: &Option<LocalSite>) -> Option<LemmyResult<Regex>> {
|
||||
local_site
|
||||
.as_ref()
|
||||
.map(local_site_to_slur_regex)
|
||||
|
@ -557,7 +562,11 @@ pub async fn send_application_approved_email(
|
|||
user: &LocalUserView,
|
||||
settings: &Settings,
|
||||
) -> LemmyResult<()> {
|
||||
let email = &user.local_user.email.clone().expect("email");
|
||||
let email = &user
|
||||
.local_user
|
||||
.email
|
||||
.clone()
|
||||
.ok_or(LemmyErrorType::EmailRequired)?;
|
||||
let lang = get_interface_language(user);
|
||||
let subject = lang.registration_approved_subject(&user.person.actor_id);
|
||||
let body = lang.registration_approved_body(&settings.hostname);
|
||||
|
@ -579,7 +588,11 @@ pub async fn send_new_applicant_email_to_admins(
|
|||
);
|
||||
|
||||
for admin in &admins {
|
||||
let email = &admin.local_user.email.clone().expect("email");
|
||||
let email = &admin
|
||||
.local_user
|
||||
.email
|
||||
.clone()
|
||||
.ok_or(LemmyErrorType::EmailRequired)?;
|
||||
let lang = get_interface_language_from_settings(admin);
|
||||
let subject = lang.new_application_subject(&settings.hostname, applicant_username);
|
||||
let body = lang.new_application_body(applications_link);
|
||||
|
@ -601,11 +614,13 @@ pub async fn send_new_report_email_to_admins(
|
|||
let reports_link = &format!("{}/reports", settings.get_protocol_and_hostname(),);
|
||||
|
||||
for admin in &admins {
|
||||
let email = &admin.local_user.email.clone().expect("email");
|
||||
let lang = get_interface_language_from_settings(admin);
|
||||
let subject = lang.new_report_subject(&settings.hostname, reported_username, reporter_username);
|
||||
let body = lang.new_report_body(reports_link);
|
||||
send_email(&subject, email, &admin.person.name, &body, settings).await?;
|
||||
if let Some(email) = &admin.local_user.email {
|
||||
let lang = get_interface_language_from_settings(admin);
|
||||
let subject =
|
||||
lang.new_report_subject(&settings.hostname, reported_username, reporter_username);
|
||||
let body = lang.new_report_body(reports_link);
|
||||
send_email(&subject, email, &admin.person.name, &body, settings).await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1030,7 +1045,7 @@ pub fn check_conflicting_like_filters(
|
|||
|
||||
pub async fn process_markdown(
|
||||
text: &str,
|
||||
slur_regex: &Option<Regex>,
|
||||
slur_regex: &Option<LemmyResult<Regex>>,
|
||||
url_blocklist: &RegexSet,
|
||||
context: &LemmyContext,
|
||||
) -> LemmyResult<String> {
|
||||
|
@ -1062,7 +1077,7 @@ pub async fn process_markdown(
|
|||
|
||||
pub async fn process_markdown_opt(
|
||||
text: &Option<String>,
|
||||
slur_regex: &Option<Regex>,
|
||||
slur_regex: &Option<LemmyResult<Regex>>,
|
||||
url_blocklist: &RegexSet,
|
||||
context: &LemmyContext,
|
||||
) -> LemmyResult<Option<String>> {
|
||||
|
|
|
@ -162,7 +162,7 @@ fn validate_create_payload(local_site: &LocalSite, create_site: &CreateSite) ->
|
|||
.slur_filter_regex
|
||||
.as_deref()
|
||||
.or(local_site.slur_filter_regex.as_deref()),
|
||||
)?;
|
||||
);
|
||||
|
||||
site_name_length_check(&create_site.name)?;
|
||||
check_slurs(&create_site.name, &slur_regex)?;
|
||||
|
|
|
@ -211,7 +211,7 @@ fn validate_update_payload(local_site: &LocalSite, edit_site: &EditSite) -> Lemm
|
|||
.slur_filter_regex
|
||||
.as_deref()
|
||||
.or(local_site.slur_filter_regex.as_deref()),
|
||||
)?;
|
||||
);
|
||||
|
||||
if let Some(name) = &edit_site.name {
|
||||
// The name doesn't need to be updated, but if provided it cannot be blanked out...
|
||||
|
|
|
@ -148,14 +148,15 @@ pub async fn register(
|
|||
let inserted_local_user = create_local_user(&context, language_tags, &local_user_form).await?;
|
||||
|
||||
if local_site.site_setup && require_registration_application {
|
||||
// Create the registration application
|
||||
let form = RegistrationApplicationInsertForm {
|
||||
local_user_id: inserted_local_user.id,
|
||||
// We already made sure answer was not null above
|
||||
answer: data.answer.clone().expect("must have an answer"),
|
||||
};
|
||||
if let Some(answer) = data.answer.clone() {
|
||||
// Create the registration application
|
||||
let form = RegistrationApplicationInsertForm {
|
||||
local_user_id: inserted_local_user.id,
|
||||
answer,
|
||||
};
|
||||
|
||||
RegistrationApplication::create(&mut context.pool(), &form).await?;
|
||||
RegistrationApplication::create(&mut context.pool(), &form).await?;
|
||||
}
|
||||
}
|
||||
|
||||
// Email the admins, only if email verification is not required
|
||||
|
@ -373,17 +374,19 @@ pub async fn authenticate_with_oauth(
|
|||
&& !local_user.accepted_application
|
||||
&& !local_user.admin
|
||||
{
|
||||
// Create the registration application
|
||||
RegistrationApplication::create(
|
||||
&mut context.pool(),
|
||||
&RegistrationApplicationInsertForm {
|
||||
local_user_id: local_user.id,
|
||||
answer: data.answer.clone().expect("must have an answer"),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
if let Some(answer) = data.answer.clone() {
|
||||
// Create the registration application
|
||||
RegistrationApplication::create(
|
||||
&mut context.pool(),
|
||||
&RegistrationApplicationInsertForm {
|
||||
local_user_id: local_user.id,
|
||||
answer,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
login_response.registration_created = true;
|
||||
login_response.registration_created = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check email is verified when required
|
||||
|
@ -483,7 +486,7 @@ async fn send_verification_email_if_required(
|
|||
&local_user
|
||||
.email
|
||||
.clone()
|
||||
.expect("invalid verification email"),
|
||||
.ok_or(LemmyErrorType::EmailRequired)?,
|
||||
&mut context.pool(),
|
||||
context.settings(),
|
||||
)
|
||||
|
|
|
@ -130,7 +130,7 @@ impl AnnounceActivity {
|
|||
actor: c.actor.clone().into_inner(),
|
||||
other: serde_json::to_value(c.object)?
|
||||
.as_object()
|
||||
.expect("is object")
|
||||
.ok_or(FederationError::Unreachable)?
|
||||
.clone(),
|
||||
};
|
||||
let announce_compat = AnnounceActivity::new(announcable_page, community, context)?;
|
||||
|
|
|
@ -5,7 +5,7 @@ use activitypub_federation::{
|
|||
};
|
||||
use diesel::NotFound;
|
||||
use itertools::Itertools;
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_api_common::{context::LemmyContext, LemmyErrorType};
|
||||
use lemmy_db_schema::traits::ApubActor;
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::error::{LemmyError, LemmyResult};
|
||||
|
@ -42,7 +42,7 @@ where
|
|||
let (name, domain) = identifier
|
||||
.splitn(2, '@')
|
||||
.collect_tuple()
|
||||
.expect("invalid query");
|
||||
.ok_or(LemmyErrorType::InvalidUrl)?;
|
||||
let actor = DbActor::read_from_name_and_domain(&mut context.pool(), name, domain)
|
||||
.await
|
||||
.ok()
|
||||
|
|
|
@ -50,7 +50,8 @@ impl UrlVerifier for VerifyUrlData {
|
|||
async fn verify(&self, url: &Url) -> Result<(), ActivityPubError> {
|
||||
let local_site_data = local_site_data_cached(&mut (&self.0).into())
|
||||
.await
|
||||
.expect("read local site data");
|
||||
.map_err(|e| ActivityPubError::Other(format!("Cant read local site data: {e}")))?;
|
||||
|
||||
use FederationError::*;
|
||||
check_apub_id_valid(url, &local_site_data).map_err(|err| match err {
|
||||
LemmyError {
|
||||
|
@ -176,10 +177,7 @@ pub(crate) async fn check_apub_id_valid_with_strictness(
|
|||
.domain()
|
||||
.ok_or(FederationError::UrlWithoutDomain)?
|
||||
.to_string();
|
||||
let local_instance = context
|
||||
.settings()
|
||||
.get_hostname_without_port()
|
||||
.expect("local hostname is valid");
|
||||
let local_instance = context.settings().get_hostname_without_port()?;
|
||||
if domain == local_instance {
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -196,10 +194,7 @@ pub(crate) async fn check_apub_id_valid_with_strictness(
|
|||
.iter()
|
||||
.map(|i| i.domain.clone())
|
||||
.collect::<Vec<String>>();
|
||||
let local_instance = context
|
||||
.settings()
|
||||
.get_hostname_without_port()
|
||||
.expect("local hostname is valid");
|
||||
let local_instance = context.settings().get_hostname_without_port()?;
|
||||
allowed_and_local.push(local_instance);
|
||||
|
||||
let domain = apub_id
|
||||
|
|
|
@ -372,6 +372,7 @@ async fn convert_update_languages(
|
|||
}
|
||||
|
||||
/// If all languages are returned, return empty vec instead
|
||||
#[allow(clippy::expect_used)]
|
||||
async fn convert_read_languages(
|
||||
conn: &mut AsyncPgConnection,
|
||||
language_ids: Vec<LanguageId>,
|
||||
|
@ -510,7 +511,7 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_user_languages() -> Result<(), Error> {
|
||||
async fn test_user_languages() -> LemmyResult<()> {
|
||||
let pool = &build_db_pool_for_tests();
|
||||
let pool = &mut pool.into();
|
||||
|
||||
|
@ -543,7 +544,7 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_community_languages() -> Result<(), Error> {
|
||||
async fn test_community_languages() -> LemmyResult<()> {
|
||||
let pool = &build_db_pool_for_tests();
|
||||
let pool = &mut pool.into();
|
||||
let (site, instance) = create_test_site(pool).await?;
|
||||
|
|
|
@ -57,11 +57,12 @@ mod tests {
|
|||
source::captcha_answer::{CaptchaAnswer, CaptchaAnswerForm, CheckCaptchaAnswer},
|
||||
utils::build_db_pool_for_tests,
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
use serial_test::serial;
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_captcha_happy_path() {
|
||||
async fn test_captcha_happy_path() -> LemmyResult<()> {
|
||||
let pool = &build_db_pool_for_tests();
|
||||
let pool = &mut pool.into();
|
||||
|
||||
|
@ -71,8 +72,7 @@ mod tests {
|
|||
answer: "XYZ".to_string(),
|
||||
},
|
||||
)
|
||||
.await
|
||||
.expect("should not fail to insert captcha");
|
||||
.await?;
|
||||
|
||||
let result = CaptchaAnswer::check_captcha(
|
||||
pool,
|
||||
|
@ -84,11 +84,12 @@ mod tests {
|
|||
.await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_captcha_repeat_answer_fails() {
|
||||
async fn test_captcha_repeat_answer_fails() -> LemmyResult<()> {
|
||||
let pool = &build_db_pool_for_tests();
|
||||
let pool = &mut pool.into();
|
||||
|
||||
|
@ -98,8 +99,7 @@ mod tests {
|
|||
answer: "XYZ".to_string(),
|
||||
},
|
||||
)
|
||||
.await
|
||||
.expect("should not fail to insert captcha");
|
||||
.await?;
|
||||
|
||||
let _result = CaptchaAnswer::check_captcha(
|
||||
pool,
|
||||
|
@ -120,5 +120,7 @@ mod tests {
|
|||
.await;
|
||||
|
||||
assert!(result_repeat.is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,19 +26,19 @@ use diesel::{
|
|||
QueryDsl,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
|
||||
impl LocalUser {
|
||||
pub async fn create(
|
||||
pool: &mut DbPool<'_>,
|
||||
form: &LocalUserInsertForm,
|
||||
languages: Vec<LanguageId>,
|
||||
) -> Result<LocalUser, Error> {
|
||||
) -> LemmyResult<LocalUser> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let mut form_with_encrypted_password = form.clone();
|
||||
|
||||
if let Some(password_encrypted) = &form.password_encrypted {
|
||||
let password_hash = hash(password_encrypted, DEFAULT_COST).expect("Couldn't hash password");
|
||||
let password_hash = hash(password_encrypted, DEFAULT_COST)?;
|
||||
form_with_encrypted_password.password_encrypted = Some(password_hash);
|
||||
}
|
||||
|
||||
|
@ -84,14 +84,15 @@ impl LocalUser {
|
|||
pool: &mut DbPool<'_>,
|
||||
local_user_id: LocalUserId,
|
||||
new_password: &str,
|
||||
) -> Result<Self, Error> {
|
||||
) -> LemmyResult<Self> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
let password_hash = hash(new_password, DEFAULT_COST).expect("Couldn't hash password");
|
||||
let password_hash = hash(new_password, DEFAULT_COST)?;
|
||||
|
||||
diesel::update(local_user::table.find(local_user_id))
|
||||
.set((local_user::password_encrypted.eq(password_hash),))
|
||||
.get_result::<Self>(conn)
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CouldntUpdateUser)
|
||||
}
|
||||
|
||||
pub async fn set_all_users_email_verified(pool: &mut DbPool<'_>) -> Result<Vec<Self>, Error> {
|
||||
|
|
|
@ -115,9 +115,7 @@ impl Post {
|
|||
.filter(post::local.eq(true))
|
||||
.filter(post::deleted.eq(false))
|
||||
.filter(post::removed.eq(false))
|
||||
.filter(
|
||||
post::published.ge(Utc::now().naive_utc() - SITEMAP_DAYS.expect("TimeDelta out of bounds")),
|
||||
)
|
||||
.filter(post::published.ge(Utc::now().naive_utc() - SITEMAP_DAYS))
|
||||
.order(post::published.desc())
|
||||
.limit(SITEMAP_LIMIT)
|
||||
.load::<(DbUrl, chrono::DateTime<Utc>)>(conn)
|
||||
|
|
|
@ -47,6 +47,7 @@ pub mod tagline;
|
|||
/// This is necessary so they can be successfully deserialized from API responses, even though the
|
||||
/// value is not sent by Lemmy. Necessary for crates which rely on Rust API such as
|
||||
/// lemmy-stats-crawler.
|
||||
#[allow(clippy::expect_used)]
|
||||
fn placeholder_apub_url() -> DbUrl {
|
||||
DbUrl(Box::new(
|
||||
Url::parse("http://example.com").expect("parse placeholder url"),
|
||||
|
|
|
@ -68,7 +68,7 @@ use url::Url;
|
|||
const FETCH_LIMIT_DEFAULT: i64 = 10;
|
||||
pub const FETCH_LIMIT_MAX: i64 = 50;
|
||||
pub const SITEMAP_LIMIT: i64 = 50000;
|
||||
pub const SITEMAP_DAYS: Option<TimeDelta> = TimeDelta::try_days(31);
|
||||
pub const SITEMAP_DAYS: TimeDelta = TimeDelta::days(31);
|
||||
pub const RANK_DEFAULT: f64 = 0.0001;
|
||||
|
||||
/// Some connection options to speed up queries
|
||||
|
@ -360,8 +360,8 @@ pub fn diesel_url_create(opt: Option<&str>) -> LemmyResult<Option<DbUrl>> {
|
|||
}
|
||||
|
||||
/// Sets a few additional config options necessary for starting lemmy
|
||||
fn build_config_options_uri_segment(config: &str) -> String {
|
||||
let mut url = Url::parse(config).expect("Couldn't parse postgres connection URI");
|
||||
fn build_config_options_uri_segment(config: &str) -> LemmyResult<String> {
|
||||
let mut url = Url::parse(config)?;
|
||||
|
||||
// Set `lemmy.protocol_and_hostname` so triggers can use it
|
||||
let lemmy_protocol_and_hostname_option =
|
||||
|
@ -377,7 +377,7 @@ fn build_config_options_uri_segment(config: &str) -> String {
|
|||
.join(" ");
|
||||
|
||||
url.set_query(Some(&format!("options={options_segments}")));
|
||||
url.into()
|
||||
Ok(url.into())
|
||||
}
|
||||
|
||||
fn establish_connection(config: &str) -> BoxFuture<ConnectionResult<AsyncPgConnection>> {
|
||||
|
@ -385,8 +385,11 @@ fn establish_connection(config: &str) -> BoxFuture<ConnectionResult<AsyncPgConne
|
|||
/// Use a once_lock to create the postgres connection config, since this config never changes
|
||||
static POSTGRES_CONFIG_WITH_OPTIONS: OnceLock<String> = OnceLock::new();
|
||||
|
||||
let config =
|
||||
POSTGRES_CONFIG_WITH_OPTIONS.get_or_init(|| build_config_options_uri_segment(config));
|
||||
let config = POSTGRES_CONFIG_WITH_OPTIONS.get_or_init(|| {
|
||||
build_config_options_uri_segment(config)
|
||||
.inspect_err(|e| error!("Couldn't parse postgres connection URI: {e}"))
|
||||
.unwrap_or_default()
|
||||
});
|
||||
|
||||
// We only support TLS with sslmode=require currently
|
||||
let conn = if config.contains("sslmode=require") {
|
||||
|
@ -495,6 +498,7 @@ pub fn build_db_pool() -> LemmyResult<ActualDbPool> {
|
|||
Ok(pool)
|
||||
}
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
pub fn build_db_pool_for_tests() -> ActualDbPool {
|
||||
build_db_pool().expect("db pool missing")
|
||||
}
|
||||
|
@ -511,6 +515,7 @@ pub fn post_to_comment_sort_type(sort: PostSortType) -> CommentSortType {
|
|||
}
|
||||
}
|
||||
|
||||
#[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")
|
||||
|
|
|
@ -20,7 +20,7 @@ use lemmy_db_schema::{
|
|||
ReadFn,
|
||||
},
|
||||
};
|
||||
use lemmy_utils::error::{LemmyError, LemmyErrorType};
|
||||
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
use std::future::{ready, Ready};
|
||||
|
||||
enum ReadBy<'a> {
|
||||
|
@ -146,7 +146,7 @@ impl LocalUserView {
|
|||
name: &str,
|
||||
bio: &str,
|
||||
admin: bool,
|
||||
) -> Result<Self, Error> {
|
||||
) -> LemmyResult<Self> {
|
||||
let instance_id = Instance::read_or_create(pool, "example.com".to_string())
|
||||
.await?
|
||||
.id;
|
||||
|
@ -163,7 +163,9 @@ impl LocalUserView {
|
|||
};
|
||||
let local_user = LocalUser::create(pool, &user_form, vec![]).await?;
|
||||
|
||||
LocalUserView::read(pool, local_user.id).await
|
||||
LocalUserView::read(pool, local_user.id)
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::NotFound)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -501,6 +501,7 @@ pub struct PostQuery<'a> {
|
|||
}
|
||||
|
||||
impl<'a> PostQuery<'a> {
|
||||
#[allow(clippy::expect_used)]
|
||||
async fn prefetch_upper_bound_for_page_before(
|
||||
&self,
|
||||
site: &Site,
|
||||
|
|
|
@ -286,7 +286,7 @@ mod tests {
|
|||
CommunityVisibility,
|
||||
SubscribedType,
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
use serial_test::serial;
|
||||
use url::Url;
|
||||
|
||||
|
@ -495,7 +495,7 @@ mod tests {
|
|||
};
|
||||
let communities = query.list(&data.site, pool).await?;
|
||||
for (i, c) in communities.iter().enumerate().skip(1) {
|
||||
let prev = communities.get(i - 1).expect("No previous community?");
|
||||
let prev = communities.get(i - 1).ok_or(LemmyErrorType::NotFound)?;
|
||||
assert!(c.community.title.cmp(&prev.community.title).is_ge());
|
||||
}
|
||||
|
||||
|
@ -505,7 +505,7 @@ mod tests {
|
|||
};
|
||||
let communities = query.list(&data.site, pool).await?;
|
||||
for (i, c) in communities.iter().enumerate().skip(1) {
|
||||
let prev = communities.get(i - 1).expect("No previous community?");
|
||||
let prev = communities.get(i - 1).ok_or(LemmyErrorType::NotFound)?;
|
||||
assert!(c.community.title.cmp(&prev.community.title).is_le());
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ use std::{
|
|||
/// currently fairly high because of the current structure of storing inboxes for every person, not
|
||||
/// having a separate list of shared_inboxes, and the architecture of having every instance queue be
|
||||
/// fully separate. (see https://github.com/LemmyNet/lemmy/issues/3958)
|
||||
#[allow(clippy::expect_used)]
|
||||
static FOLLOW_ADDITIONS_RECHECK_DELAY: LazyLock<chrono::TimeDelta> = LazyLock::new(|| {
|
||||
if *LEMMY_TEST_FAST_FEDERATION {
|
||||
chrono::TimeDelta::try_seconds(1).expect("TimeDelta out of bounds")
|
||||
|
@ -33,6 +34,7 @@ static FOLLOW_ADDITIONS_RECHECK_DELAY: LazyLock<chrono::TimeDelta> = LazyLock::n
|
|||
/// The same as FOLLOW_ADDITIONS_RECHECK_DELAY, but triggering when the last person on an instance
|
||||
/// unfollows a specific remote community. This is expected to happen pretty rarely and updating it
|
||||
/// in a timely manner is not too important.
|
||||
#[allow(clippy::expect_used)]
|
||||
static FOLLOW_REMOVALS_RECHECK_DELAY: LazyLock<chrono::TimeDelta> =
|
||||
LazyLock::new(|| chrono::TimeDelta::try_hours(1).expect("TimeDelta out of bounds"));
|
||||
|
||||
|
@ -472,6 +474,7 @@ mod tests {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
#[tokio::test]
|
||||
async fn test_update_communities() -> LemmyResult<()> {
|
||||
let mut collector = setup_collector();
|
||||
|
|
|
@ -331,7 +331,7 @@ impl InstanceWorker {
|
|||
self.state.last_successful_published_time = next.published;
|
||||
}
|
||||
|
||||
let save_state_every = chrono::Duration::from_std(SAVE_STATE_EVERY_TIME).expect("not negative");
|
||||
let save_state_every = chrono::Duration::from_std(SAVE_STATE_EVERY_TIME)?;
|
||||
if force_write || (Utc::now() - self.last_state_insert) > save_state_every {
|
||||
self.save_and_send_state().await?;
|
||||
}
|
||||
|
|
|
@ -312,12 +312,16 @@ where
|
|||
}
|
||||
|
||||
// TODO: remove these conversions after actix-web upgrades to http 1.0
|
||||
#[allow(clippy::expect_used)]
|
||||
fn convert_status(status: http::StatusCode) -> StatusCode {
|
||||
StatusCode::from_u16(status.as_u16()).expect("status can be converted")
|
||||
}
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
fn convert_method(method: &Method) -> http::Method {
|
||||
http::Method::from_bytes(method.as_str().as_bytes()).expect("method can be converted")
|
||||
}
|
||||
|
||||
fn convert_header<'a>(name: &'a http::HeaderName, value: &'a HeaderValue) -> (&'a str, &'a [u8]) {
|
||||
(name.as_str(), value.as_bytes())
|
||||
}
|
||||
|
|
|
@ -3,13 +3,16 @@ use activitypub_federation::{
|
|||
fetch::webfinger::{extract_webfinger_name, Webfinger, WebfingerLink, WEBFINGER_CONTENT_TYPE},
|
||||
};
|
||||
use actix_web::{web, web::Query, HttpResponse};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_api_common::{context::LemmyContext, LemmyErrorType};
|
||||
use lemmy_db_schema::{
|
||||
source::{community::Community, person::Person},
|
||||
traits::ApubActor,
|
||||
CommunityVisibility,
|
||||
};
|
||||
use lemmy_utils::{cache_header::cache_3days, error::LemmyResult};
|
||||
use lemmy_utils::{
|
||||
cache_header::cache_3days,
|
||||
error::{LemmyErrorExt, LemmyResult},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use url::Url;
|
||||
|
@ -41,7 +44,7 @@ async fn get_webfinger_response(
|
|||
let links = if name == context.settings().hostname {
|
||||
// webfinger response for instance actor (required for mastodon authorized fetch)
|
||||
let url = Url::parse(&context.settings().get_protocol_and_hostname())?;
|
||||
vec![webfinger_link_for_actor(Some(url), "none", &context)]
|
||||
vec![webfinger_link_for_actor(Some(url), "none", &context)?]
|
||||
} else {
|
||||
// webfinger response for user/community
|
||||
let user_id: Option<Url> = Person::read_from_name(&mut context.pool(), name, false)
|
||||
|
@ -65,8 +68,8 @@ async fn get_webfinger_response(
|
|||
// Mastodon seems to prioritize the last webfinger item in case of duplicates. Put
|
||||
// community last so that it gets prioritized. For Lemmy the order doesn't matter.
|
||||
vec![
|
||||
webfinger_link_for_actor(user_id, "Person", &context),
|
||||
webfinger_link_for_actor(community_id, "Group", &context),
|
||||
webfinger_link_for_actor(user_id, "Person", &context)?,
|
||||
webfinger_link_for_actor(community_id, "Group", &context)?,
|
||||
]
|
||||
}
|
||||
.into_iter()
|
||||
|
@ -94,11 +97,11 @@ fn webfinger_link_for_actor(
|
|||
url: Option<Url>,
|
||||
kind: &str,
|
||||
context: &LemmyContext,
|
||||
) -> Vec<WebfingerLink> {
|
||||
) -> LemmyResult<Vec<WebfingerLink>> {
|
||||
if let Some(url) = url {
|
||||
let type_key = "https://www.w3.org/ns/activitystreams#type"
|
||||
.parse()
|
||||
.expect("parse url");
|
||||
.with_lemmy_type(LemmyErrorType::InvalidUrl)?;
|
||||
|
||||
let mut vec = vec![
|
||||
WebfingerLink {
|
||||
|
@ -128,8 +131,8 @@ fn webfinger_link_for_actor(
|
|||
..Default::default()
|
||||
});
|
||||
}
|
||||
vec
|
||||
Ok(vec)
|
||||
} else {
|
||||
vec![]
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ pub async fn send_email(
|
|||
let email_and_port = email_config.smtp_server.split(':').collect::<Vec<&str>>();
|
||||
let email = *email_and_port
|
||||
.first()
|
||||
.ok_or(LemmyErrorType::MissingAnEmail)?;
|
||||
.ok_or(LemmyErrorType::EmailRequired)?;
|
||||
let port = email_and_port
|
||||
.get(1)
|
||||
.ok_or(LemmyErrorType::EmailSmtpServerNeedsAPort)?
|
||||
|
@ -45,16 +45,20 @@ pub async fn send_email(
|
|||
// use usize::MAX as the line wrap length, since lettre handles the wrapping for us
|
||||
let plain_text = html2text::from_read(html.as_bytes(), usize::MAX);
|
||||
|
||||
let smtp_from_address = &email_config.smtp_from_address;
|
||||
|
||||
let email = Message::builder()
|
||||
.from(
|
||||
email_config
|
||||
.smtp_from_address
|
||||
smtp_from_address
|
||||
.parse()
|
||||
.expect("email from address isn't valid"),
|
||||
.with_lemmy_type(LemmyErrorType::InvalidEmailAddress(
|
||||
smtp_from_address.into(),
|
||||
))?,
|
||||
)
|
||||
.to(Mailbox::new(
|
||||
Some(to_username.to_string()),
|
||||
Address::from_str(to_email).expect("email to address isn't valid"),
|
||||
Address::from_str(to_email)
|
||||
.with_lemmy_type(LemmyErrorType::InvalidEmailAddress(to_email.into()))?,
|
||||
))
|
||||
.message_id(Some(format!("<{}@{}>", Uuid::new_v4(), settings.hostname)))
|
||||
.subject(subject)
|
||||
|
@ -62,7 +66,7 @@ pub async fn send_email(
|
|||
plain_text,
|
||||
html.to_string(),
|
||||
))
|
||||
.expect("email built incorrectly");
|
||||
.with_lemmy_type(LemmyErrorType::EmailSendFailed)?;
|
||||
|
||||
// don't worry about 'dangeous'. it's just that leaving it at the default configuration
|
||||
// is bad.
|
||||
|
|
|
@ -73,7 +73,7 @@ pub enum LemmyErrorType {
|
|||
NoEmailSetup,
|
||||
LocalSiteNotSetup,
|
||||
EmailSmtpServerNeedsAPort,
|
||||
MissingAnEmail,
|
||||
InvalidEmailAddress(String),
|
||||
RateLimitError,
|
||||
InvalidName,
|
||||
InvalidDisplayName,
|
||||
|
@ -129,6 +129,7 @@ pub enum LemmyErrorType {
|
|||
InvalidRegex,
|
||||
CaptchaIncorrect,
|
||||
CouldntCreateAudioCaptcha,
|
||||
CouldntCreateImageCaptcha,
|
||||
InvalidUrlScheme,
|
||||
CouldntSendWebmention,
|
||||
ContradictingFilters,
|
||||
|
@ -185,6 +186,7 @@ pub enum FederationError {
|
|||
CantDeleteSite,
|
||||
ObjectIsNotPublic,
|
||||
ObjectIsNotPrivate,
|
||||
Unreachable,
|
||||
}
|
||||
|
||||
cfg_if! {
|
||||
|
|
|
@ -29,6 +29,7 @@ pub struct RateLimitCell {
|
|||
state: Arc<Mutex<RateLimitState>>,
|
||||
}
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
impl RateLimitCell {
|
||||
pub fn new(rate_limit_config: EnumMap<ActionType, BucketConfig>) -> Self {
|
||||
let state = Arc::new(Mutex::new(RateLimitState::new(rate_limit_config)));
|
||||
|
@ -133,6 +134,7 @@ pub struct RateLimitedMiddleware<S> {
|
|||
service: Rc<S>,
|
||||
}
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
impl RateLimitChecker {
|
||||
/// Returns true if the request passed the rate limit, false if it failed and should be rejected.
|
||||
pub fn check(self, ip_addr: IpAddr) -> bool {
|
||||
|
|
|
@ -18,6 +18,7 @@ pub struct InstantSecs {
|
|||
secs: u32,
|
||||
}
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
impl InstantSecs {
|
||||
pub fn now() -> Self {
|
||||
InstantSecs {
|
||||
|
|
|
@ -10,6 +10,7 @@ where
|
|||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[allow(clippy::expect_used)]
|
||||
async fn retry_custom<F, Fut, T>(f: F) -> Result<T, reqwest_middleware::Error>
|
||||
where
|
||||
F: Fn() -> Fut,
|
||||
|
|
|
@ -11,19 +11,20 @@ pub fn jsonify_plain_text_errors<BODY>(
|
|||
return Ok(ErrorHandlerResponse::Response(res.map_into_left_body()));
|
||||
}
|
||||
// We're assuming that any LemmyError is already in JSON format, so we don't need to do anything
|
||||
if maybe_error
|
||||
.expect("http responses with 400-599 statuses should have an error object")
|
||||
.as_error::<LemmyError>()
|
||||
.is_some()
|
||||
{
|
||||
return Ok(ErrorHandlerResponse::Response(res.map_into_left_body()));
|
||||
if let Some(maybe_error) = maybe_error {
|
||||
if maybe_error.as_error::<LemmyError>().is_some() {
|
||||
return Ok(ErrorHandlerResponse::Response(res.map_into_left_body()));
|
||||
}
|
||||
}
|
||||
|
||||
let (req, res) = res.into_parts();
|
||||
let error = res
|
||||
.error()
|
||||
.expect("expected an error object in the response");
|
||||
let response = HttpResponse::build(res.status()).json(LemmyErrorType::Unknown(error.to_string()));
|
||||
let (req, res_parts) = res.into_parts();
|
||||
let lemmy_err_type = if let Some(error) = res_parts.error() {
|
||||
LemmyErrorType::Unknown(error.to_string())
|
||||
} else {
|
||||
LemmyErrorType::Unknown("couldnt build json".into())
|
||||
};
|
||||
|
||||
let response = HttpResponse::build(res_parts.status()).json(lemmy_err_type);
|
||||
|
||||
let service_response = ServiceResponse::new(req, response);
|
||||
Ok(ErrorHandlerResponse::Response(
|
||||
|
|
|
@ -3,6 +3,7 @@ use anyhow::{anyhow, Context};
|
|||
use deser_hjson::from_str;
|
||||
use regex::Regex;
|
||||
use std::{env, fs, io::Error, sync::LazyLock};
|
||||
use url::Url;
|
||||
use urlencoding::encode;
|
||||
|
||||
pub mod structs;
|
||||
|
@ -11,6 +12,7 @@ use structs::{DatabaseConnection, PictrsConfig, PictrsImageMode, Settings};
|
|||
|
||||
static DEFAULT_CONFIG_FILE: &str = "config/config.hjson";
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
pub static SETTINGS: LazyLock<Settings> = LazyLock::new(|| {
|
||||
if env::var("LEMMY_INITIALIZE_WITH_DEFAULT_SETTINGS").is_ok() {
|
||||
println!(
|
||||
|
@ -23,6 +25,7 @@ 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,}})@{}$",
|
||||
|
@ -128,3 +131,9 @@ impl PictrsConfig {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
/// Necessary to avoid URL expect failures
|
||||
fn pictrs_placeholder_url() -> Url {
|
||||
Url::parse("http://localhost:8080").expect("parse pictrs url")
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use super::pictrs_placeholder_url;
|
||||
use doku::Document;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use smart_default::SmartDefault;
|
||||
|
@ -68,7 +69,7 @@ impl Settings {
|
|||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct PictrsConfig {
|
||||
/// Address where pictrs is available (for image hosting)
|
||||
#[default(Url::parse("http://localhost:8080").expect("parse pictrs url"))]
|
||||
#[default(pictrs_placeholder_url())]
|
||||
#[doku(example = "http://localhost:8080")]
|
||||
pub url: Url,
|
||||
|
||||
|
|
|
@ -58,11 +58,13 @@ fn find_urls<T: NodeValue + UrlAndTitle>(src: &str) -> Vec<(usize, usize)> {
|
|||
let mut links_offsets = vec![];
|
||||
ast.walk(|node, _depth| {
|
||||
if let Some(image) = node.cast::<T>() {
|
||||
let (_, node_offset) = node.srcmap.expect("srcmap is none").get_byte_offsets();
|
||||
let start_offset = node_offset - image.url_len() - 1 - image.title_len();
|
||||
let end_offset = node_offset - 1;
|
||||
if let Some(srcmap) = node.srcmap {
|
||||
let (_, node_offset) = srcmap.get_byte_offsets();
|
||||
let start_offset = node_offset - image.url_len() - 1 - image.title_len();
|
||||
let end_offset = node_offset - 1;
|
||||
|
||||
links_offsets.push((start_offset, end_offset));
|
||||
links_offsets.push((start_offset, end_offset));
|
||||
}
|
||||
}
|
||||
});
|
||||
links_offsets
|
||||
|
|
|
@ -2,6 +2,7 @@ use itertools::Itertools;
|
|||
use regex::Regex;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
static MENTIONS_REGEX: LazyLock<Regex> = LazyLock::new(|| {
|
||||
Regex::new(r"@(?P<name>[\w.]+)@(?P<domain>[a-zA-Z0-9._:-]+)").expect("compile regex")
|
||||
});
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
|
||||
use regex::{Regex, RegexBuilder};
|
||||
|
||||
pub fn remove_slurs(test: &str, slur_regex: &Option<Regex>) -> String {
|
||||
if let Some(slur_regex) = slur_regex {
|
||||
pub fn remove_slurs(test: &str, slur_regex: &Option<LemmyResult<Regex>>) -> String {
|
||||
if let Some(Ok(slur_regex)) = slur_regex {
|
||||
slur_regex.replace_all(test, "*removed*").to_string()
|
||||
} else {
|
||||
test.to_string()
|
||||
|
@ -11,9 +11,9 @@ pub fn remove_slurs(test: &str, slur_regex: &Option<Regex>) -> String {
|
|||
|
||||
pub(crate) fn slur_check<'a>(
|
||||
test: &'a str,
|
||||
slur_regex: &'a Option<Regex>,
|
||||
slur_regex: &'a Option<LemmyResult<Regex>>,
|
||||
) -> Result<(), Vec<&'a str>> {
|
||||
if let Some(slur_regex) = slur_regex {
|
||||
if let Some(Ok(slur_regex)) = slur_regex {
|
||||
let mut matches: Vec<&str> = slur_regex.find_iter(test).map(|mat| mat.as_str()).collect();
|
||||
|
||||
// Unique
|
||||
|
@ -30,16 +30,16 @@ pub(crate) fn slur_check<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn build_slur_regex(regex_str: Option<&str>) -> Option<Regex> {
|
||||
pub fn build_slur_regex(regex_str: Option<&str>) -> Option<LemmyResult<Regex>> {
|
||||
regex_str.map(|slurs| {
|
||||
RegexBuilder::new(slurs)
|
||||
.case_insensitive(true)
|
||||
.build()
|
||||
.expect("compile regex")
|
||||
.with_lemmy_type(LemmyErrorType::InvalidRegex)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn check_slurs(text: &str, slur_regex: &Option<Regex>) -> LemmyResult<()> {
|
||||
pub fn check_slurs(text: &str, slur_regex: &Option<LemmyResult<Regex>>) -> LemmyResult<()> {
|
||||
if let Err(slurs) = slur_check(text, slur_regex) {
|
||||
Err(anyhow::anyhow!("{}", slurs_vec_to_str(&slurs))).with_lemmy_type(LemmyErrorType::Slurs)
|
||||
} else {
|
||||
|
@ -47,7 +47,10 @@ pub fn check_slurs(text: &str, slur_regex: &Option<Regex>) -> LemmyResult<()> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn check_slurs_opt(text: &Option<String>, slur_regex: &Option<Regex>) -> LemmyResult<()> {
|
||||
pub fn check_slurs_opt(
|
||||
text: &Option<String>,
|
||||
slur_regex: &Option<LemmyResult<Regex>>,
|
||||
) -> LemmyResult<()> {
|
||||
match text {
|
||||
Some(t) => check_slurs(t, slur_regex),
|
||||
None => Ok(()),
|
||||
|
@ -64,7 +67,7 @@ pub(crate) fn slurs_vec_to_str(slurs: &[&str]) -> String {
|
|||
mod test {
|
||||
|
||||
use crate::{
|
||||
error::LemmyResult,
|
||||
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
|
||||
utils::slurs::{remove_slurs, slur_check, slurs_vec_to_str},
|
||||
};
|
||||
use pretty_assertions::assert_eq;
|
||||
|
@ -72,7 +75,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_slur_filter() -> LemmyResult<()> {
|
||||
let slur_regex = Some(RegexBuilder::new(r"(fag(g|got|tard)?\b|cock\s?sucker(s|ing)?|ni((g{2,}|q)+|[gq]{2,})[e3r]+(s|z)?|mudslime?s?|kikes?|\bspi(c|k)s?\b|\bchinks?|gooks?|bitch(es|ing|y)?|whor(es?|ing)|\btr(a|@)nn?(y|ies?)|\b(b|re|r)tard(ed)?s?)").case_insensitive(true).build()?);
|
||||
let slur_regex = Some(RegexBuilder::new(r"(fag(g|got|tard)?\b|cock\s?sucker(s|ing)?|ni((g{2,}|q)+|[gq]{2,})[e3r]+(s|z)?|mudslime?s?|kikes?|\bspi(c|k)s?\b|\bchinks?|gooks?|bitch(es|ing|y)?|whor(es?|ing)|\btr(a|@)nn?(y|ies?)|\b(b|re|r)tard(ed)?s?)").case_insensitive(true).build().with_lemmy_type(LemmyErrorType::InvalidRegex));
|
||||
let test =
|
||||
"faggot test kike tranny cocksucker retardeds. Capitalized Niggerz. This is a bunch of other safe text.";
|
||||
let slur_free = "No slurs here";
|
||||
|
|
|
@ -6,11 +6,13 @@ use std::sync::LazyLock;
|
|||
use url::{ParseError, Url};
|
||||
|
||||
// From here: https://github.com/vector-im/element-android/blob/develop/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt#L35
|
||||
#[allow(clippy::expect_used)]
|
||||
static VALID_MATRIX_ID_REGEX: LazyLock<Regex> = LazyLock::new(|| {
|
||||
Regex::new(r"^@[A-Za-z0-9\x21-\x39\x3B-\x7F]+:[A-Za-z0-9.-]+(:[0-9]{2,5})?$")
|
||||
.expect("compile regex")
|
||||
});
|
||||
// taken from https://en.wikipedia.org/wiki/UTM_parameters
|
||||
#[allow(clippy::expect_used)]
|
||||
static URL_CLEANER: LazyLock<UrlCleaner> =
|
||||
LazyLock::new(|| UrlCleaner::from_embedded_rules().expect("compile clearurls"));
|
||||
const ALLOWED_POST_URL_SCHEMES: [&str; 3] = ["http", "https", "magnet"];
|
||||
|
@ -88,6 +90,7 @@ pub fn is_valid_actor_name(name: &str, actor_name_max_length: usize) -> LemmyRes
|
|||
// Only allow characters from a single alphabet per username. This avoids problems with lookalike
|
||||
// characters like `o` which looks identical in Latin and Cyrillic, and can be used to imitate
|
||||
// other users. Checks for additional alphabets can be added in the same way.
|
||||
#[allow(clippy::expect_used)]
|
||||
static VALID_ACTOR_NAME_REGEX: LazyLock<Regex> = LazyLock::new(|| {
|
||||
Regex::new(r"^(?:[a-zA-Z0-9_]+|[0-9_\p{Arabic}]+|[0-9_\p{Cyrillic}]+)$").expect("compile regex")
|
||||
});
|
||||
|
@ -218,33 +221,32 @@ fn min_length_check(item: &str, min_length: usize, min_msg: LemmyErrorType) -> L
|
|||
}
|
||||
|
||||
/// Attempts to build a regex and check it for common errors before inserting into the DB.
|
||||
pub fn build_and_check_regex(regex_str_opt: &Option<&str>) -> LemmyResult<Option<Regex>> {
|
||||
regex_str_opt.map_or_else(
|
||||
|| Ok(None::<Regex>),
|
||||
|regex_str| {
|
||||
if regex_str.is_empty() {
|
||||
// If the proposed regex is empty, return as having no regex at all; this is the same
|
||||
// behavior that happens downstream before the write to the database.
|
||||
return Ok(None::<Regex>);
|
||||
}
|
||||
|
||||
RegexBuilder::new(regex_str)
|
||||
.case_insensitive(true)
|
||||
.build()
|
||||
.with_lemmy_type(LemmyErrorType::InvalidRegex)
|
||||
.and_then(|regex| {
|
||||
// NOTE: It is difficult to know, in the universe of user-crafted regex, which ones
|
||||
// may match against any string text. To keep it simple, we'll match the regex
|
||||
// against an innocuous string - a single number - which should help catch a regex
|
||||
// that accidentally matches against all strings.
|
||||
if regex.is_match("1") {
|
||||
Err(LemmyErrorType::PermissiveRegex.into())
|
||||
} else {
|
||||
Ok(Some(regex))
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
pub fn build_and_check_regex(regex_str_opt: &Option<&str>) -> Option<LemmyResult<Regex>> {
|
||||
if let Some(regex) = regex_str_opt {
|
||||
if regex.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
RegexBuilder::new(regex)
|
||||
.case_insensitive(true)
|
||||
.build()
|
||||
.with_lemmy_type(LemmyErrorType::InvalidRegex)
|
||||
.and_then(|regex| {
|
||||
// NOTE: It is difficult to know, in the universe of user-crafted regex, which ones
|
||||
// may match against any string text. To keep it simple, we'll match the regex
|
||||
// against an innocuous string - a single number - which should help catch a regex
|
||||
// that accidentally matches against all strings.
|
||||
if regex.is_match("1") {
|
||||
Err(LemmyErrorType::PermissiveRegex.into())
|
||||
} else {
|
||||
Ok(regex)
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Cleans a url of tracking parameters.
|
||||
|
@ -565,13 +567,27 @@ Line3",
|
|||
|
||||
#[test]
|
||||
fn test_valid_slur_regex() {
|
||||
let valid_regexes = [&None, &Some(""), &Some("(foo|bar)")];
|
||||
let valid_regex = Some("(foo|bar)");
|
||||
let result = build_and_check_regex(&valid_regex);
|
||||
assert!(
|
||||
result.is_some_and(|x| x.is_ok()),
|
||||
"Testing regex: {:?}",
|
||||
valid_regex
|
||||
);
|
||||
}
|
||||
|
||||
valid_regexes.iter().for_each(|regex| {
|
||||
let result = build_and_check_regex(regex);
|
||||
#[test]
|
||||
fn test_missing_slur_regex() {
|
||||
let missing_regex = None;
|
||||
let result = build_and_check_regex(&missing_regex);
|
||||
assert!(result.is_none());
|
||||
}
|
||||
|
||||
assert!(result.is_ok(), "Testing regex: {:?}", regex);
|
||||
});
|
||||
#[test]
|
||||
fn test_empty_slur_regex() {
|
||||
let empty = Some("");
|
||||
let result = build_and_check_regex(&empty);
|
||||
assert!(result.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -587,9 +603,9 @@ Line3",
|
|||
.for_each(|(regex_str, expected_err)| {
|
||||
let result = build_and_check_regex(regex_str);
|
||||
|
||||
assert!(result.is_err());
|
||||
assert!(result.as_ref().is_some_and(Result::is_err));
|
||||
assert!(
|
||||
result.is_err_and(|e| e.error_type.eq(&expected_err.clone())),
|
||||
result.is_some_and(|x| x.is_err_and(|e| e.error_type.eq(&expected_err.clone()))),
|
||||
"Testing regex {:?}, expected error {}",
|
||||
regex_str,
|
||||
expected_err
|
||||
|
|
|
@ -29,7 +29,10 @@ use lemmy_db_schema::{
|
|||
traits::Crud,
|
||||
utils::{get_conn, DbPool},
|
||||
};
|
||||
use lemmy_utils::{error::LemmyResult, settings::structs::Settings};
|
||||
use lemmy_utils::{
|
||||
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
|
||||
settings::structs::Settings,
|
||||
};
|
||||
use tracing::info;
|
||||
use url::Url;
|
||||
|
||||
|
@ -421,7 +424,7 @@ async fn initialize_local_site_2022_10_10(
|
|||
|
||||
let domain = settings
|
||||
.get_hostname_without_port()
|
||||
.expect("must have domain");
|
||||
.with_lemmy_type(LemmyErrorType::Unknown("must have domain".into()))?;
|
||||
|
||||
// Upsert this to the instance table
|
||||
let instance = Instance::read_or_create(pool, domain).await?;
|
||||
|
|
|
@ -37,7 +37,7 @@ use lemmy_db_schema::{source::secret::Secret, utils::build_db_pool};
|
|||
use lemmy_federate::{Opts, SendManager};
|
||||
use lemmy_routes::{feeds, images, nodeinfo, webfinger};
|
||||
use lemmy_utils::{
|
||||
error::LemmyResult,
|
||||
error::{LemmyErrorType, LemmyResult},
|
||||
rate_limit::RateLimitCell,
|
||||
response::jsonify_plain_text_errors,
|
||||
settings::{structs::Settings, SETTINGS},
|
||||
|
@ -178,7 +178,8 @@ pub async fn start_lemmy_server(args: CmdArgs) -> LemmyResult<()> {
|
|||
.set(Box::new(move |d, c| {
|
||||
Box::pin(match_outgoing_activities(d, c))
|
||||
}))
|
||||
.expect("set function pointer");
|
||||
.map_err(|_| LemmyErrorType::Unknown("couldnt set function pointer".into()))?;
|
||||
|
||||
let request_data = federation_config.to_request_data();
|
||||
let outgoing_activities_task = tokio::task::spawn(handle_outgoing_activities(
|
||||
request_data.reset_request_count(),
|
||||
|
@ -281,7 +282,7 @@ fn create_http_server(
|
|||
let prom_api_metrics = PrometheusMetricsBuilder::new("lemmy_api")
|
||||
.registry(default_registry().clone())
|
||||
.build()
|
||||
.expect("Should always be buildable");
|
||||
.map_err(|e| LemmyErrorType::Unknown(format!("Should always be buildable: {e}")))?;
|
||||
|
||||
let context: LemmyContext = federation_config.deref().clone();
|
||||
let rate_limit_cell = federation_config.rate_limit_cell().clone();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use clap::Parser;
|
||||
use lemmy_server::{start_lemmy_server, CmdArgs};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
use tracing::level_filters::LevelFilter;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
|
@ -17,7 +17,7 @@ pub async fn main() -> LemmyResult<()> {
|
|||
|
||||
rustls::crypto::ring::default_provider()
|
||||
.install_default()
|
||||
.expect("Failed to install rustls crypto provider");
|
||||
.map_err(|_| LemmyErrorType::Unknown("Failed to install rustls crypto provider".into()))?;
|
||||
|
||||
start_lemmy_server(args).await?;
|
||||
Ok(())
|
||||
|
|
|
@ -169,10 +169,7 @@ async fn process_ranks_in_batches(
|
|||
where_clause: &str,
|
||||
set_clause: &str,
|
||||
) {
|
||||
let process_start_time: DateTime<Utc> = Utc
|
||||
.timestamp_opt(0, 0)
|
||||
.single()
|
||||
.expect("0 timestamp creation");
|
||||
let process_start_time: DateTime<Utc> = Utc.timestamp_opt(0, 0).single().unwrap_or_default();
|
||||
|
||||
let update_batch_size = 1000; // Bigger batches than this tend to cause seq scans
|
||||
let mut processed_rows_count = 0;
|
||||
|
@ -220,10 +217,7 @@ async fn process_ranks_in_batches(
|
|||
/// Post aggregates is a special case, since it needs to join to the community_aggregates
|
||||
/// table, to get the active monthly user counts.
|
||||
async fn process_post_aggregates_ranks_in_batches(conn: &mut AsyncPgConnection) {
|
||||
let process_start_time: DateTime<Utc> = Utc
|
||||
.timestamp_opt(0, 0)
|
||||
.single()
|
||||
.expect("0 timestamp creation");
|
||||
let process_start_time: DateTime<Utc> = Utc.timestamp_opt(0, 0).single().unwrap_or_default();
|
||||
|
||||
let update_batch_size = 1000; // Bigger batches than this tend to cause seq scans
|
||||
let mut processed_rows_count = 0;
|
||||
|
|
Loading…
Reference in a new issue