From 99d01e186ada2bb3f5cf3fdc447e0cbc5dfae089 Mon Sep 17 00:00:00 2001 From: Sander Saarend Date: Mon, 25 Mar 2024 13:56:03 +0200 Subject: [PATCH 1/8] Fix rate limiter (#4560) --- crates/utils/src/rate_limit/rate_limiter.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/crates/utils/src/rate_limit/rate_limiter.rs b/crates/utils/src/rate_limit/rate_limiter.rs index d452fcbb3..66d830c7e 100644 --- a/crates/utils/src/rate_limit/rate_limiter.rs +++ b/crates/utils/src/rate_limit/rate_limiter.rs @@ -158,7 +158,7 @@ impl MapLevel for Map { // Evaluated if `some_children_remaining` is false let total_has_refill_in_future = || { - group.total.into_iter().all(|(action_type, bucket)| { + group.total.into_iter().any(|(action_type, bucket)| { #[allow(clippy::indexing_slicing)] let config = configs[action_type]; bucket.update(now, config).tokens != config.capacity @@ -416,5 +416,23 @@ mod tests { rate_limiter.remove_full_buckets(now); assert!(rate_limiter.ipv4_buckets.is_empty()); assert!(rate_limiter.ipv6_buckets.is_empty()); + + // `remove full buckets` should not remove empty buckets + let ip = "1.1.1.1".parse().unwrap(); + // empty the bucket with 2 requests + assert!(rate_limiter.check(ActionType::Post, ip, now)); + assert!(rate_limiter.check(ActionType::Post, ip, now)); + + rate_limiter.remove_full_buckets(now); + assert!(!rate_limiter.ipv4_buckets.is_empty()); + + // `remove full buckets` should not remove partial buckets + now.secs += 2; + let ip = "1.1.1.1".parse().unwrap(); + // Only make one request, so bucket still has 1 token + assert!(rate_limiter.check(ActionType::Post, ip, now)); + + rate_limiter.remove_full_buckets(now); + assert!(!rate_limiter.ipv4_buckets.is_empty()); } } From d06ef2c47e9b596fa4bce6b2d1576d76bfee6fef Mon Sep 17 00:00:00 2001 From: Nutomic Date: Mon, 25 Mar 2024 13:10:09 +0100 Subject: [PATCH 2/8] Migrate apub block activity to standard `endTime` property and deprecate `expires` (fixes #2316) (#4541) * Migrate apub block activity to standard `endTime` property (fixes #2316) * add todo --- crates/apub/src/activities/block/block_user.rs | 3 ++- crates/apub/src/activities/block/undo_block_user.rs | 2 +- crates/apub/src/protocol/activities/block/block_user.rs | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/apub/src/activities/block/block_user.rs b/crates/apub/src/activities/block/block_user.rs index 177df2ca5..a2a1f25bf 100644 --- a/crates/apub/src/activities/block/block_user.rs +++ b/crates/apub/src/activities/block/block_user.rs @@ -72,6 +72,7 @@ impl BlockUser { )?, audience, expires, + end_time: expires, }) } @@ -149,7 +150,7 @@ impl ActivityHandler for BlockUser { #[tracing::instrument(skip_all)] async fn receive(self, context: &Data) -> Result<(), LemmyError> { insert_received_activity(&self.id, context).await?; - let expires = self.expires.map(Into::into); + let expires = self.expires.or(self.end_time).map(Into::into); let mod_person = self.actor.dereference(context).await?; let blocked_person = self.object.dereference(context).await?; let target = self.target.dereference(context).await?; diff --git a/crates/apub/src/activities/block/undo_block_user.rs b/crates/apub/src/activities/block/undo_block_user.rs index da25a4af0..756d0a149 100644 --- a/crates/apub/src/activities/block/undo_block_user.rs +++ b/crates/apub/src/activities/block/undo_block_user.rs @@ -98,7 +98,7 @@ impl ActivityHandler for UndoBlockUser { #[tracing::instrument(skip_all)] async fn receive(self, context: &Data) -> Result<(), LemmyError> { insert_received_activity(&self.id, context).await?; - let expires = self.object.expires.map(Into::into); + let expires = self.object.expires.or(self.object.end_time).map(Into::into); let mod_person = self.actor.dereference(context).await?; let blocked_person = self.object.object.dereference(context).await?; match self.object.target.dereference(context).await? { diff --git a/crates/apub/src/protocol/activities/block/block_user.rs b/crates/apub/src/protocol/activities/block/block_user.rs index b958b58e1..c5cb4306c 100644 --- a/crates/apub/src/protocol/activities/block/block_user.rs +++ b/crates/apub/src/protocol/activities/block/block_user.rs @@ -38,7 +38,9 @@ pub struct BlockUser { pub(crate) remove_data: Option, /// block reason, written to mod log pub(crate) summary: Option, + /// TODO: deprecated pub(crate) expires: Option>, + pub(crate) end_time: Option>, } #[async_trait::async_trait] From 846848c4f6a5ab0ebce9c513f62ff0074b27f50c Mon Sep 17 00:00:00 2001 From: Nutomic Date: Mon, 25 Mar 2024 21:02:12 +0100 Subject: [PATCH 3/8] On registration, automatically set content languages from `accept-language` header (#4550) * On registration, automatically set content languages from accept header * no need to set site language or default language for new user anymore * fix test * fix langs * avoid duplicate writing of new user languages --- api_tests/src/user.spec.ts | 20 ++++ .../src/local_user/generate_totp_secret.rs | 5 +- crates/api/src/local_user/update_totp.rs | 5 +- crates/api_common/src/claims.rs | 4 +- crates/api_crud/src/user/create.rs | 32 ++++-- crates/apub/src/api/user_settings_backup.rs | 2 +- crates/db_schema/src/impls/actor_language.rs | 19 ++-- crates/db_schema/src/impls/local_user.rs | 100 +++++++++--------- .../src/impls/password_reset_request.rs | 4 +- crates/db_views/src/comment_report_view.rs | 4 +- crates/db_views/src/comment_view.rs | 2 +- crates/db_views/src/post_report_view.rs | 4 +- crates/db_views/src/post_view.rs | 3 +- .../src/registration_application_view.rs | 6 +- crates/db_views_actor/src/community_view.rs | 4 +- crates/db_views_actor/src/person_view.rs | 4 +- src/code_migrations.rs | 2 +- src/session_middleware.rs | 4 +- 18 files changed, 130 insertions(+), 94 deletions(-) diff --git a/api_tests/src/user.spec.ts b/api_tests/src/user.spec.ts index 4846d60f7..73f3f3942 100644 --- a/api_tests/src/user.spec.ts +++ b/api_tests/src/user.spec.ts @@ -139,3 +139,23 @@ test("Create user with Arabic name", async () => { let alphaPerson = (await resolvePerson(alpha, apShortname)).person; expect(alphaPerson).toBeDefined(); }); + +test("Create user with accept-language", async () => { + let lemmy_http = new LemmyHttp(alphaUrl, { + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language#syntax + headers: { "Accept-Language": "fr-CH, en;q=0.8, de;q=0.7, *;q=0.5" }, + }); + let user = await registerUser(lemmy_http, alphaUrl); + + let site = await getSite(user); + expect(site.my_user).toBeDefined(); + expect(site.my_user?.local_user_view.local_user.interface_language).toBe( + "fr", + ); + let langs = site.all_languages + .filter(a => site.my_user?.discussion_languages.includes(a.id)) + .map(l => l.code); + // should have languages from accept header, as well as "undetermined" + // which is automatically enabled by backend + expect(langs).toStrictEqual(["und", "de", "en", "fr"]); +}); diff --git a/crates/api/src/local_user/generate_totp_secret.rs b/crates/api/src/local_user/generate_totp_secret.rs index a983beaaa..342a90a78 100644 --- a/crates/api/src/local_user/generate_totp_secret.rs +++ b/crates/api/src/local_user/generate_totp_secret.rs @@ -6,10 +6,7 @@ use lemmy_api_common::{ person::GenerateTotpSecretResponse, sensitive::Sensitive, }; -use lemmy_db_schema::{ - source::local_user::{LocalUser, LocalUserUpdateForm}, - traits::Crud, -}; +use lemmy_db_schema::source::local_user::{LocalUser, LocalUserUpdateForm}; use lemmy_db_views::structs::{LocalUserView, SiteView}; use lemmy_utils::error::{LemmyError, LemmyErrorType}; diff --git a/crates/api/src/local_user/update_totp.rs b/crates/api/src/local_user/update_totp.rs index 8f37213e2..c8ca9f64e 100644 --- a/crates/api/src/local_user/update_totp.rs +++ b/crates/api/src/local_user/update_totp.rs @@ -4,10 +4,7 @@ use lemmy_api_common::{ context::LemmyContext, person::{UpdateTotp, UpdateTotpResponse}, }; -use lemmy_db_schema::{ - source::local_user::{LocalUser, LocalUserUpdateForm}, - traits::Crud, -}; +use lemmy_db_schema::source::local_user::{LocalUser, LocalUserUpdateForm}; use lemmy_db_views::structs::LocalUserView; use lemmy_utils::error::LemmyError; diff --git a/crates/api_common/src/claims.rs b/crates/api_common/src/claims.rs index 0a96f7455..0e6968742 100644 --- a/crates/api_common/src/claims.rs +++ b/crates/api_common/src/claims.rs @@ -124,7 +124,9 @@ mod tests { .password_encrypted("123456".to_string()) .build(); - let inserted_local_user = LocalUser::create(pool, &local_user_form).await.unwrap(); + let inserted_local_user = LocalUser::create(pool, &local_user_form, vec![]) + .await + .unwrap(); let req = TestRequest::default().to_http_request(); let jwt = Claims::generate(inserted_local_user.id, req, &context) diff --git a/crates/api_crud/src/user/create.rs b/crates/api_crud/src/user/create.rs index 29e16cb20..d24a287db 100644 --- a/crates/api_crud/src/user/create.rs +++ b/crates/api_crud/src/user/create.rs @@ -20,6 +20,7 @@ use lemmy_db_schema::{ aggregates::structs::PersonAggregates, source::{ captcha_answer::{CaptchaAnswer, CheckCaptchaAnswer}, + language::Language, local_user::{LocalUser, LocalUserInsertForm}, local_user_vote_display_mode::LocalUserVoteDisplayMode, person::{Person, PersonInsertForm}, @@ -36,6 +37,7 @@ use lemmy_utils::{ validation::is_valid_actor_name, }, }; +use std::collections::HashSet; #[tracing::instrument(skip(context))] pub async fn register( @@ -128,12 +130,15 @@ pub async fn register( let accepted_application = Some(!require_registration_application); // Get the user's preferred language using the Accept-Language header - let language_tag = req.headers().get("Accept-Language").and_then(|hdr| { - accept_language::parse(hdr.to_str().unwrap_or_default()) - .first() - // Remove the optional region code - .map(|lang_str| lang_str.split('-').next().unwrap_or_default().to_string()) - }); + let language_tags: Vec = req + .headers() + .get("Accept-Language") + .map(|hdr| accept_language::parse(hdr.to_str().unwrap_or_default())) + .iter() + .flatten() + // Remove the optional region code + .map(|lang_str| lang_str.split('-').next().unwrap_or_default().to_string()) + .collect(); // Create the local user let local_user_form = LocalUserInsertForm::builder() @@ -144,12 +149,23 @@ pub async fn register( .accepted_application(accepted_application) .default_listing_type(Some(local_site.default_post_listing_type)) .post_listing_mode(Some(local_site.default_post_listing_mode)) - .interface_language(language_tag) + .interface_language(language_tags.first().cloned()) // If its the initial site setup, they are an admin .admin(Some(!local_site.site_setup)) .build(); - let inserted_local_user = LocalUser::create(&mut context.pool(), &local_user_form).await?; + let all_languages = Language::read_all(&mut context.pool()).await?; + // use hashset to avoid duplicates + let mut language_ids = HashSet::new(); + for l in language_tags { + if let Some(found) = all_languages.iter().find(|all| all.code == l) { + language_ids.insert(found.id); + } + } + let language_ids = language_ids.into_iter().collect(); + + let inserted_local_user = + LocalUser::create(&mut context.pool(), &local_user_form, language_ids).await?; if local_site.site_setup && require_registration_application { // Create the registration application diff --git a/crates/apub/src/api/user_settings_backup.rs b/crates/apub/src/api/user_settings_backup.rs index a2e6c55ac..d46e415b0 100644 --- a/crates/apub/src/api/user_settings_backup.rs +++ b/crates/apub/src/api/user_settings_backup.rs @@ -361,7 +361,7 @@ mod tests { .person_id(person.id) .password_encrypted("pass".to_string()) .build(); - let local_user = LocalUser::create(&mut context.pool(), &user_form).await?; + let local_user = LocalUser::create(&mut context.pool(), &user_form, vec![]).await?; Ok(LocalUserView::read(&mut context.pool(), local_user.id).await?) } diff --git a/crates/db_schema/src/impls/actor_language.rs b/crates/db_schema/src/impls/actor_language.rs index 1cb5f4c77..29d99b2d8 100644 --- a/crates/db_schema/src/impls/actor_language.rs +++ b/crates/db_schema/src/impls/actor_language.rs @@ -523,10 +523,6 @@ mod tests { let pool = &mut pool.into(); let (site, instance) = create_test_site(pool).await; - let mut test_langs = test_langs1(pool).await; - SiteLanguage::update(pool, test_langs.clone(), &site) - .await - .unwrap(); let person_form = PersonInsertForm::builder() .name("my test person".to_string()) @@ -539,14 +535,13 @@ mod tests { .password_encrypted("my_pw".to_string()) .build(); - let local_user = LocalUser::create(pool, &local_user_form).await.unwrap(); + let local_user = LocalUser::create(pool, &local_user_form, vec![]) + .await + .unwrap(); let local_user_langs1 = LocalUserLanguage::read(pool, local_user.id).await.unwrap(); - // new user should be initialized with site languages and undetermined - //test_langs.push(UNDETERMINED_ID); - //test_langs.sort(); - test_langs.insert(0, UNDETERMINED_ID); - assert_eq!(test_langs, local_user_langs1); + // new user should be initialized with all languages + assert_eq!(0, local_user_langs1.len()); // update user languages let test_langs2 = test_langs2(pool).await; @@ -655,7 +650,9 @@ mod tests { .person_id(person.id) .password_encrypted("my_pw".to_string()) .build(); - let local_user = LocalUser::create(pool, &local_user_form).await.unwrap(); + let local_user = LocalUser::create(pool, &local_user_form, vec![]) + .await + .unwrap(); LocalUserLanguage::update(pool, test_langs2, local_user.id) .await .unwrap(); diff --git a/crates/db_schema/src/impls/local_user.rs b/crates/db_schema/src/impls/local_user.rs index 14ae65469..d253afd8d 100644 --- a/crates/db_schema/src/impls/local_user.rs +++ b/crates/db_schema/src/impls/local_user.rs @@ -1,12 +1,11 @@ use crate::{ - newtypes::{DbUrl, LocalUserId, PersonId}, + newtypes::{DbUrl, LanguageId, LocalUserId, PersonId}, schema::{local_user, person, registration_application}, source::{ - actor_language::{LocalUserLanguage, SiteLanguage}, + actor_language::LocalUserLanguage, local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm}, local_user_vote_display_mode::{LocalUserVoteDisplayMode, LocalUserVoteDisplayModeInsertForm}, }, - traits::Crud, utils::{ functions::{coalesce, lower}, get_conn, @@ -25,6 +24,52 @@ use diesel::{ use diesel_async::RunQueryDsl; impl LocalUser { + pub async fn create( + pool: &mut DbPool<'_>, + form: &LocalUserInsertForm, + languages: Vec, + ) -> Result { + let conn = &mut get_conn(pool).await?; + let mut form_with_encrypted_password = form.clone(); + let password_hash = + hash(&form.password_encrypted, DEFAULT_COST).expect("Couldn't hash password"); + form_with_encrypted_password.password_encrypted = password_hash; + + let local_user_ = insert_into(local_user::table) + .values(form_with_encrypted_password) + .get_result::(conn) + .await?; + + LocalUserLanguage::update(pool, languages, local_user_.id).await?; + + // Create their vote_display_modes + let vote_display_mode_form = LocalUserVoteDisplayModeInsertForm::builder() + .local_user_id(local_user_.id) + .build(); + LocalUserVoteDisplayMode::create(pool, &vote_display_mode_form).await?; + + Ok(local_user_) + } + + pub async fn update( + pool: &mut DbPool<'_>, + local_user_id: LocalUserId, + form: &LocalUserUpdateForm, + ) -> Result { + let conn = &mut get_conn(pool).await?; + diesel::update(local_user::table.find(local_user_id)) + .set(form) + .get_result::(conn) + .await + } + + pub async fn delete(pool: &mut DbPool<'_>, id: LocalUserId) -> Result { + let conn = &mut *get_conn(pool).await?; + diesel::delete(local_user::table.find(id)) + .execute(conn) + .await + } + pub async fn update_password( pool: &mut DbPool<'_>, local_user_id: LocalUserId, @@ -183,52 +228,3 @@ pub struct UserBackupLists { pub blocked_users: Vec, pub blocked_instances: Vec, } - -#[async_trait] -impl Crud for LocalUser { - type InsertForm = LocalUserInsertForm; - type UpdateForm = LocalUserUpdateForm; - type IdType = LocalUserId; - - async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result { - let conn = &mut get_conn(pool).await?; - let mut form_with_encrypted_password = form.clone(); - let password_hash = - hash(&form.password_encrypted, DEFAULT_COST).expect("Couldn't hash password"); - form_with_encrypted_password.password_encrypted = password_hash; - - let local_user_ = insert_into(local_user::table) - .values(form_with_encrypted_password) - .get_result::(conn) - .await?; - - let site_languages = SiteLanguage::read_local_raw(pool).await; - if let Ok(langs) = site_languages { - // if site exists, init user with site languages - LocalUserLanguage::update(pool, langs, local_user_.id).await?; - } else { - // otherwise, init with all languages (this only happens during tests and - // for first admin user, which is created before site) - LocalUserLanguage::update(pool, vec![], local_user_.id).await?; - } - - // Create their vote_display_modes - let vote_display_mode_form = LocalUserVoteDisplayModeInsertForm::builder() - .local_user_id(local_user_.id) - .build(); - LocalUserVoteDisplayMode::create(pool, &vote_display_mode_form).await?; - - Ok(local_user_) - } - async fn update( - pool: &mut DbPool<'_>, - local_user_id: LocalUserId, - form: &Self::UpdateForm, - ) -> Result { - let conn = &mut get_conn(pool).await?; - diesel::update(local_user::table.find(local_user_id)) - .set(form) - .get_result::(conn) - .await - } -} diff --git a/crates/db_schema/src/impls/password_reset_request.rs b/crates/db_schema/src/impls/password_reset_request.rs index 6fcf86014..3b910b1d3 100644 --- a/crates/db_schema/src/impls/password_reset_request.rs +++ b/crates/db_schema/src/impls/password_reset_request.rs @@ -121,7 +121,9 @@ mod tests { .password_encrypted("pass".to_string()) .build(); - let inserted_local_user = LocalUser::create(pool, &new_local_user).await.unwrap(); + let inserted_local_user = LocalUser::create(pool, &new_local_user, vec![]) + .await + .unwrap(); let token = "nope"; diff --git a/crates/db_views/src/comment_report_view.rs b/crates/db_views/src/comment_report_view.rs index 1aa3bb6e7..317ebf29a 100644 --- a/crates/db_views/src/comment_report_view.rs +++ b/crates/db_views/src/comment_report_view.rs @@ -308,7 +308,9 @@ mod tests { .person_id(inserted_timmy.id) .password_encrypted("123".to_string()) .build(); - let timmy_local_user = LocalUser::create(pool, &new_local_user).await.unwrap(); + let timmy_local_user = LocalUser::create(pool, &new_local_user, vec![]) + .await + .unwrap(); let timmy_view = LocalUserView { local_user: timmy_local_user, local_user_vote_display_mode: LocalUserVoteDisplayMode::default(), diff --git a/crates/db_views/src/comment_view.rs b/crates/db_views/src/comment_view.rs index 5f811cb0d..d80cc2e58 100644 --- a/crates/db_views/src/comment_view.rs +++ b/crates/db_views/src/comment_view.rs @@ -503,7 +503,7 @@ mod tests { .admin(Some(true)) .password_encrypted(String::new()) .build(); - let inserted_timmy_local_user = LocalUser::create(pool, &timmy_local_user_form) + let inserted_timmy_local_user = LocalUser::create(pool, &timmy_local_user_form, vec![]) .await .unwrap(); diff --git a/crates/db_views/src/post_report_view.rs b/crates/db_views/src/post_report_view.rs index 0f1f3639c..20795c8c0 100644 --- a/crates/db_views/src/post_report_view.rs +++ b/crates/db_views/src/post_report_view.rs @@ -330,7 +330,9 @@ mod tests { .person_id(inserted_timmy.id) .password_encrypted("123".to_string()) .build(); - let timmy_local_user = LocalUser::create(pool, &new_local_user).await.unwrap(); + let timmy_local_user = LocalUser::create(pool, &new_local_user, vec![]) + .await + .unwrap(); let timmy_view = LocalUserView { local_user: timmy_local_user, local_user_vote_display_mode: LocalUserVoteDisplayMode::default(), diff --git a/crates/db_views/src/post_view.rs b/crates/db_views/src/post_view.rs index ae5ec383a..e1891b37a 100644 --- a/crates/db_views/src/post_view.rs +++ b/crates/db_views/src/post_view.rs @@ -829,7 +829,7 @@ mod tests { admin: Some(true), ..LocalUserInsertForm::test_form(inserted_person.id) }; - let inserted_local_user = LocalUser::create(pool, &local_user_form).await?; + let inserted_local_user = LocalUser::create(pool, &local_user_form, vec![]).await?; let new_bot = PersonInsertForm { bot_account: Some(true), @@ -855,6 +855,7 @@ mod tests { let inserted_blocked_local_user = LocalUser::create( pool, &LocalUserInsertForm::test_form(inserted_blocked_person.id), + vec![], ) .await?; diff --git a/crates/db_views/src/registration_application_view.rs b/crates/db_views/src/registration_application_view.rs index a59158318..c2f2e3120 100644 --- a/crates/db_views/src/registration_application_view.rs +++ b/crates/db_views/src/registration_application_view.rs @@ -176,7 +176,7 @@ mod tests { .admin(Some(true)) .build(); - let _inserted_timmy_local_user = LocalUser::create(pool, &timmy_local_user_form) + let _inserted_timmy_local_user = LocalUser::create(pool, &timmy_local_user_form, vec![]) .await .unwrap(); @@ -193,7 +193,7 @@ mod tests { .password_encrypted("nada".to_string()) .build(); - let inserted_sara_local_user = LocalUser::create(pool, &sara_local_user_form) + let inserted_sara_local_user = LocalUser::create(pool, &sara_local_user_form, vec![]) .await .unwrap(); @@ -224,7 +224,7 @@ mod tests { .password_encrypted("nada".to_string()) .build(); - let inserted_jess_local_user = LocalUser::create(pool, &jess_local_user_form) + let inserted_jess_local_user = LocalUser::create(pool, &jess_local_user_form, vec![]) .await .unwrap(); diff --git a/crates/db_views_actor/src/community_view.rs b/crates/db_views_actor/src/community_view.rs index c1cb6eee1..98a146c15 100644 --- a/crates/db_views_actor/src/community_view.rs +++ b/crates/db_views_actor/src/community_view.rs @@ -296,7 +296,9 @@ mod tests { .person_id(inserted_person.id) .password_encrypted(String::new()) .build(); - let local_user = LocalUser::create(pool, &local_user_form).await.unwrap(); + let local_user = LocalUser::create(pool, &local_user_form, vec![]) + .await + .unwrap(); let new_community = CommunityInsertForm::builder() .name("test_community_3".to_string()) diff --git a/crates/db_views_actor/src/person_view.rs b/crates/db_views_actor/src/person_view.rs index aee56748e..1ee51a819 100644 --- a/crates/db_views_actor/src/person_view.rs +++ b/crates/db_views_actor/src/person_view.rs @@ -204,7 +204,7 @@ mod tests { .person_id(alice.id) .password_encrypted(String::new()) .build(); - let alice_local_user = LocalUser::create(pool, &alice_local_user_form).await?; + let alice_local_user = LocalUser::create(pool, &alice_local_user_form, vec![]).await?; let bob_form = PersonInsertForm::builder() .name("bob".to_string()) @@ -218,7 +218,7 @@ mod tests { .person_id(bob.id) .password_encrypted(String::new()) .build(); - let bob_local_user = LocalUser::create(pool, &bob_local_user_form).await?; + let bob_local_user = LocalUser::create(pool, &bob_local_user_form, vec![]).await?; Ok(Data { alice, diff --git a/src/code_migrations.rs b/src/code_migrations.rs index 8e17b8a8c..cee02075c 100644 --- a/src/code_migrations.rs +++ b/src/code_migrations.rs @@ -475,7 +475,7 @@ async fn initialize_local_site_2022_10_10( .email(setup.admin_email.clone()) .admin(Some(true)) .build(); - LocalUser::create(pool, &local_user_form).await?; + LocalUser::create(pool, &local_user_form, vec![]).await?; }; // Add an entry for the site table diff --git a/src/session_middleware.rs b/src/session_middleware.rs index 2c4e36913..474709dbe 100644 --- a/src/session_middleware.rs +++ b/src/session_middleware.rs @@ -155,7 +155,9 @@ mod tests { .password_encrypted("123456".to_string()) .build(); - let inserted_local_user = LocalUser::create(pool, &local_user_form).await.unwrap(); + let inserted_local_user = LocalUser::create(pool, &local_user_form, vec![]) + .await + .unwrap(); let req = TestRequest::default().to_http_request(); let jwt = Claims::generate(inserted_local_user.id, req, &context) From e4356a770176dc856d8ab03330911d48de8cc3b3 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 25 Mar 2024 19:14:35 -0400 Subject: [PATCH 4/8] Fixing a few broken tests from the change in LocalUser::create (#4569) --- crates/db_views/src/comment_view.rs | 1 + crates/db_views/src/post_view.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/crates/db_views/src/comment_view.rs b/crates/db_views/src/comment_view.rs index d80cc2e58..f8e91b501 100644 --- a/crates/db_views/src/comment_view.rs +++ b/crates/db_views/src/comment_view.rs @@ -1183,6 +1183,7 @@ mod tests { let inserted_banned_from_comm_local_user = LocalUser::create( pool, &LocalUserInsertForm::test_form(inserted_banned_from_comm_person.id), + vec![], ) .await?; diff --git a/crates/db_views/src/post_view.rs b/crates/db_views/src/post_view.rs index e1891b37a..b323437d5 100644 --- a/crates/db_views/src/post_view.rs +++ b/crates/db_views/src/post_view.rs @@ -1747,6 +1747,7 @@ mod tests { let inserted_banned_from_comm_local_user = LocalUser::create( pool, &LocalUserInsertForm::test_form(inserted_banned_from_comm_person.id), + vec![], ) .await?; From 95069d7648afd0cc0abd8990d9581c948536072c Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 26 Mar 2024 05:17:42 -0400 Subject: [PATCH 5/8] Fixing some clippy and woodpecker lints. (#4565) * Fixing some clippy and woodpecker lints. * Try fixing woodpecker 1. * Revert "Try fixing woodpecker 1." This reverts commit 7c2020a08d7b72b179b23cf7c56c1d9fcdcb2a6e. --- .woodpecker.yml | 44 ++++++++++--------- crates/api/src/lib.rs | 4 +- crates/api/src/sitemap.rs | 2 +- crates/api_common/src/claims.rs | 4 +- crates/api_common/src/request.rs | 4 +- crates/api_common/src/utils.rs | 4 +- crates/api_crud/src/site/create.rs | 4 +- crates/api_crud/src/site/mod.rs | 4 +- crates/api_crud/src/site/update.rs | 4 +- crates/apub/src/activity_lists.rs | 2 +- crates/apub/src/api/user_settings_backup.rs | 2 +- .../src/collections/community_moderators.rs | 2 +- crates/apub/src/http/community.rs | 4 +- .../src/aggregates/comment_aggregates.rs | 4 +- .../src/aggregates/community_aggregates.rs | 4 +- .../src/aggregates/person_aggregates.rs | 4 +- .../src/aggregates/post_aggregates.rs | 4 +- .../src/aggregates/site_aggregates.rs | 4 +- crates/db_schema/src/impls/activity.rs | 4 +- crates/db_schema/src/impls/actor_language.rs | 4 +- crates/db_schema/src/impls/captcha_answer.rs | 4 +- crates/db_schema/src/impls/comment.rs | 4 +- crates/db_schema/src/impls/comment_reply.rs | 4 +- crates/db_schema/src/impls/community.rs | 4 +- .../src/impls/federation_allowlist.rs | 4 +- crates/db_schema/src/impls/language.rs | 4 +- crates/db_schema/src/impls/moderator.rs | 4 +- .../src/impls/password_reset_request.rs | 4 +- crates/db_schema/src/impls/person.rs | 4 +- crates/db_schema/src/impls/person_mention.rs | 4 +- crates/db_schema/src/impls/post.rs | 4 +- crates/db_schema/src/impls/post_report.rs | 4 +- crates/db_schema/src/impls/private_message.rs | 4 +- crates/db_schema/src/utils.rs | 4 +- crates/db_views/src/comment_report_view.rs | 4 +- crates/db_views/src/comment_view.rs | 4 +- crates/db_views/src/post_report_view.rs | 4 +- .../src/private_message_report_view.rs | 4 +- crates/db_views/src/private_message_view.rs | 4 +- .../src/registration_application_view.rs | 4 +- crates/db_views/src/vote_view.rs | 4 +- crates/db_views_actor/src/community_view.rs | 4 +- crates/db_views_actor/src/person_view.rs | 4 +- crates/utils/src/rate_limit/mod.rs | 4 +- crates/utils/src/rate_limit/rate_limiter.rs | 4 +- crates/utils/src/utils/markdown/mod.rs | 4 +- .../utils/src/utils/markdown/spoiler_rule.rs | 4 +- crates/utils/src/utils/mention.rs | 4 +- crates/utils/src/utils/slurs.rs | 4 +- crates/utils/src/utils/validation.rs | 4 +- src/scheduled_tasks.rs | 4 +- src/session_middleware.rs | 4 +- 52 files changed, 121 insertions(+), 119 deletions(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index a1f008bd0..ab38db329 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -6,21 +6,23 @@ variables: - &install_pnpm "corepack enable pnpm" - &slow_check_paths - path: - # rust source code - - "crates/**" - - "src/**" - - "**/Cargo.toml" - - "Cargo.lock" - # database migrations - - "migrations/**" - # typescript tests - - "api_tests/**" - # config files and scripts used by ci - - ".woodpecker.yml" - - ".rustfmt.toml" - - "scripts/update_config_defaults.sh" - - "diesel.toml" - - ".gitmodules" + include: [ + # rust source code + "crates/**", + "src/**", + "**/Cargo.toml", + "Cargo.lock", + # database migrations + "migrations/**", + # typescript tests + "api_tests/**", + # config files and scripts used by ci + ".woodpecker.yml", + ".rustfmt.toml", + "scripts/update_config_defaults.sh", + "diesel.toml", + ".gitmodules", + ] # Broken for cron jobs currently, see # https://github.com/woodpecker-ci/woodpecker/issues/1716 @@ -198,7 +200,7 @@ steps: - cat target/log/lemmy_*.out || true - "# If you can't see all output, then use the download button" when: - status: [failure] + - status: [failure] publish_release_docker: image: woodpeckerci/plugin-docker-buildx @@ -211,7 +213,7 @@ steps: - RUST_RELEASE_MODE=release tag: ${CI_COMMIT_TAG} when: - event: tag + - event: tag nightly_build: image: woodpeckerci/plugin-docker-buildx @@ -224,7 +226,7 @@ steps: - RUST_RELEASE_MODE=release tag: dev when: - event: cron + - event: cron # using https://github.com/pksunkara/cargo-workspaces publish_to_crates_io: @@ -237,7 +239,7 @@ steps: - cargo workspaces publish --from-git --allow-dirty --no-verify --allow-branch "${CI_COMMIT_TAG}" --yes custom "${CI_COMMIT_TAG}" secrets: [cargo_api_token] when: - event: tag + - event: tag notify_on_failure: image: alpine:3 @@ -245,7 +247,7 @@ steps: - apk add curl - "curl -d'Lemmy CI build failed: ${CI_PIPELINE_URL}' ntfy.sh/lemmy_drone_ci" when: - status: [failure] + - status: [failure] notify_on_tag_deploy: image: alpine:3 @@ -253,7 +255,7 @@ steps: - apk add curl - "curl -d'lemmy:${CI_COMMIT_TAG} deployed' ntfy.sh/lemmy_drone_ci" when: - event: tag + - event: tag services: database: diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs index 814dd67eb..d65ae0a28 100644 --- a/crates/api/src/lib.rs +++ b/crates/api/src/lib.rs @@ -259,9 +259,9 @@ pub async fn local_user_view_from_jwt( } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use super::*; diff --git a/crates/api/src/sitemap.rs b/crates/api/src/sitemap.rs index ec32f837f..bd0e0dad8 100644 --- a/crates/api/src/sitemap.rs +++ b/crates/api/src/sitemap.rs @@ -42,8 +42,8 @@ pub async fn get_sitemap(context: Data) -> LemmyResult Resu } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::{ context::LemmyContext, diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs index b13d21f9f..e4ab91b08 100644 --- a/crates/api_common/src/utils.rs +++ b/crates/api_common/src/utils.rs @@ -1019,9 +1019,9 @@ pub async fn proxy_image_link_opt_apub( } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use super::*; use pretty_assertions::assert_eq; diff --git a/crates/api_crud/src/site/create.rs b/crates/api_crud/src/site/create.rs index 76aae405e..8542117e7 100644 --- a/crates/api_crud/src/site/create.rs +++ b/crates/api_crud/src/site/create.rs @@ -189,9 +189,9 @@ fn validate_create_payload(local_site: &LocalSite, create_site: &CreateSite) -> } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::site::create::validate_create_payload; use lemmy_api_common::site::CreateSite; diff --git a/crates/api_crud/src/site/mod.rs b/crates/api_crud/src/site/mod.rs index e4911ba48..0bf7cc279 100644 --- a/crates/api_crud/src/site/mod.rs +++ b/crates/api_crud/src/site/mod.rs @@ -41,9 +41,9 @@ pub fn application_question_check( } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::site::{application_question_check, site_default_post_listing_type_check}; use lemmy_db_schema::{ListingType, RegistrationMode}; diff --git a/crates/api_crud/src/site/update.rs b/crates/api_crud/src/site/update.rs index 809dbe498..6d419a6d8 100644 --- a/crates/api_crud/src/site/update.rs +++ b/crates/api_crud/src/site/update.rs @@ -231,9 +231,9 @@ fn validate_update_payload(local_site: &LocalSite, edit_site: &EditSite) -> Lemm } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::site::update::validate_update_payload; use lemmy_api_common::site::EditSite; diff --git a/crates/apub/src/activity_lists.rs b/crates/apub/src/activity_lists.rs index 206f0088b..7b33499c8 100644 --- a/crates/apub/src/activity_lists.rs +++ b/crates/apub/src/activity_lists.rs @@ -123,8 +123,8 @@ impl InCommunity for AnnouncableActivities { } #[cfg(test)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::indexing_slicing)] use crate::{ activity_lists::{GroupInboxActivities, PersonInboxActivities, SharedInboxActivities}, diff --git a/crates/apub/src/api/user_settings_backup.rs b/crates/apub/src/api/user_settings_backup.rs index d46e415b0..4c1edcbff 100644 --- a/crates/apub/src/api/user_settings_backup.rs +++ b/crates/apub/src/api/user_settings_backup.rs @@ -319,8 +319,8 @@ pub async fn import_settings( } #[cfg(test)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::indexing_slicing)] use crate::api::user_settings_backup::{export_settings, import_settings}; use activitypub_federation::config::Data; diff --git a/crates/apub/src/collections/community_moderators.rs b/crates/apub/src/collections/community_moderators.rs index 87d88d071..0532d0aef 100644 --- a/crates/apub/src/collections/community_moderators.rs +++ b/crates/apub/src/collections/community_moderators.rs @@ -101,8 +101,8 @@ impl Collection for ApubCommunityModerators { } #[cfg(test)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::indexing_slicing)] use super::*; use crate::{ diff --git a/crates/apub/src/http/community.rs b/crates/apub/src/http/community.rs index 83ccdacf9..cf0d1625d 100644 --- a/crates/apub/src/http/community.rs +++ b/crates/apub/src/http/community.rs @@ -115,9 +115,9 @@ pub(crate) async fn get_apub_community_featured( } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] pub(crate) mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use super::*; use crate::protocol::objects::{group::Group, tombstone::Tombstone}; diff --git a/crates/db_schema/src/aggregates/comment_aggregates.rs b/crates/db_schema/src/aggregates/comment_aggregates.rs index 78ceaa0c6..2120c7f38 100644 --- a/crates/db_schema/src/aggregates/comment_aggregates.rs +++ b/crates/db_schema/src/aggregates/comment_aggregates.rs @@ -33,9 +33,9 @@ impl CommentAggregates { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::{ aggregates::comment_aggregates::CommentAggregates, diff --git a/crates/db_schema/src/aggregates/community_aggregates.rs b/crates/db_schema/src/aggregates/community_aggregates.rs index 334688b97..f1f54663b 100644 --- a/crates/db_schema/src/aggregates/community_aggregates.rs +++ b/crates/db_schema/src/aggregates/community_aggregates.rs @@ -31,9 +31,9 @@ impl CommunityAggregates { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::{ aggregates::community_aggregates::CommunityAggregates, diff --git a/crates/db_schema/src/aggregates/person_aggregates.rs b/crates/db_schema/src/aggregates/person_aggregates.rs index b48b37641..520b45225 100644 --- a/crates/db_schema/src/aggregates/person_aggregates.rs +++ b/crates/db_schema/src/aggregates/person_aggregates.rs @@ -18,9 +18,9 @@ impl PersonAggregates { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::{ aggregates::person_aggregates::PersonAggregates, diff --git a/crates/db_schema/src/aggregates/post_aggregates.rs b/crates/db_schema/src/aggregates/post_aggregates.rs index 9415f8971..7f95dc05a 100644 --- a/crates/db_schema/src/aggregates/post_aggregates.rs +++ b/crates/db_schema/src/aggregates/post_aggregates.rs @@ -52,9 +52,9 @@ impl PostAggregates { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::{ aggregates::post_aggregates::PostAggregates, diff --git a/crates/db_schema/src/aggregates/site_aggregates.rs b/crates/db_schema/src/aggregates/site_aggregates.rs index 453430841..b179ff7c7 100644 --- a/crates/db_schema/src/aggregates/site_aggregates.rs +++ b/crates/db_schema/src/aggregates/site_aggregates.rs @@ -14,9 +14,9 @@ impl SiteAggregates { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::{ aggregates::site_aggregates::SiteAggregates, diff --git a/crates/db_schema/src/impls/activity.rs b/crates/db_schema/src/impls/activity.rs index bb17c83e7..e1802c7c5 100644 --- a/crates/db_schema/src/impls/activity.rs +++ b/crates/db_schema/src/impls/activity.rs @@ -61,9 +61,9 @@ impl ReceivedActivity { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use super::*; use crate::{source::activity::ActorType, utils::build_db_pool_for_tests}; diff --git a/crates/db_schema/src/impls/actor_language.rs b/crates/db_schema/src/impls/actor_language.rs index 29d99b2d8..0191dd872 100644 --- a/crates/db_schema/src/impls/actor_language.rs +++ b/crates/db_schema/src/impls/actor_language.rs @@ -385,9 +385,9 @@ async fn convert_read_languages( } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use super::*; use crate::{ diff --git a/crates/db_schema/src/impls/captcha_answer.rs b/crates/db_schema/src/impls/captcha_answer.rs index 72e2d1285..1d0604c0a 100644 --- a/crates/db_schema/src/impls/captcha_answer.rs +++ b/crates/db_schema/src/impls/captcha_answer.rs @@ -48,9 +48,9 @@ impl CaptchaAnswer { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::{ source::captcha_answer::{CaptchaAnswer, CaptchaAnswerForm, CheckCaptchaAnswer}, diff --git a/crates/db_schema/src/impls/comment.rs b/crates/db_schema/src/impls/comment.rs index 979c3b721..b27d44591 100644 --- a/crates/db_schema/src/impls/comment.rs +++ b/crates/db_schema/src/impls/comment.rs @@ -241,9 +241,9 @@ impl Saveable for CommentSaved { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::{ newtypes::LanguageId, diff --git a/crates/db_schema/src/impls/comment_reply.rs b/crates/db_schema/src/impls/comment_reply.rs index d76b46b8a..40c99e045 100644 --- a/crates/db_schema/src/impls/comment_reply.rs +++ b/crates/db_schema/src/impls/comment_reply.rs @@ -73,9 +73,9 @@ impl CommentReply { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::{ source::{ diff --git a/crates/db_schema/src/impls/community.rs b/crates/db_schema/src/impls/community.rs index 88b0ef46d..dbacbcb72 100644 --- a/crates/db_schema/src/impls/community.rs +++ b/crates/db_schema/src/impls/community.rs @@ -381,9 +381,9 @@ impl ApubActor for Community { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::{ source::{ diff --git a/crates/db_schema/src/impls/federation_allowlist.rs b/crates/db_schema/src/impls/federation_allowlist.rs index 0664a8a4f..80408a526 100644 --- a/crates/db_schema/src/impls/federation_allowlist.rs +++ b/crates/db_schema/src/impls/federation_allowlist.rs @@ -48,9 +48,9 @@ impl FederationAllowList { } } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::{ source::{federation_allowlist::FederationAllowList, instance::Instance}, diff --git a/crates/db_schema/src/impls/language.rs b/crates/db_schema/src/impls/language.rs index 66fc05729..905221402 100644 --- a/crates/db_schema/src/impls/language.rs +++ b/crates/db_schema/src/impls/language.rs @@ -41,9 +41,9 @@ impl Language { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::{source::language::Language, utils::build_db_pool_for_tests}; use pretty_assertions::assert_eq; diff --git a/crates/db_schema/src/impls/moderator.rs b/crates/db_schema/src/impls/moderator.rs index cfb64d883..99f117878 100644 --- a/crates/db_schema/src/impls/moderator.rs +++ b/crates/db_schema/src/impls/moderator.rs @@ -465,9 +465,9 @@ impl Crud for AdminPurgeComment { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::{ source::{ diff --git a/crates/db_schema/src/impls/password_reset_request.rs b/crates/db_schema/src/impls/password_reset_request.rs index 3b910b1d3..491097135 100644 --- a/crates/db_schema/src/impls/password_reset_request.rs +++ b/crates/db_schema/src/impls/password_reset_request.rs @@ -81,9 +81,9 @@ impl PasswordResetRequest { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::{ source::{ diff --git a/crates/db_schema/src/impls/person.rs b/crates/db_schema/src/impls/person.rs index 0de7c32f5..c90f67891 100644 --- a/crates/db_schema/src/impls/person.rs +++ b/crates/db_schema/src/impls/person.rs @@ -212,9 +212,9 @@ impl PersonFollower { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::{ source::{ diff --git a/crates/db_schema/src/impls/person_mention.rs b/crates/db_schema/src/impls/person_mention.rs index 75950e7e9..9882e662c 100644 --- a/crates/db_schema/src/impls/person_mention.rs +++ b/crates/db_schema/src/impls/person_mention.rs @@ -74,9 +74,9 @@ impl PersonMention { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::{ source::{ diff --git a/crates/db_schema/src/impls/post.rs b/crates/db_schema/src/impls/post.rs index 3425a795d..8db871ad5 100644 --- a/crates/db_schema/src/impls/post.rs +++ b/crates/db_schema/src/impls/post.rs @@ -362,9 +362,9 @@ impl PostHide { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::{ source::{ diff --git a/crates/db_schema/src/impls/post_report.rs b/crates/db_schema/src/impls/post_report.rs index face766db..260574bd2 100644 --- a/crates/db_schema/src/impls/post_report.rs +++ b/crates/db_schema/src/impls/post_report.rs @@ -80,9 +80,9 @@ impl Reportable for PostReport { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use super::*; use crate::{ diff --git a/crates/db_schema/src/impls/private_message.rs b/crates/db_schema/src/impls/private_message.rs index 961e36518..ee0009189 100644 --- a/crates/db_schema/src/impls/private_message.rs +++ b/crates/db_schema/src/impls/private_message.rs @@ -74,9 +74,9 @@ impl PrivateMessage { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::{ source::{ diff --git a/crates/db_schema/src/utils.rs b/crates/db_schema/src/utils.rs index 9a89902e7..465ecb4cd 100644 --- a/crates/db_schema/src/utils.rs +++ b/crates/db_schema/src/utils.rs @@ -536,9 +536,9 @@ impl Queries { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use super::*; use pretty_assertions::assert_eq; diff --git a/crates/db_views/src/comment_report_view.rs b/crates/db_views/src/comment_report_view.rs index 317ebf29a..0da5b3cff 100644 --- a/crates/db_views/src/comment_report_view.rs +++ b/crates/db_views/src/comment_report_view.rs @@ -258,9 +258,9 @@ impl CommentReportQuery { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::{ comment_report_view::{CommentReportQuery, CommentReportView}, diff --git a/crates/db_views/src/comment_view.rs b/crates/db_views/src/comment_view.rs index f8e91b501..01ff394eb 100644 --- a/crates/db_views/src/comment_view.rs +++ b/crates/db_views/src/comment_view.rs @@ -426,9 +426,9 @@ impl<'a> CommentQuery<'a> { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::{ comment_view::{CommentQuery, CommentSortType, CommentView, DbPool}, diff --git a/crates/db_views/src/post_report_view.rs b/crates/db_views/src/post_report_view.rs index 20795c8c0..878937d01 100644 --- a/crates/db_views/src/post_report_view.rs +++ b/crates/db_views/src/post_report_view.rs @@ -283,9 +283,9 @@ impl PostReportQuery { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::{ post_report_view::{PostReportQuery, PostReportView}, diff --git a/crates/db_views/src/private_message_report_view.rs b/crates/db_views/src/private_message_report_view.rs index 2c6919542..091071b16 100644 --- a/crates/db_views/src/private_message_report_view.rs +++ b/crates/db_views/src/private_message_report_view.rs @@ -106,9 +106,9 @@ impl PrivateMessageReportQuery { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::private_message_report_view::PrivateMessageReportQuery; use lemmy_db_schema::{ diff --git a/crates/db_views/src/private_message_view.rs b/crates/db_views/src/private_message_view.rs index a3e2469c9..466f3bdf0 100644 --- a/crates/db_views/src/private_message_view.rs +++ b/crates/db_views/src/private_message_view.rs @@ -173,9 +173,9 @@ impl PrivateMessageQuery { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::{private_message_view::PrivateMessageQuery, structs::PrivateMessageView}; use lemmy_db_schema::{ diff --git a/crates/db_views/src/registration_application_view.rs b/crates/db_views/src/registration_application_view.rs index c2f2e3120..85916bb90 100644 --- a/crates/db_views/src/registration_application_view.rs +++ b/crates/db_views/src/registration_application_view.rs @@ -127,9 +127,9 @@ impl RegistrationApplicationQuery { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::registration_application_view::{ RegistrationApplicationQuery, diff --git a/crates/db_views/src/vote_view.rs b/crates/db_views/src/vote_view.rs index 723c188f5..26df3bdbb 100644 --- a/crates/db_views/src/vote_view.rs +++ b/crates/db_views/src/vote_view.rs @@ -50,9 +50,9 @@ impl VoteView { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::structs::VoteView; use lemmy_db_schema::{ diff --git a/crates/db_views_actor/src/community_view.rs b/crates/db_views_actor/src/community_view.rs index 98a146c15..3107478dc 100644 --- a/crates/db_views_actor/src/community_view.rs +++ b/crates/db_views_actor/src/community_view.rs @@ -250,9 +250,9 @@ impl<'a> CommunityQuery<'a> { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::{community_view::CommunityQuery, structs::CommunityView}; use lemmy_db_schema::{ diff --git a/crates/db_views_actor/src/person_view.rs b/crates/db_views_actor/src/person_view.rs index 1ee51a819..b3eac296d 100644 --- a/crates/db_views_actor/src/person_view.rs +++ b/crates/db_views_actor/src/person_view.rs @@ -163,9 +163,9 @@ impl PersonQuery { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use super::*; use diesel::NotFound; diff --git a/crates/utils/src/rate_limit/mod.rs b/crates/utils/src/rate_limit/mod.rs index b2efbe2c9..de64bac46 100644 --- a/crates/utils/src/rate_limit/mod.rs +++ b/crates/utils/src/rate_limit/mod.rs @@ -221,9 +221,9 @@ fn parse_ip(addr: &str) -> Option { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] #[test] fn test_parse_ip() { diff --git a/crates/utils/src/rate_limit/rate_limiter.rs b/crates/utils/src/rate_limit/rate_limiter.rs index 66d830c7e..3f89aee7e 100644 --- a/crates/utils/src/rate_limit/rate_limiter.rs +++ b/crates/utils/src/rate_limit/rate_limiter.rs @@ -306,9 +306,9 @@ fn split_ipv6(ip: Ipv6Addr) -> ([u8; 6], u8, u8) { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use super::{ActionType, BucketConfig, InstantSecs, RateLimitState, RateLimitedGroup}; use pretty_assertions::assert_eq; diff --git a/crates/utils/src/utils/markdown/mod.rs b/crates/utils/src/utils/markdown/mod.rs index c3def13a7..87e2cb3a2 100644 --- a/crates/utils/src/utils/markdown/mod.rs +++ b/crates/utils/src/utils/markdown/mod.rs @@ -107,9 +107,9 @@ pub fn markdown_check_for_blocked_urls(text: &str, blocklist: &RegexSet) -> Lemm } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use super::*; use pretty_assertions::assert_eq; diff --git a/crates/utils/src/utils/markdown/spoiler_rule.rs b/crates/utils/src/utils/markdown/spoiler_rule.rs index e41ea436f..3f12807fd 100644 --- a/crates/utils/src/utils/markdown/spoiler_rule.rs +++ b/crates/utils/src/utils/markdown/spoiler_rule.rs @@ -134,9 +134,9 @@ pub fn add(markdown_parser: &mut MarkdownIt) { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::utils::markdown::spoiler_rule::add; use markdown_it::MarkdownIt; diff --git a/crates/utils/src/utils/mention.rs b/crates/utils/src/utils/mention.rs index 9e9ee64a2..7e5e5f27c 100644 --- a/crates/utils/src/utils/mention.rs +++ b/crates/utils/src/utils/mention.rs @@ -34,9 +34,9 @@ pub fn scrape_text_for_mentions(text: &str) -> Vec { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod test { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::utils::mention::scrape_text_for_mentions; use pretty_assertions::assert_eq; diff --git a/crates/utils/src/utils/slurs.rs b/crates/utils/src/utils/slurs.rs index fba43d706..e379ae439 100644 --- a/crates/utils/src/utils/slurs.rs +++ b/crates/utils/src/utils/slurs.rs @@ -64,9 +64,9 @@ pub(crate) fn slurs_vec_to_str(slurs: &[&str]) -> String { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod test { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::utils::slurs::{remove_slurs, slur_check, slurs_vec_to_str}; use pretty_assertions::assert_eq; diff --git a/crates/utils/src/utils/validation.rs b/crates/utils/src/utils/validation.rs index f9b2a87e5..93d581327 100644 --- a/crates/utils/src/utils/validation.rs +++ b/crates/utils/src/utils/validation.rs @@ -327,9 +327,9 @@ pub fn check_urls_are_valid(urls: &Vec) -> LemmyResult> { } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use crate::{ error::LemmyErrorType, diff --git a/src/scheduled_tasks.rs b/src/scheduled_tasks.rs index 38eb4ece2..491902fb3 100644 --- a/src/scheduled_tasks.rs +++ b/src/scheduled_tasks.rs @@ -517,9 +517,9 @@ async fn update_instance_software( } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use lemmy_routes::nodeinfo::NodeInfo; use pretty_assertions::assert_eq; diff --git a/src/session_middleware.rs b/src/session_middleware.rs index 474709dbe..2bee64ca0 100644 --- a/src/session_middleware.rs +++ b/src/session_middleware.rs @@ -98,9 +98,9 @@ where } #[cfg(test)] +#[allow(clippy::unwrap_used)] +#[allow(clippy::indexing_slicing)] mod tests { - #![allow(clippy::unwrap_used)] - #![allow(clippy::indexing_slicing)] use super::*; use actix_web::test::TestRequest; From 7929e7760268ef2fa247f56489313cb0980fb7b6 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 26 Mar 2024 10:46:37 -0400 Subject: [PATCH 6/8] Fixing issue with comment replies wrongly marked as read. (#4567) * Fixing issue with comment replies wrongly marked as read. - Fixes #4566 * Elaborating on a comment. --- crates/api_crud/src/comment/create.rs | 10 ++++-- crates/db_schema/src/impls/comment_reply.rs | 33 ++++++++++++++------ crates/db_schema/src/impls/person_mention.rs | 22 ++++++------- 3 files changed, 41 insertions(+), 24 deletions(-) diff --git a/crates/api_crud/src/comment/create.rs b/crates/api_crud/src/comment/create.rs index 9269ec382..acb386c60 100644 --- a/crates/api_crud/src/comment/create.rs +++ b/crates/api_crud/src/comment/create.rs @@ -164,10 +164,15 @@ pub async fn create_comment( ) .await?; - // If its a reply, mark the parent as read + // If we're responding to a comment where we're the recipient, + // (ie we're the grandparent, or the recipient of the parent comment_reply), + // then mark the parent as read. + // Then we don't have to do it manually after we respond to a comment. if let Some(parent) = parent_opt { + let person_id = local_user_view.person.id; let parent_id = parent.id; - let comment_reply = CommentReply::read_by_comment(&mut context.pool(), parent_id).await; + let comment_reply = + CommentReply::read_by_comment_and_person(&mut context.pool(), parent_id, person_id).await; if let Ok(reply) = comment_reply { CommentReply::update( &mut context.pool(), @@ -179,7 +184,6 @@ pub async fn create_comment( } // If the parent has PersonMentions mark them as read too - let person_id = local_user_view.person.id; let person_mention = PersonMention::read_by_comment_and_person(&mut context.pool(), parent_id, person_id).await; if let Ok(mention) = person_mention { diff --git a/crates/db_schema/src/impls/comment_reply.rs b/crates/db_schema/src/impls/comment_reply.rs index 40c99e045..e6d0915e9 100644 --- a/crates/db_schema/src/impls/comment_reply.rs +++ b/crates/db_schema/src/impls/comment_reply.rs @@ -1,6 +1,6 @@ use crate::{ newtypes::{CommentId, CommentReplyId, PersonId}, - schema::comment_reply::dsl::{comment_id, comment_reply, read, recipient_id}, + schema::comment_reply, source::comment_reply::{CommentReply, CommentReplyInsertForm, CommentReplyUpdateForm}, traits::Crud, utils::{get_conn, DbPool}, @@ -22,9 +22,9 @@ impl Crud for CommentReply { // since the return here isnt utilized, we dont need to do an update // but get_result doesnt return the existing row here - insert_into(comment_reply) + insert_into(comment_reply::table) .values(comment_reply_form) - .on_conflict((recipient_id, comment_id)) + .on_conflict((comment_reply::recipient_id, comment_reply::comment_id)) .do_update() .set(comment_reply_form) .get_result::(conn) @@ -37,7 +37,7 @@ impl Crud for CommentReply { comment_reply_form: &Self::UpdateForm, ) -> Result { let conn = &mut get_conn(pool).await?; - diesel::update(comment_reply.find(comment_reply_id)) + diesel::update(comment_reply::table.find(comment_reply_id)) .set(comment_reply_form) .get_result::(conn) .await @@ -51,11 +51,11 @@ impl CommentReply { ) -> Result, Error> { let conn = &mut get_conn(pool).await?; diesel::update( - comment_reply - .filter(recipient_id.eq(for_recipient_id)) - .filter(read.eq(false)), + comment_reply::table + .filter(comment_reply::recipient_id.eq(for_recipient_id)) + .filter(comment_reply::read.eq(false)), ) - .set(read.eq(true)) + .set(comment_reply::read.eq(true)) .get_results::(conn) .await } @@ -65,8 +65,21 @@ impl CommentReply { for_comment_id: CommentId, ) -> Result { let conn = &mut get_conn(pool).await?; - comment_reply - .filter(comment_id.eq(for_comment_id)) + comment_reply::table + .filter(comment_reply::comment_id.eq(for_comment_id)) + .first::(conn) + .await + } + + pub async fn read_by_comment_and_person( + pool: &mut DbPool<'_>, + for_comment_id: CommentId, + for_recipient_id: PersonId, + ) -> Result { + let conn = &mut get_conn(pool).await?; + comment_reply::table + .filter(comment_reply::comment_id.eq(for_comment_id)) + .filter(comment_reply::recipient_id.eq(for_recipient_id)) .first::(conn) .await } diff --git a/crates/db_schema/src/impls/person_mention.rs b/crates/db_schema/src/impls/person_mention.rs index 9882e662c..5524f87ee 100644 --- a/crates/db_schema/src/impls/person_mention.rs +++ b/crates/db_schema/src/impls/person_mention.rs @@ -1,6 +1,6 @@ use crate::{ newtypes::{CommentId, PersonId, PersonMentionId}, - schema::person_mention::dsl::{comment_id, person_mention, read, recipient_id}, + schema::person_mention, source::person_mention::{PersonMention, PersonMentionInsertForm, PersonMentionUpdateForm}, traits::Crud, utils::{get_conn, DbPool}, @@ -21,9 +21,9 @@ impl Crud for PersonMention { let conn = &mut get_conn(pool).await?; // since the return here isnt utilized, we dont need to do an update // but get_result doesnt return the existing row here - insert_into(person_mention) + insert_into(person_mention::table) .values(person_mention_form) - .on_conflict((recipient_id, comment_id)) + .on_conflict((person_mention::recipient_id, person_mention::comment_id)) .do_update() .set(person_mention_form) .get_result::(conn) @@ -36,7 +36,7 @@ impl Crud for PersonMention { person_mention_form: &Self::UpdateForm, ) -> Result { let conn = &mut get_conn(pool).await?; - diesel::update(person_mention.find(person_mention_id)) + diesel::update(person_mention::table.find(person_mention_id)) .set(person_mention_form) .get_result::(conn) .await @@ -50,11 +50,11 @@ impl PersonMention { ) -> Result, Error> { let conn = &mut get_conn(pool).await?; diesel::update( - person_mention - .filter(recipient_id.eq(for_recipient_id)) - .filter(read.eq(false)), + person_mention::table + .filter(person_mention::recipient_id.eq(for_recipient_id)) + .filter(person_mention::read.eq(false)), ) - .set(read.eq(true)) + .set(person_mention::read.eq(true)) .get_results::(conn) .await } @@ -65,9 +65,9 @@ impl PersonMention { for_recipient_id: PersonId, ) -> Result { let conn = &mut get_conn(pool).await?; - person_mention - .filter(comment_id.eq(for_comment_id)) - .filter(recipient_id.eq(for_recipient_id)) + person_mention::table + .filter(person_mention::comment_id.eq(for_comment_id)) + .filter(person_mention::recipient_id.eq(for_recipient_id)) .first::(conn) .await } From 945064726f9e3b4f8451ae319ee7154a27b65cc3 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 26 Mar 2024 11:22:04 -0400 Subject: [PATCH 7/8] Add creator_banned_from_community to vote_view. (#4568) * Add creator_banned_from_community to vote_view. - Fixes #4561 * Adding tests. --- crates/db_views/src/structs.rs | 1 + crates/db_views/src/vote_view.rs | 84 +++++++++++++++++++++++++++++--- 2 files changed, 79 insertions(+), 6 deletions(-) diff --git a/crates/db_views/src/structs.rs b/crates/db_views/src/structs.rs index cfaee591d..a290ca4a1 100644 --- a/crates/db_views/src/structs.rs +++ b/crates/db_views/src/structs.rs @@ -211,5 +211,6 @@ pub struct CustomEmojiView { /// A vote view for checking a post or comments votes. pub struct VoteView { pub creator: Person, + pub creator_banned_from_community: bool, pub score: i16, } diff --git a/crates/db_views/src/vote_view.rs b/crates/db_views/src/vote_view.rs index 26df3bdbb..a0441ff4e 100644 --- a/crates/db_views/src/vote_view.rs +++ b/crates/db_views/src/vote_view.rs @@ -1,9 +1,16 @@ use crate::structs::VoteView; -use diesel::{result::Error, ExpressionMethods, QueryDsl}; +use diesel::{ + result::Error, + BoolExpressionMethods, + ExpressionMethods, + JoinOnDsl, + NullableExpressionMethods, + QueryDsl, +}; use diesel_async::RunQueryDsl; use lemmy_db_schema::{ newtypes::{CommentId, PostId}, - schema::{comment_like, person, post_like}, + schema::{comment_like, community_person_ban, person, post, post_like}, utils::{get_conn, limit_and_offset, DbPool}, }; @@ -19,8 +26,21 @@ impl VoteView { post_like::table .inner_join(person::table) + .inner_join(post::table) + // Join to community_person_ban to get creator_banned_from_community + .left_join( + community_person_ban::table.on( + post::community_id + .eq(community_person_ban::community_id) + .and(community_person_ban::person_id.eq(post_like::person_id)), + ), + ) .filter(post_like::post_id.eq(post_id)) - .select((person::all_columns, post_like::score)) + .select(( + person::all_columns, + community_person_ban::community_id.nullable().is_not_null(), + post_like::score, + )) .order_by(post_like::score) .limit(limit) .offset(offset) @@ -39,8 +59,21 @@ impl VoteView { comment_like::table .inner_join(person::table) + .inner_join(post::table) + // Join to community_person_ban to get creator_banned_from_community + .left_join( + community_person_ban::table.on( + post::community_id + .eq(community_person_ban::community_id) + .and(community_person_ban::person_id.eq(comment_like::person_id)), + ), + ) .filter(comment_like::comment_id.eq(comment_id)) - .select((person::all_columns, comment_like::score)) + .select(( + person::all_columns, + community_person_ban::community_id.nullable().is_not_null(), + comment_like::score, + )) .order_by(comment_like::score) .limit(limit) .offset(offset) @@ -58,12 +91,12 @@ mod tests { use lemmy_db_schema::{ source::{ comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm}, - community::{Community, CommunityInsertForm}, + community::{Community, CommunityInsertForm, CommunityPersonBan, CommunityPersonBanForm}, instance::Instance, person::{Person, PersonInsertForm}, post::{Post, PostInsertForm, PostLike, PostLikeForm}, }, - traits::{Crud, Likeable}, + traits::{Bannable, Crud, Likeable}, utils::build_db_pool_for_tests, }; use pretty_assertions::assert_eq; @@ -139,10 +172,12 @@ mod tests { let expected_post_vote_views = [ VoteView { creator: inserted_sara.clone(), + creator_banned_from_community: false, score: -1, }, VoteView { creator: inserted_timmy.clone(), + creator_banned_from_community: false, score: 1, }, ]; @@ -177,10 +212,12 @@ mod tests { let expected_comment_vote_views = [ VoteView { creator: inserted_timmy.clone(), + creator_banned_from_community: false, score: -1, }, VoteView { creator: inserted_sara.clone(), + creator_banned_from_community: false, score: 1, }, ]; @@ -190,6 +227,41 @@ mod tests { .unwrap(); assert_eq!(read_comment_vote_views, expected_comment_vote_views); + // Ban timmy from that community + let ban_timmy_form = CommunityPersonBanForm { + community_id: inserted_community.id, + person_id: inserted_timmy.id, + expires: None, + }; + CommunityPersonBan::ban(pool, &ban_timmy_form) + .await + .unwrap(); + + // Make sure creator_banned_from_community is true + let read_comment_vote_views_after_ban = + VoteView::list_for_comment(pool, inserted_comment.id, None, None) + .await + .unwrap(); + + assert!( + read_comment_vote_views_after_ban + .first() + .unwrap() + .creator_banned_from_community + ); + + let read_post_vote_views_after_ban = + VoteView::list_for_post(pool, inserted_post.id, None, None) + .await + .unwrap(); + + assert!( + read_post_vote_views_after_ban + .get(1) + .unwrap() + .creator_banned_from_community + ); + // Cleanup Instance::delete(pool, inserted_instance.id).await.unwrap(); } From 6bfbb9332dc48fbf09e27e21308d17b092a9ebdd Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 26 Mar 2024 12:06:11 -0400 Subject: [PATCH 8/8] Adding listMedia endpoint, to view all your local image uploads. (#4509) * Adding listMedia endpoint, to view all your local image uploads. - Fixes #4445 * Fix ts import. * Forgot to order by published desc * Adding an endpoint to list all images, for admins only. * Forgot to add file. * Add additional test. * Use better logic for no-limit version. * Better call sites. * Adding another test. * Fix tests. * Moving list_media to /account action. * Addressing PR comments. * Removing pointless comment. --------- Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com> --- api_tests/package.json | 2 +- api_tests/pnpm-lock.yaml | 85 +------------------ api_tests/src/follow.spec.ts | 4 +- api_tests/src/image.spec.ts | 46 ++++++++-- api_tests/src/private_message.spec.ts | 4 +- api_tests/src/shared.ts | 18 ++++ crates/api/src/local_user/list_media.rs | 26 ++++++ crates/api/src/local_user/mod.rs | 1 + crates/api/src/site/list_all_media.rs | 24 ++++++ crates/api/src/site/mod.rs | 1 + crates/api/src/site/purge/person.rs | 2 +- crates/api_common/src/person.rs | 19 ++++- crates/db_schema/src/impls/images.rs | 71 ++++++++++++---- crates/db_schema/src/source/images.rs | 15 +++- .../db_views_actor/src/comment_reply_view.rs | 19 +++++ .../db_views_actor/src/person_mention_view.rs | 18 ++++ crates/db_views_actor/src/structs.rs | 2 + src/api_routes_http.rs | 9 ++ 18 files changed, 252 insertions(+), 114 deletions(-) create mode 100644 crates/api/src/local_user/list_media.rs create mode 100644 crates/api/src/site/list_all_media.rs diff --git a/api_tests/package.json b/api_tests/package.json index 07f9fa776..75ef362f8 100644 --- a/api_tests/package.json +++ b/api_tests/package.json @@ -27,7 +27,7 @@ "eslint": "^8.57.0", "eslint-plugin-prettier": "^5.1.3", "jest": "^29.5.0", - "lemmy-js-client": "0.19.4-alpha.8", + "lemmy-js-client": "0.19.4-alpha.13", "prettier": "^3.2.5", "ts-jest": "^29.1.0", "typescript": "^5.4.2" diff --git a/api_tests/pnpm-lock.yaml b/api_tests/pnpm-lock.yaml index 39bba75a1..7b8c05328 100644 --- a/api_tests/pnpm-lock.yaml +++ b/api_tests/pnpm-lock.yaml @@ -30,8 +30,8 @@ devDependencies: specifier: ^29.5.0 version: 29.7.0(@types/node@20.11.27) lemmy-js-client: - specifier: 0.19.4-alpha.8 - version: 0.19.4-alpha.8 + specifier: 0.19.4-alpha.13 + version: 0.19.4-alpha.13 prettier: specifier: ^3.2.5 version: 3.2.5 @@ -1044,10 +1044,6 @@ packages: engines: {node: '>=8'} dev: true - /asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - dev: true - /babel-jest@29.7.0(@babel/core@7.23.9): resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1261,13 +1257,6 @@ packages: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} dev: true - /combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} - dependencies: - delayed-stream: 1.0.0 - dev: true - /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true @@ -1295,14 +1284,6 @@ packages: - ts-node dev: true - /cross-fetch@4.0.0: - resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} - dependencies: - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding - dev: true - /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -1342,11 +1323,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - dev: true - /detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} @@ -1646,15 +1622,6 @@ packages: resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} dev: true - /form-data@4.0.0: - resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} - engines: {node: '>= 6'} - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - dev: true - /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: true @@ -2390,13 +2357,8 @@ packages: engines: {node: '>=6'} dev: true - /lemmy-js-client@0.19.4-alpha.8: - resolution: {integrity: sha512-8vjqUYVOhyUTcmG9FvPLjrWziVwNa2/Zi+kSflTrajJsK0V+5DclJ5dhdVMUQ4DEA70gb0OuNMDlipPG2FoS5A==} - dependencies: - cross-fetch: 4.0.0 - form-data: 4.0.0 - transitivePeerDependencies: - - encoding + /lemmy-js-client@0.19.4-alpha.13: + resolution: {integrity: sha512-ru1dCqPSfOJdsGq7am5J7P7f+/hpyHGhNbCEV/JAZP2U1lGHul32gLpBkilDnStDNdeq52scjKx+3WskRJFGFA==} dev: true /leven@3.1.0: @@ -2485,18 +2447,6 @@ packages: picomatch: 2.3.1 dev: true - /mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - dev: true - - /mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - dependencies: - mime-db: 1.52.0 - dev: true - /mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -2523,18 +2473,6 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true - /node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - dependencies: - whatwg-url: 5.0.0 - dev: true - /node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} dev: true @@ -2952,10 +2890,6 @@ packages: is-number: 7.0.0 dev: true - /tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - dev: true - /ts-api-utils@1.3.0(typescript@5.4.2): resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} engines: {node: '>=16'} @@ -3067,17 +3001,6 @@ packages: makeerror: 1.0.12 dev: true - /webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - dev: true - - /whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - dev: true - /which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} diff --git a/api_tests/src/follow.spec.ts b/api_tests/src/follow.spec.ts index 0187e3ee1..276213eac 100644 --- a/api_tests/src/follow.spec.ts +++ b/api_tests/src/follow.spec.ts @@ -5,18 +5,18 @@ import { setupLogins, resolveBetaCommunity, followCommunity, - unfollowRemotes, getSite, waitUntil, beta, betaUrl, registerUser, + unfollows, } from "./shared"; beforeAll(setupLogins); afterAll(() => { - unfollowRemotes(alpha); + unfollows(); }); test("Follow local community", async () => { diff --git a/api_tests/src/image.spec.ts b/api_tests/src/image.spec.ts index 6414a8913..7fd1bd47c 100644 --- a/api_tests/src/image.spec.ts +++ b/api_tests/src/image.spec.ts @@ -14,25 +14,30 @@ import { betaUrl, createCommunity, createPost, + deleteAllImages, delta, epsilon, gamma, getSite, + imageFetchLimit, registerUser, resolveBetaCommunity, resolvePost, setupLogins, - unfollowRemotes, + unfollows, } from "./shared"; const downloadFileSync = require("download-file-sync"); beforeAll(setupLogins); afterAll(() => { - unfollowRemotes(alphaImage); + unfollows(); }); test("Upload image and delete it", async () => { + // Before running this test, you need to delete all previous images in the DB + await deleteAllImages(alpha); + // Upload test image. We use a simple string buffer as pictrs doesnt require an actual image // in testing mode. const upload_form: UploadImage = { @@ -48,6 +53,24 @@ test("Upload image and delete it", async () => { const content = downloadFileSync(upload.url); expect(content.length).toBeGreaterThan(0); + // Ensure that it comes back with the list_media endpoint + const listMediaRes = await alphaImage.listMedia(); + expect(listMediaRes.images.length).toBe(1); + + // Ensure that it also comes back with the admin all images + const listAllMediaRes = await alphaImage.listAllMedia({ + limit: imageFetchLimit, + }); + + // This number comes from all the previous thumbnails fetched in other tests. + const previousThumbnails = 1; + expect(listAllMediaRes.images.length).toBe(previousThumbnails); + + // The deleteUrl is a combination of the endpoint, delete token, and alias + let firstImage = listMediaRes.images[0]; + let deleteUrl = `${alphaUrl}/pictrs/image/delete/${firstImage.pictrs_delete_token}/${firstImage.pictrs_alias}`; + expect(deleteUrl).toBe(upload.delete_url); + // delete image const delete_form: DeleteImage = { token: upload.files![0].delete_token, @@ -59,6 +82,16 @@ test("Upload image and delete it", async () => { // ensure that image is deleted const content2 = downloadFileSync(upload.url); expect(content2).toBe(""); + + // Ensure that it shows the image is deleted + const deletedListMediaRes = await alphaImage.listMedia(); + expect(deletedListMediaRes.images.length).toBe(0); + + // Ensure that the admin shows its deleted + const deletedListAllMediaRes = await alphaImage.listAllMedia({ + limit: imageFetchLimit, + }); + expect(deletedListAllMediaRes.images.length).toBe(previousThumbnails - 1); }); test("Purge user, uploaded image removed", async () => { @@ -80,10 +113,10 @@ test("Purge user, uploaded image removed", async () => { // purge user let site = await getSite(user); - const purge_form: PurgePerson = { + const purgeForm: PurgePerson = { person_id: site.my_user!.local_user_view.person.id, }; - const delete_ = await alphaImage.purgePerson(purge_form); + const delete_ = await alphaImage.purgePerson(purgeForm); expect(delete_.success).toBe(true); // ensure that image is deleted @@ -117,10 +150,11 @@ test("Purge post, linked image removed", async () => { expect(post.post_view.post.url).toBe(upload.url); // purge post - const purge_form: PurgePost = { + + const purgeForm: PurgePost = { post_id: post.post_view.post.id, }; - const delete_ = await beta.purgePost(purge_form); + const delete_ = await beta.purgePost(purgeForm); expect(delete_.success).toBe(true); // ensure that image is deleted diff --git a/api_tests/src/private_message.spec.ts b/api_tests/src/private_message.spec.ts index 75dcaee33..063ee05ee 100644 --- a/api_tests/src/private_message.spec.ts +++ b/api_tests/src/private_message.spec.ts @@ -8,9 +8,9 @@ import { editPrivateMessage, listPrivateMessages, deletePrivateMessage, - unfollowRemotes, waitUntil, reportPrivateMessage, + unfollows, } from "./shared"; let recipient_id: number; @@ -22,7 +22,7 @@ beforeAll(async () => { }); afterAll(() => { - unfollowRemotes(alpha); + unfollows(); }); test("Create a private message", async () => { diff --git a/api_tests/src/shared.ts b/api_tests/src/shared.ts index af2629393..723b63887 100644 --- a/api_tests/src/shared.ts +++ b/api_tests/src/shared.ts @@ -5,6 +5,7 @@ import { BlockInstanceResponse, CommunityId, CreatePrivateMessageReport, + DeleteImage, EditCommunity, GetReplies, GetRepliesResponse, @@ -79,6 +80,7 @@ import { GetPersonDetails } from "lemmy-js-client/dist/types/GetPersonDetails"; import { ListingType } from "lemmy-js-client/dist/types/ListingType"; export const fetchFunction = fetch; +export const imageFetchLimit = 50; export let alphaUrl = "http://127.0.0.1:8541"; export let betaUrl = "http://127.0.0.1:8551"; @@ -865,9 +867,25 @@ export function randomString(length: number): string { return result; } +export async function deleteAllImages(api: LemmyHttp) { + const imagesRes = await api.listAllMedia({ + limit: imageFetchLimit, + }); + imagesRes.images; + + for (const image of imagesRes.images) { + const form: DeleteImage = { + token: image.pictrs_delete_token, + filename: image.pictrs_alias, + }; + await api.deleteImage(form); + } +} + export async function unfollows() { await Promise.all([ unfollowRemotes(alpha), + unfollowRemotes(beta), unfollowRemotes(gamma), unfollowRemotes(delta), unfollowRemotes(epsilon), diff --git a/crates/api/src/local_user/list_media.rs b/crates/api/src/local_user/list_media.rs new file mode 100644 index 000000000..25df8a4c2 --- /dev/null +++ b/crates/api/src/local_user/list_media.rs @@ -0,0 +1,26 @@ +use actix_web::web::{Data, Json, Query}; +use lemmy_api_common::{ + context::LemmyContext, + person::{ListMedia, ListMediaResponse}, +}; +use lemmy_db_schema::source::images::LocalImage; +use lemmy_db_views::structs::LocalUserView; +use lemmy_utils::error::LemmyError; + +#[tracing::instrument(skip(context))] +pub async fn list_media( + data: Query, + context: Data, + local_user_view: LocalUserView, +) -> Result, LemmyError> { + let page = data.page; + let limit = data.limit; + let images = LocalImage::get_all_paged_by_local_user_id( + &mut context.pool(), + local_user_view.local_user.id, + page, + limit, + ) + .await?; + Ok(Json(ListMediaResponse { images })) +} diff --git a/crates/api/src/local_user/mod.rs b/crates/api/src/local_user/mod.rs index 8bf2e5327..c00a4516e 100644 --- a/crates/api/src/local_user/mod.rs +++ b/crates/api/src/local_user/mod.rs @@ -10,6 +10,7 @@ pub mod generate_totp_secret; pub mod get_captcha; pub mod list_banned; pub mod list_logins; +pub mod list_media; pub mod login; pub mod logout; pub mod notifications; diff --git a/crates/api/src/site/list_all_media.rs b/crates/api/src/site/list_all_media.rs new file mode 100644 index 000000000..495e72e48 --- /dev/null +++ b/crates/api/src/site/list_all_media.rs @@ -0,0 +1,24 @@ +use actix_web::web::{Data, Json, Query}; +use lemmy_api_common::{ + context::LemmyContext, + person::{ListMedia, ListMediaResponse}, + utils::is_admin, +}; +use lemmy_db_schema::source::images::LocalImage; +use lemmy_db_views::structs::LocalUserView; +use lemmy_utils::error::LemmyError; + +#[tracing::instrument(skip(context))] +pub async fn list_all_media( + data: Query, + context: Data, + local_user_view: LocalUserView, +) -> Result, LemmyError> { + // Only let admins view all media + is_admin(&local_user_view)?; + + let page = data.page; + let limit = data.limit; + let images = LocalImage::get_all(&mut context.pool(), page, limit).await?; + Ok(Json(ListMediaResponse { images })) +} diff --git a/crates/api/src/site/mod.rs b/crates/api/src/site/mod.rs index d63c77ad9..f18dea3d0 100644 --- a/crates/api/src/site/mod.rs +++ b/crates/api/src/site/mod.rs @@ -1,6 +1,7 @@ pub mod block; pub mod federated_instances; pub mod leave_admin; +pub mod list_all_media; pub mod mod_log; pub mod purge; pub mod registration_applications; diff --git a/crates/api/src/site/purge/person.rs b/crates/api/src/site/purge/person.rs index 130d04552..658846eab 100644 --- a/crates/api/src/site/purge/person.rs +++ b/crates/api/src/site/purge/person.rs @@ -32,7 +32,7 @@ pub async fn purge_person( // Read the local user to get their images, and delete them if let Ok(local_user) = LocalUserView::read_person(&mut context.pool(), data.person_id).await { let pictrs_uploads = - LocalImage::get_all_by_local_user_id(&mut context.pool(), &local_user.local_user.id).await?; + LocalImage::get_all_by_local_user_id(&mut context.pool(), local_user.local_user.id).await?; for upload in pictrs_uploads { delete_image_from_pictrs(&upload.pictrs_alias, &upload.pictrs_delete_token, &context) diff --git a/crates/api_common/src/person.rs b/crates/api_common/src/person.rs index 7af966164..a4f9b64d9 100644 --- a/crates/api_common/src/person.rs +++ b/crates/api_common/src/person.rs @@ -1,7 +1,7 @@ use crate::sensitive::Sensitive; use lemmy_db_schema::{ newtypes::{CommentReplyId, CommunityId, LanguageId, PersonId, PersonMentionId}, - source::site::Site, + source::{images::LocalImage, site::Site}, CommentSortType, ListingType, PostListingMode, @@ -422,3 +422,20 @@ pub struct UpdateTotp { pub struct UpdateTotpResponse { pub enabled: bool, } + +#[skip_serializing_none] +#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "full", derive(TS))] +#[cfg_attr(feature = "full", ts(export))] +/// Get your user's image / media uploads. +pub struct ListMedia { + pub page: Option, + pub limit: Option, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS))] +#[cfg_attr(feature = "full", ts(export))] +pub struct ListMediaResponse { + pub images: Vec, +} diff --git a/crates/db_schema/src/impls/images.rs b/crates/db_schema/src/impls/images.rs index a5982bd98..40d5c5853 100644 --- a/crates/db_schema/src/impls/images.rs +++ b/crates/db_schema/src/impls/images.rs @@ -1,11 +1,8 @@ use crate::{ newtypes::{DbUrl, LocalUserId}, - schema::{ - local_image::dsl::{local_image, local_user_id, pictrs_alias}, - remote_image::dsl::{link, remote_image}, - }, + schema::{local_image, remote_image}, source::images::{LocalImage, LocalImageForm, RemoteImage, RemoteImageForm}, - utils::{get_conn, DbPool}, + utils::{get_conn, limit_and_offset, DbPool}, }; use diesel::{ dsl::exists, @@ -15,7 +12,6 @@ use diesel::{ ExpressionMethods, NotFound, QueryDsl, - Table, }; use diesel_async::RunQueryDsl; use url::Url; @@ -23,27 +19,64 @@ use url::Url; impl LocalImage { pub async fn create(pool: &mut DbPool<'_>, form: &LocalImageForm) -> Result { let conn = &mut get_conn(pool).await?; - insert_into(local_image) + insert_into(local_image::table) .values(form) .get_result::(conn) .await } + pub async fn get_all_paged_by_local_user_id( + pool: &mut DbPool<'_>, + user_id: LocalUserId, + page: Option, + limit: Option, + ) -> Result, Error> { + Self::get_all_helper(pool, Some(user_id), page, limit, false).await + } + pub async fn get_all_by_local_user_id( pool: &mut DbPool<'_>, - user_id: &LocalUserId, + user_id: LocalUserId, + ) -> Result, Error> { + Self::get_all_helper(pool, Some(user_id), None, None, true).await + } + + pub async fn get_all( + pool: &mut DbPool<'_>, + page: Option, + limit: Option, + ) -> Result, Error> { + Self::get_all_helper(pool, None, page, limit, false).await + } + + async fn get_all_helper( + pool: &mut DbPool<'_>, + user_id: Option, + page: Option, + limit: Option, + ignore_page_limits: bool, ) -> Result, Error> { let conn = &mut get_conn(pool).await?; - local_image - .filter(local_user_id.eq(user_id)) - .select(local_image::all_columns()) - .load::(conn) - .await + let mut query = local_image::table + .select(local_image::all_columns) + .order_by(local_image::published.desc()) + .into_boxed(); + + if let Some(user_id) = user_id { + query = query.filter(local_image::local_user_id.eq(user_id)) + } + + if !ignore_page_limits { + let (limit, offset) = limit_and_offset(page, limit)?; + query = query.limit(limit).offset(offset); + } + + query.load::(conn).await } pub async fn delete_by_alias(pool: &mut DbPool<'_>, alias: &str) -> Result { let conn = &mut get_conn(pool).await?; - diesel::delete(local_image.filter(pictrs_alias.eq(alias))) + diesel::delete(local_image::table.filter(local_image::pictrs_alias.eq(alias))) .execute(conn) .await } @@ -56,7 +89,7 @@ impl RemoteImage { .into_iter() .map(|url| RemoteImageForm { link: url.into() }) .collect::>(); - insert_into(remote_image) + insert_into(remote_image::table) .values(forms) .on_conflict_do_nothing() .execute(conn) @@ -66,9 +99,11 @@ impl RemoteImage { pub async fn validate(pool: &mut DbPool<'_>, link_: DbUrl) -> Result<(), Error> { let conn = &mut get_conn(pool).await?; - let exists = select(exists(remote_image.filter((link).eq(link_)))) - .get_result::(conn) - .await?; + let exists = select(exists( + remote_image::table.filter(remote_image::link.eq(link_)), + )) + .get_result::(conn) + .await?; if exists { Ok(()) } else { diff --git a/crates/db_schema/src/source/images.rs b/crates/db_schema/src/source/images.rs index 3bf2a5bb8..da6be2a14 100644 --- a/crates/db_schema/src/source/images.rs +++ b/crates/db_schema/src/source/images.rs @@ -2,16 +2,27 @@ use crate::newtypes::{DbUrl, LocalUserId}; #[cfg(feature = "full")] use crate::schema::{local_image, remote_image}; use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use serde_with::skip_serializing_none; use std::fmt::Debug; +#[cfg(feature = "full")] +use ts_rs::TS; use typed_builder::TypedBuilder; -#[derive(PartialEq, Eq, Debug, Clone)] -#[cfg_attr(feature = "full", derive(Queryable, Associations))] +#[skip_serializing_none] +#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] +#[cfg_attr( + feature = "full", + derive(Queryable, Selectable, Identifiable, Associations, TS) +)] +#[cfg_attr(feature = "full", ts(export))] #[cfg_attr(feature = "full", diesel(table_name = local_image))] #[cfg_attr( feature = "full", diesel(belongs_to(crate::source::local_user::LocalUser)) )] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", diesel(primary_key(local_user_id)))] pub struct LocalImage { pub local_user_id: Option, pub pictrs_alias: String, diff --git a/crates/db_views_actor/src/comment_reply_view.rs b/crates/db_views_actor/src/comment_reply_view.rs index 6d122291a..77f744a97 100644 --- a/crates/db_views_actor/src/comment_reply_view.rs +++ b/crates/db_views_actor/src/comment_reply_view.rs @@ -47,6 +47,16 @@ fn queries<'a>() -> Queries< ), ); + let is_local_user_banned_from_community = |person_id| { + exists( + community_person_ban::table.filter( + community::id + .eq(community_person_ban::community_id) + .and(community_person_ban::person_id.eq(person_id)), + ), + ) + }; + let is_saved = |person_id| { exists( comment_saved::table.filter( @@ -107,6 +117,14 @@ fn queries<'a>() -> Queries< let all_joins = move |query: comment_reply::BoxedQuery<'a, Pg>, my_person_id: Option| { + let is_local_user_banned_from_community_selection: Box< + dyn BoxableExpression<_, Pg, SqlType = sql_types::Bool>, + > = if let Some(person_id) = my_person_id { + Box::new(is_local_user_banned_from_community(person_id)) + } else { + Box::new(false.into_sql::()) + }; + let score_selection: Box< dyn BoxableExpression<_, Pg, SqlType = sql_types::Nullable>, > = if let Some(person_id) = my_person_id { @@ -153,6 +171,7 @@ fn queries<'a>() -> Queries< aliases::person1.fields(person::all_columns), comment_aggregates::all_columns, is_creator_banned_from_community, + is_local_user_banned_from_community_selection, creator_is_moderator, creator_is_admin, subscribed_type_selection, diff --git a/crates/db_views_actor/src/person_mention_view.rs b/crates/db_views_actor/src/person_mention_view.rs index 21a1bd0de..399850292 100644 --- a/crates/db_views_actor/src/person_mention_view.rs +++ b/crates/db_views_actor/src/person_mention_view.rs @@ -47,6 +47,16 @@ fn queries<'a>() -> Queries< ), ); + let is_local_user_banned_from_community = |person_id| { + exists( + community_person_ban::table.filter( + community::id + .eq(community_person_ban::community_id) + .and(community_person_ban::person_id.eq(person_id)), + ), + ) + }; + let is_saved = |person_id| { exists( comment_saved::table.filter( @@ -107,6 +117,13 @@ fn queries<'a>() -> Queries< let all_joins = move |query: person_mention::BoxedQuery<'a, Pg>, my_person_id: Option| { + let is_local_user_banned_from_community_selection: Box< + dyn BoxableExpression<_, Pg, SqlType = sql_types::Bool>, + > = if let Some(person_id) = my_person_id { + Box::new(is_local_user_banned_from_community(person_id)) + } else { + Box::new(false.into_sql::()) + }; let score_selection: Box< dyn BoxableExpression<_, Pg, SqlType = sql_types::Nullable>, > = if let Some(person_id) = my_person_id { @@ -153,6 +170,7 @@ fn queries<'a>() -> Queries< aliases::person1.fields(person::all_columns), comment_aggregates::all_columns, is_creator_banned_from_community, + is_local_user_banned_from_community_selection, creator_is_moderator, creator_is_admin, subscribed_type_selection, diff --git a/crates/db_views_actor/src/structs.rs b/crates/db_views_actor/src/structs.rs index f25662f7b..2356d2be4 100644 --- a/crates/db_views_actor/src/structs.rs +++ b/crates/db_views_actor/src/structs.rs @@ -108,6 +108,7 @@ pub struct PersonMentionView { pub recipient: Person, pub counts: CommentAggregates, pub creator_banned_from_community: bool, + pub banned_from_community: bool, pub creator_is_moderator: bool, pub creator_is_admin: bool, pub subscribed: SubscribedType, @@ -131,6 +132,7 @@ pub struct CommentReplyView { pub recipient: Person, pub counts: CommentAggregates, pub creator_banned_from_community: bool, + pub banned_from_community: bool, pub creator_is_moderator: bool, pub creator_is_admin: bool, pub subscribed: SubscribedType, diff --git a/src/api_routes_http.rs b/src/api_routes_http.rs index 966862fa5..013e2e092 100644 --- a/src/api_routes_http.rs +++ b/src/api_routes_http.rs @@ -29,6 +29,7 @@ use lemmy_api::{ get_captcha::get_captcha, list_banned::list_banned_users, list_logins::list_logins, + list_media::list_media, login::login, logout::logout, notifications::{ @@ -71,6 +72,7 @@ use lemmy_api::{ block::block_instance, federated_instances::get_federated_instances, leave_admin::leave_admin, + list_all_media::list_all_media, mod_log::get_mod_log, purge::{ comment::purge_comment, @@ -282,6 +284,12 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) { .wrap(rate_limit.import_user_settings()) .route(web::post().to(import_settings)), ) + // TODO, all the current account related actions under /user need to get moved here eventually + .service( + web::scope("/account") + .wrap(rate_limit.message()) + .route("/list_media", web::get().to(list_media)), + ) // User actions .service( web::scope("/user") @@ -339,6 +347,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) { "/registration_application/approve", web::put().to(approve_registration_application), ) + .route("/list_all_media", web::get().to(list_all_media)) .service( web::scope("/purge") .route("/person", web::post().to(purge_person))