diff --git a/.woodpecker.yml b/.woodpecker.yml index a51a1499d..4bac67169 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -2,7 +2,7 @@ # See https://github.com/woodpecker-ci/woodpecker/issues/1677 variables: - - &rust_image "rust:1.75" + - &rust_image "rust:1.76" - &install_pnpm "corepack enable pnpm" - &slow_check_paths - path: diff --git a/crates/api_common/src/post.rs b/crates/api_common/src/post.rs index 36f971ca7..1db07e451 100644 --- a/crates/api_common/src/post.rs +++ b/crates/api_common/src/post.rs @@ -28,6 +28,9 @@ pub struct CreatePost { pub honeypot: Option, pub nsfw: Option, pub language_id: Option, + #[cfg_attr(feature = "full", ts(type = "string"))] + /// Instead of fetching a thumbnail, use a custom one. + pub custom_thumbnail: Option, } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -114,6 +117,9 @@ pub struct EditPost { pub body: Option, pub nsfw: Option, pub language_id: Option, + #[cfg_attr(feature = "full", ts(type = "string"))] + /// Instead of fetching a thumbnail, use a custom one. + pub custom_thumbnail: Option, } #[derive(Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash)] diff --git a/crates/api_crud/src/post/create.rs b/crates/api_crud/src/post/create.rs index 910454d5b..a5a9c013f 100644 --- a/crates/api_crud/src/post/create.rs +++ b/crates/api_crud/src/post/create.rs @@ -57,10 +57,12 @@ pub async fn create_post( let data_url = data.url.as_ref(); let url = data_url.map(clean_url_params); // TODO no good way to handle a "clear" + let custom_thumbnail = data.custom_thumbnail.as_ref().map(clean_url_params); is_valid_post_title(&data.name)?; is_valid_body_field(&body, true)?; - check_url_scheme(&data.url)?; + check_url_scheme(&url)?; + check_url_scheme(&custom_thumbnail)?; check_community_user_action( &local_user_view.person, @@ -84,9 +86,17 @@ pub async fn create_post( } } + // Only generate the thumbnail if there's no custom thumbnail provided, + // otherwise it will save it in pictrs + let generate_thumbnail = custom_thumbnail.is_none(); + // Fetch post links and pictrs cached image - let metadata = fetch_link_metadata_opt(url.as_ref(), true, &context).await; + let metadata = fetch_link_metadata_opt(url.as_ref(), generate_thumbnail, &context).await; let url = proxy_image_link_opt_apub(url, &context).await?; + let thumbnail_url = proxy_image_link_opt_apub(custom_thumbnail, &context) + .await? + .map(Into::into) + .or(metadata.thumbnail); // Only need to check if language is allowed in case user set it explicitly. When using default // language, it already only returns allowed languages. @@ -121,7 +131,7 @@ pub async fn create_post( .embed_description(metadata.opengraph_data.description) .embed_video_url(metadata.opengraph_data.embed_video_url) .language_id(language_id) - .thumbnail_url(metadata.thumbnail) + .thumbnail_url(thumbnail_url) .build(); let inserted_post = Post::create(&mut context.pool(), &post_form) diff --git a/crates/api_crud/src/post/update.rs b/crates/api_crud/src/post/update.rs index c36718615..e858d9b30 100644 --- a/crates/api_crud/src/post/update.rs +++ b/crates/api_crud/src/post/update.rs @@ -43,6 +43,7 @@ pub async fn update_post( // TODO No good way to handle a clear. // Issue link: https://github.com/LemmyNet/lemmy/issues/2287 let url = data.url.as_ref().map(clean_url_params); + let custom_thumbnail = data.custom_thumbnail.as_ref().map(clean_url_params); let slur_regex = local_site_to_slur_regex(&local_site); check_slurs_opt(&data.name, &slur_regex)?; @@ -53,7 +54,8 @@ pub async fn update_post( } is_valid_body_field(&body, true)?; - check_url_scheme(&data.url)?; + check_url_scheme(&url)?; + check_url_scheme(&custom_thumbnail)?; let post_id = data.post_id; let orig_post = Post::read(&mut context.pool(), post_id).await?; @@ -70,10 +72,14 @@ pub async fn update_post( Err(LemmyErrorType::NoPostEditAllowed)? } - // Fetch post links and Pictrs cached image if url was updated - let (embed_title, embed_description, embed_video_url, thumbnail_url) = match &url { + // Fetch post links and thumbnail if url was updated + let (embed_title, embed_description, embed_video_url, metadata_thumbnail) = match &url { Some(url) => { - let metadata = fetch_link_metadata(url, true, &context).await?; + // Only generate the thumbnail if there's no custom thumbnail provided, + // otherwise it will save it in pictrs + let generate_thumbnail = custom_thumbnail.is_none(); + + let metadata = fetch_link_metadata(url, generate_thumbnail, &context).await?; ( Some(metadata.opengraph_data.title), Some(metadata.opengraph_data.description), @@ -83,11 +89,21 @@ pub async fn update_post( } _ => Default::default(), }; + let url = match url { Some(url) => Some(proxy_image_link_opt_apub(Some(url), &context).await?), _ => Default::default(), }; + let custom_thumbnail = match custom_thumbnail { + Some(custom_thumbnail) => { + Some(proxy_image_link_opt_apub(Some(custom_thumbnail), &context).await?) + } + _ => Default::default(), + }; + + let thumbnail_url = custom_thumbnail.or(metadata_thumbnail); + let language_id = data.language_id; CommunityLanguage::is_allowed_community_language( &mut context.pool(), diff --git a/crates/db_schema/src/impls/local_user.rs b/crates/db_schema/src/impls/local_user.rs index 98d1c8494..14da24bae 100644 --- a/crates/db_schema/src/impls/local_user.rs +++ b/crates/db_schema/src/impls/local_user.rs @@ -1,12 +1,6 @@ use crate::{ newtypes::{DbUrl, LocalUserId, PersonId}, - schema::local_user::dsl::{ - accepted_application, - email, - email_verified, - local_user, - password_encrypted, - }, + schema::{local_user, person, registration_application}, source::{ actor_language::{LocalUserLanguage, SiteLanguage}, local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm}, @@ -15,11 +9,18 @@ use crate::{ utils::{ functions::{coalesce, lower}, get_conn, + now, DbPool, }, }; use bcrypt::{hash, DEFAULT_COST}; -use diesel::{dsl::insert_into, result::Error, ExpressionMethods, JoinOnDsl, QueryDsl}; +use diesel::{ + dsl::{insert_into, not, IntervalDsl}, + result::Error, + ExpressionMethods, + JoinOnDsl, + QueryDsl, +}; use diesel_async::RunQueryDsl; impl LocalUser { @@ -31,16 +32,16 @@ impl LocalUser { let conn = &mut get_conn(pool).await?; let password_hash = hash(new_password, DEFAULT_COST).expect("Couldn't hash password"); - diesel::update(local_user.find(local_user_id)) - .set((password_encrypted.eq(password_hash),)) + diesel::update(local_user::table.find(local_user_id)) + .set((local_user::password_encrypted.eq(password_hash),)) .get_result::(conn) .await } pub async fn set_all_users_email_verified(pool: &mut DbPool<'_>) -> Result, Error> { let conn = &mut get_conn(pool).await?; - diesel::update(local_user) - .set(email_verified.eq(true)) + diesel::update(local_user::table) + .set(local_user::email_verified.eq(true)) .get_results::(conn) .await } @@ -49,18 +50,43 @@ impl LocalUser { pool: &mut DbPool<'_>, ) -> Result, Error> { let conn = &mut get_conn(pool).await?; - diesel::update(local_user) - .set(accepted_application.eq(true)) + diesel::update(local_user::table) + .set(local_user::accepted_application.eq(true)) .get_results::(conn) .await } - pub async fn is_email_taken(pool: &mut DbPool<'_>, email_: &str) -> Result { + pub async fn delete_old_denied_local_users(pool: &mut DbPool<'_>) -> Result { + let conn = &mut get_conn(pool).await?; + + // Make sure: + // - The deny reason exists + // - The app is older than a week + // - The accepted_application is false + let old_denied_registrations = registration_application::table + .filter(registration_application::deny_reason.is_not_null()) + .filter(registration_application::published.lt(now() - 1.week())) + .select(registration_application::local_user_id); + + // Delete based on join logic is here: + // https://stackoverflow.com/questions/60836040/how-do-i-perform-a-delete-with-sub-query-in-diesel-against-a-postgres-database + let local_users = local_user::table + .filter(local_user::id.eq_any(old_denied_registrations)) + .filter(not(local_user::accepted_application)) + .select(local_user::person_id); + + // Delete the person rows, which should automatically clear the local_user ones + let persons = person::table.filter(person::id.eq_any(local_users)); + + diesel::delete(persons).execute(conn).await + } + + pub async fn is_email_taken(pool: &mut DbPool<'_>, email: &str) -> Result { use diesel::dsl::{exists, select}; let conn = &mut get_conn(pool).await?; - select(exists( - local_user.filter(lower(coalesce(email, "")).eq(email_.to_lowercase())), - )) + select(exists(local_user::table.filter( + lower(coalesce(local_user::email, "")).eq(email.to_lowercase()), + ))) .get_result(conn) .await } @@ -78,7 +104,6 @@ impl LocalUser { community_follower, instance, instance_block, - person, person_block, post, post_saved, @@ -140,6 +165,15 @@ impl LocalUser { } } +impl LocalUserInsertForm { + pub fn test_form(person_id: PersonId) -> Self { + Self::builder() + .person_id(person_id) + .password_encrypted(String::new()) + .build() + } +} + pub struct UserBackupLists { pub followed_communities: Vec, pub saved_posts: Vec, @@ -162,7 +196,7 @@ impl Crud for LocalUser { 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) + let local_user_ = insert_into(local_user::table) .values(form_with_encrypted_password) .get_result::(conn) .await?; @@ -185,7 +219,7 @@ impl Crud for LocalUser { form: &Self::UpdateForm, ) -> Result { let conn = &mut get_conn(pool).await?; - diesel::update(local_user.find(local_user_id)) + diesel::update(local_user::table.find(local_user_id)) .set(form) .get_result::(conn) .await diff --git a/crates/db_schema/src/impls/person.rs b/crates/db_schema/src/impls/person.rs index 32ce6c97a..9fb1ee1c5 100644 --- a/crates/db_schema/src/impls/person.rs +++ b/crates/db_schema/src/impls/person.rs @@ -1,5 +1,5 @@ use crate::{ - newtypes::{CommunityId, DbUrl, PersonId}, + newtypes::{CommunityId, DbUrl, InstanceId, PersonId}, schema::{instance, local_user, person, person_follower}, source::person::{ Person, @@ -86,6 +86,16 @@ impl Person { } } +impl PersonInsertForm { + pub fn test_form(instance_id: InstanceId, name: &str) -> Self { + Self::builder() + .name(name.to_owned()) + .public_key("pubkey".to_string()) + .instance_id(instance_id) + .build() + } +} + #[async_trait] impl ApubActor for Person { async fn read_from_apub_id( diff --git a/crates/db_views/src/post_view.rs b/crates/db_views/src/post_view.rs index cdfa923d4..6c5a983b1 100644 --- a/crates/db_views/src/post_view.rs +++ b/crates/db_views/src/post_view.rs @@ -698,7 +698,7 @@ mod tests { use lemmy_db_schema::{ aggregates::structs::PostAggregates, impls::actor_language::UNDETERMINED_ID, - newtypes::{InstanceId, LanguageId, PersonId}, + newtypes::LanguageId, source::{ actor_language::LocalUserLanguage, comment::{Comment, CommentInsertForm}, @@ -757,37 +757,22 @@ mod tests { } } - fn default_person_insert_form(instance_id: InstanceId, name: &str) -> PersonInsertForm { - PersonInsertForm::builder() - .name(name.to_owned()) - .public_key("pubkey".to_string()) - .instance_id(instance_id) - .build() - } - - fn default_local_user_form(person_id: PersonId) -> LocalUserInsertForm { - LocalUserInsertForm::builder() - .person_id(person_id) - .password_encrypted(String::new()) - .build() - } - async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult { let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?; - let new_person = default_person_insert_form(inserted_instance.id, "tegan"); + let new_person = PersonInsertForm::test_form(inserted_instance.id, "tegan"); let inserted_person = Person::create(pool, &new_person).await?; let local_user_form = LocalUserInsertForm { admin: Some(true), - ..default_local_user_form(inserted_person.id) + ..LocalUserInsertForm::test_form(inserted_person.id) }; let inserted_local_user = LocalUser::create(pool, &local_user_form).await?; let new_bot = PersonInsertForm { bot_account: Some(true), - ..default_person_insert_form(inserted_instance.id, "mybot") + ..PersonInsertForm::test_form(inserted_instance.id, "mybot") }; let inserted_bot = Person::create(pool, &new_bot).await?; @@ -802,12 +787,15 @@ mod tests { let inserted_community = Community::create(pool, &new_community).await?; // Test a person block, make sure the post query doesn't include their post - let blocked_person = default_person_insert_form(inserted_instance.id, "john"); + let blocked_person = PersonInsertForm::test_form(inserted_instance.id, "john"); let inserted_blocked_person = Person::create(pool, &blocked_person).await?; - let inserted_blocked_local_user = - LocalUser::create(pool, &default_local_user_form(inserted_blocked_person.id)).await?; + let inserted_blocked_local_user = LocalUser::create( + pool, + &LocalUserInsertForm::test_form(inserted_blocked_person.id), + ) + .await?; let post_from_blocked_person = PostInsertForm::builder() .name(POST_BY_BLOCKED_PERSON.to_string()) diff --git a/crates/db_views/src/private_message_view.rs b/crates/db_views/src/private_message_view.rs index d04ff7b49..a3e2469c9 100644 --- a/crates/db_views/src/private_message_view.rs +++ b/crates/db_views/src/private_message_view.rs @@ -12,7 +12,7 @@ use diesel_async::RunQueryDsl; use lemmy_db_schema::{ aliases, newtypes::{PersonId, PrivateMessageId}, - schema::{person, person_block, private_message}, + schema::{instance_block, person, person_block, private_message}, utils::{get_conn, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn}, }; use tracing::debug; @@ -34,6 +34,13 @@ fn queries<'a>() -> Queries< .and(person_block::person_id.eq(aliases::person1.field(person::id))), ), ) + .left_join( + instance_block::table.on( + person::instance_id + .eq(instance_block::instance_id) + .and(instance_block::person_id.eq(aliases::person1.field(person::id))), + ), + ) }; let selection = ( @@ -55,7 +62,9 @@ fn queries<'a>() -> Queries< let mut query = all_joins(private_message::table.into_boxed()) .select(selection) // Dont show replies from blocked users - .filter(person_block::person_id.is_null()); + .filter(person_block::person_id.is_null()) + // Dont show replies from blocked instances + .filter(instance_block::person_id.is_null()); // If its unread, I only want the ones to me if options.unread_only { @@ -116,6 +125,8 @@ impl PrivateMessageView { use diesel::dsl::count; let conn = &mut get_conn(pool).await?; private_message::table + // Necessary to get the senders instance_id + .inner_join(person::table.on(private_message::creator_id.eq(person::id))) .left_join( person_block::table.on( private_message::creator_id @@ -123,8 +134,17 @@ impl PrivateMessageView { .and(person_block::person_id.eq(my_person_id)), ), ) + .left_join( + instance_block::table.on( + person::instance_id + .eq(instance_block::instance_id) + .and(instance_block::person_id.eq(my_person_id)), + ), + ) // Dont count replies from blocked users .filter(person_block::person_id.is_null()) + // Dont count replies from blocked instances + .filter(instance_block::person_id.is_null()) .filter(private_message::read.eq(false)) .filter(private_message::recipient_id.eq(my_person_id)) .filter(private_message::deleted.eq(false)) @@ -160,24 +180,30 @@ mod tests { use crate::{private_message_view::PrivateMessageQuery, structs::PrivateMessageView}; use lemmy_db_schema::{ assert_length, + newtypes::InstanceId, source::{ instance::Instance, + instance_block::{InstanceBlock, InstanceBlockForm}, person::{Person, PersonInsertForm}, person_block::{PersonBlock, PersonBlockForm}, private_message::{PrivateMessage, PrivateMessageInsertForm}, }, traits::{Blockable, Crud}, - utils::build_db_pool_for_tests, + utils::{build_db_pool_for_tests, DbPool}, }; + use lemmy_utils::error::LemmyResult; use pretty_assertions::assert_eq; use serial_test::serial; - #[tokio::test] - #[serial] - async fn test_crud() { + struct Data { + instance: Instance, + timmy: Person, + jess: Person, + sara: Person, + } + + async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult { let message_content = String::new(); - let pool = &build_db_pool_for_tests().await; - let pool = &mut pool.into(); let instance = Instance::read_or_create(pool, "my_domain.tld".to_string()) .await @@ -243,6 +269,32 @@ mod tests { .await .unwrap(); + Ok(Data { + instance, + timmy, + jess, + sara, + }) + } + + async fn cleanup(instance_id: InstanceId, pool: &mut DbPool<'_>) -> LemmyResult<()> { + // This also deletes all persons and private messages thanks to sql `on delete cascade` + Instance::delete(pool, instance_id).await.unwrap(); + Ok(()) + } + + #[tokio::test] + #[serial] + async fn read_private_messages() -> LemmyResult<()> { + let pool = &build_db_pool_for_tests().await; + let pool = &mut pool.into(); + let Data { + timmy, + jess, + sara, + instance, + } = init_data(pool).await?; + let timmy_messages = PrivateMessageQuery { unread_only: false, creator_id: None, @@ -303,6 +355,21 @@ mod tests { assert_eq!(timmy_sara_unread_messages[0].creator.id, sara.id); assert_eq!(timmy_sara_unread_messages[0].recipient.id, timmy.id); + cleanup(instance.id, pool).await + } + + #[tokio::test] + #[serial] + async fn ensure_person_block() -> LemmyResult<()> { + let pool = &build_db_pool_for_tests().await; + let pool = &mut pool.into(); + let Data { + timmy, + sara, + instance, + jess: _, + } = init_data(pool).await?; + // Make sure blocks are working let timmy_blocks_sara_form = PersonBlockForm { person_id: timmy.id, @@ -336,7 +403,52 @@ mod tests { .unwrap(); assert_eq!(timmy_unread_messages, 1); - // This also deletes all persons and private messages thanks to sql `on delete cascade` - Instance::delete(pool, instance.id).await.unwrap(); + cleanup(instance.id, pool).await + } + + #[tokio::test] + #[serial] + async fn ensure_instance_block() -> LemmyResult<()> { + let pool = &build_db_pool_for_tests().await; + let pool = &mut pool.into(); + let Data { + timmy, + jess: _, + sara, + instance, + } = init_data(pool).await?; + // Make sure instance_blocks are working + let timmy_blocks_instance_form = InstanceBlockForm { + person_id: timmy.id, + instance_id: sara.instance_id, + }; + + let inserted_instance_block = InstanceBlock::block(pool, &timmy_blocks_instance_form) + .await + .unwrap(); + + let expected_instance_block = InstanceBlock { + person_id: timmy.id, + instance_id: sara.instance_id, + published: inserted_instance_block.published, + }; + assert_eq!(expected_instance_block, inserted_instance_block); + + let timmy_messages = PrivateMessageQuery { + unread_only: true, + creator_id: None, + ..Default::default() + } + .list(pool, timmy.id) + .await + .unwrap(); + + assert_length!(0, &timmy_messages); + + let timmy_unread_messages = PrivateMessageView::get_unread_messages(pool, timmy.id) + .await + .unwrap(); + assert_eq!(timmy_unread_messages, 0); + cleanup(instance.id, pool).await } } diff --git a/crates/routes/src/feeds.rs b/crates/routes/src/feeds.rs index 9695401df..490fce4ae 100644 --- a/crates/routes/src/feeds.rs +++ b/crates/routes/src/feeds.rs @@ -29,6 +29,7 @@ use once_cell::sync::Lazy; use rss::{ extension::{dublincore::DublinCoreExtension, ExtensionBuilder, ExtensionMap}, Channel, + EnclosureBuilder, Guid, Item, }; @@ -495,7 +496,6 @@ fn create_post_items( let mut items: Vec = Vec::new(); for p in posts { - // TODO add images let post_url = format!("{}/post/{}", protocol_and_hostname, p.post.id); let community_url = format!( "{}/c/{}", @@ -520,12 +520,21 @@ fn create_post_items( p.counts.comments); // If its a url post, add it to the description - let link = Some(if let Some(url) = p.post.url { + // and see if we can parse it as a media enclosure. + let enclosure_opt = p.post.url.map(|url| { let link_html = format!("
{url}"); description.push_str(&link_html); - url.to_string() - } else { - post_url.clone() + + let mime_type = p + .post + .url_content_type + .unwrap_or_else(|| "application/octet-stream".to_string()); + let mut enclosure_bld = EnclosureBuilder::default(); + + enclosure_bld.url(url.as_str().to_string()); + enclosure_bld.mime_type(mime_type); + enclosure_bld.length("0".to_string()); + enclosure_bld.build() }); if let Some(body) = p.post.body { @@ -558,8 +567,9 @@ fn create_post_items( guid, description: Some(sanitize_xml(description)), dublin_core_ext, - link, + link: Some(post_url.clone()), extensions, + enclosure: enclosure_opt, ..Default::default() }; diff --git a/docker/Dockerfile b/docker/Dockerfile index e33b99cfb..1bbf4ddbd 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:1.6 -ARG RUST_VERSION=1.75 +ARG RUST_VERSION=1.76 ARG CARGO_BUILD_FEATURES=default ARG RUST_RELEASE_MODE=debug diff --git a/src/scheduled_tasks.rs b/src/scheduled_tasks.rs index 7433c9638..38eb4ece2 100644 --- a/src/scheduled_tasks.rs +++ b/src/scheduled_tasks.rs @@ -22,7 +22,10 @@ use lemmy_db_schema::{ received_activity, sent_activity, }, - source::instance::{Instance, InstanceForm}, + source::{ + instance::{Instance, InstanceForm}, + local_user::LocalUser, + }, utils::{get_conn, naive_now, now, DbPool, DELETED_REPLACEMENT_TEXT}, }; use lemmy_routes::nodeinfo::NodeInfo; @@ -79,24 +82,19 @@ pub async fn setup(context: LemmyContext) -> Result<(), LemmyError> { }); let context_1 = context.clone(); - // Overwrite deleted & removed posts and comments every day + // Daily tasks: + // - Overwrite deleted & removed posts and comments every day + // - Delete old denied users + // - Update instance software scheduler.every(CTimeUnits::days(1)).run(move || { let context = context_1.clone(); async move { overwrite_deleted_posts_and_comments(&mut context.pool()).await; - } - }); - - let context_1 = context.clone(); - // Update the Instance Software - scheduler.every(CTimeUnits::days(1)).run(move || { - let context = context_1.clone(); - - async move { + delete_old_denied_users(&mut context.pool()).await; update_instance_software(&mut context.pool(), context.client()) .await - .map_err(|e| warn!("Failed to update instance software: {e}")) + .inspect_err(|e| warn!("Failed to update instance software: {e}")) .ok(); } }); @@ -115,6 +113,7 @@ async fn startup_jobs(pool: &mut DbPool<'_>) { update_banned_when_expired(pool).await; clear_old_activities(pool).await; overwrite_deleted_posts_and_comments(pool).await; + delete_old_denied_users(pool).await; } /// Update the hot_rank columns for the aggregates tables @@ -277,10 +276,10 @@ async fn delete_expired_captcha_answers(pool: &mut DbPool<'_>) { ) .execute(&mut conn) .await - .map(|_| { + .inspect(|_| { info!("Done."); }) - .map_err(|e| error!("Failed to clear old captcha answers: {e}")) + .inspect_err(|e| error!("Failed to clear old captcha answers: {e}")) .ok(); } Err(e) => { @@ -301,7 +300,7 @@ async fn clear_old_activities(pool: &mut DbPool<'_>) { ) .execute(&mut conn) .await - .map_err(|e| error!("Failed to clear old sent activities: {e}")) + .inspect_err(|e| error!("Failed to clear old sent activities: {e}")) .ok(); diesel::delete( @@ -310,8 +309,8 @@ async fn clear_old_activities(pool: &mut DbPool<'_>) { ) .execute(&mut conn) .await - .map(|_| info!("Done.")) - .map_err(|e| error!("Failed to clear old received activities: {e}")) + .inspect(|_| info!("Done.")) + .inspect_err(|e| error!("Failed to clear old received activities: {e}")) .ok(); } Err(e) => { @@ -320,6 +319,16 @@ async fn clear_old_activities(pool: &mut DbPool<'_>) { } } +async fn delete_old_denied_users(pool: &mut DbPool<'_>) { + LocalUser::delete_old_denied_local_users(pool) + .await + .inspect(|_| { + info!("Done."); + }) + .inspect(|e| error!("Failed to deleted old denied users: {e}")) + .ok(); +} + /// overwrite posts and comments 30d after deletion async fn overwrite_deleted_posts_and_comments(pool: &mut DbPool<'_>) { info!("Overwriting deleted posts..."); @@ -339,10 +348,10 @@ async fn overwrite_deleted_posts_and_comments(pool: &mut DbPool<'_>) { )) .execute(&mut conn) .await - .map(|_| { + .inspect(|_| { info!("Done."); }) - .map_err(|e| error!("Failed to overwrite deleted posts: {e}")) + .inspect_err(|e| error!("Failed to overwrite deleted posts: {e}")) .ok(); info!("Overwriting deleted comments..."); @@ -355,10 +364,10 @@ async fn overwrite_deleted_posts_and_comments(pool: &mut DbPool<'_>) { .set(comment::content.eq(DELETED_REPLACEMENT_TEXT)) .execute(&mut conn) .await - .map(|_| { + .inspect(|_| { info!("Done."); }) - .map_err(|e| error!("Failed to overwrite deleted comments: {e}")) + .inspect_err(|e| error!("Failed to overwrite deleted comments: {e}")) .ok(); } Err(e) => { @@ -390,14 +399,14 @@ async fn active_counts(pool: &mut DbPool<'_>) { sql_query(update_site_stmt) .execute(&mut conn) .await - .map_err(|e| error!("Failed to update site stats: {e}")) + .inspect_err(|e| error!("Failed to update site stats: {e}")) .ok(); let update_community_stmt = format!("update community_aggregates ca set users_active_{} = mv.count_ from community_aggregates_activity('{}') mv where ca.community_id = mv.community_id_", i.1, i.0); sql_query(update_community_stmt) .execute(&mut conn) .await - .map_err(|e| error!("Failed to update community stats: {e}")) + .inspect_err(|e| error!("Failed to update community stats: {e}")) .ok(); } @@ -424,7 +433,7 @@ async fn update_banned_when_expired(pool: &mut DbPool<'_>) { .set(person::banned.eq(false)) .execute(&mut conn) .await - .map_err(|e| error!("Failed to update person.banned when expires: {e}")) + .inspect_err(|e| error!("Failed to update person.banned when expires: {e}")) .ok(); diesel::delete( @@ -432,7 +441,7 @@ async fn update_banned_when_expired(pool: &mut DbPool<'_>) { ) .execute(&mut conn) .await - .map_err(|e| error!("Failed to remove community_ban expired rows: {e}")) + .inspect_err(|e| error!("Failed to remove community_ban expired rows: {e}")) .ok(); } Err(e) => {