Ignore old federated post edits (ref #4529) (#4586)

* Ignore old federated post edits (ref #4529)

* use filter on insert

* coalesce(updated, published)

* avoid comment conflict clause

---------

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
This commit is contained in:
Nutomic 2024-04-10 16:03:51 +02:00 committed by GitHub
parent 99d585b7be
commit 0203b62a6d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 136 additions and 42 deletions

View file

@ -29,6 +29,7 @@ use lemmy_db_schema::{
post::Post,
},
traits::Crud,
utils::naive_now,
};
use lemmy_utils::{
error::{LemmyError, LemmyErrorType},
@ -141,6 +142,7 @@ impl Object for ApubComment {
check_apub_id_valid_with_strictness(note.id.inner(), community.local, context).await?;
verify_is_remote_object(note.id.inner(), context.settings())?;
verify_person_in_community(&note.attributed_to, &community, context).await?;
let (post, _) = note.get_parents(context).await?;
let creator = note.attributed_to.dereference(context).await?;
let is_mod_or_admin = is_mod_or_admin(&mut context.pool(), &creator, community.id)
@ -184,7 +186,14 @@ impl Object for ApubComment {
language_id,
};
let parent_comment_path = parent_comment.map(|t| t.0.path);
let comment = Comment::create(&mut context.pool(), &form, parent_comment_path.as_ref()).await?;
let timestamp: DateTime<Utc> = note.updated.or(note.published).unwrap_or_else(naive_now);
let comment = Comment::insert_apub(
&mut context.pool(),
Some(timestamp),
&form,
parent_comment_path.as_ref(),
)
.await?;
Ok(comment.into())
}
}

View file

@ -175,7 +175,8 @@ impl Object for ApubCommunity {
let languages =
LanguageTag::to_language_id_multiple(group.language, &mut context.pool()).await?;
let community = Community::create(&mut context.pool(), &form).await?;
let timestamp = group.updated.or(group.published).unwrap_or_else(naive_now);
let community = Community::insert_apub(&mut context.pool(), timestamp, &form).await?;
CommunityLanguage::update(&mut context.pool(), languages, community.id).await?;
let community: ApubCommunity = community.into();

View file

@ -41,6 +41,7 @@ use lemmy_db_schema::{
post::{Post, PostInsertForm, PostUpdateForm},
},
traits::Crud,
utils::naive_now,
};
use lemmy_db_views_actor::structs::CommunityModeratorView;
use lemmy_utils::{
@ -270,7 +271,8 @@ impl Object for ApubPost {
.build()
};
let post = Post::create(&mut context.pool(), &form).await?;
let timestamp = page.updated.or(page.published).unwrap_or_else(naive_now);
let post = Post::insert_apub(&mut context.pool(), timestamp, &form).await?;
generate_post_link_metadata(
post.clone(),

View file

@ -23,6 +23,7 @@ use lemmy_db_schema::{
private_message::{PrivateMessage, PrivateMessageInsertForm},
},
traits::Crud,
utils::naive_now,
};
use lemmy_utils::{
error::{LemmyError, LemmyErrorType},
@ -142,7 +143,8 @@ impl Object for ApubPrivateMessage {
ap_id: Some(note.id.into()),
local: Some(false),
};
let pm = PrivateMessage::create(&mut context.pool(), &form).await?;
let timestamp = note.updated.or(note.published).unwrap_or_else(naive_now);
let pm = PrivateMessage::insert_apub(&mut context.pool(), timestamp, &form).await?;
Ok(pm.into())
}
}

View file

@ -1,4 +1,5 @@
use crate::{
diesel::DecoratableTarget,
newtypes::{CommentId, DbUrl, PersonId},
schema::comment,
source::comment::{
@ -11,8 +12,9 @@ use crate::{
CommentUpdateForm,
},
traits::{Crud, Likeable, Saveable},
utils::{get_conn, naive_now, DbPool, DELETED_REPLACEMENT_TEXT},
utils::{functions::coalesce, get_conn, naive_now, DbPool, DELETED_REPLACEMENT_TEXT},
};
use chrono::{DateTime, Utc};
use diesel::{
dsl::{insert_into, sql_query},
result::Error,
@ -59,6 +61,15 @@ impl Comment {
pool: &mut DbPool<'_>,
comment_form: &CommentInsertForm,
parent_path: Option<&Ltree>,
) -> Result<Comment, Error> {
Self::insert_apub(pool, None, comment_form, parent_path).await
}
pub async fn insert_apub(
pool: &mut DbPool<'_>,
timestamp: Option<DateTime<Utc>>,
comment_form: &CommentInsertForm,
parent_path: Option<&Ltree>,
) -> Result<Comment, Error> {
let conn = &mut get_conn(pool).await?;
@ -67,13 +78,21 @@ impl Comment {
.run(|conn| {
Box::pin(async move {
// Insert, to get the id
let inserted_comment = insert_into(comment::table)
.values(comment_form)
.on_conflict(comment::ap_id)
.do_update()
.set(comment_form)
.get_result::<Self>(conn)
.await?;
let inserted_comment = if let Some(timestamp) = timestamp {
insert_into(comment::table)
.values(comment_form)
.on_conflict(comment::ap_id)
.filter_target(coalesce(comment::updated, comment::published).lt(timestamp))
.do_update()
.set(comment_form)
.get_result::<Self>(conn)
.await?
} else {
insert_into(comment::table)
.values(comment_form)
.get_result::<Self>(conn)
.await?
};
let comment_id = inserted_comment.id;
@ -129,6 +148,7 @@ where ca.comment_id = c.id"
})
.await
}
pub async fn read_from_apub_id(
pool: &mut DbPool<'_>,
object_id: Url,

View file

@ -1,4 +1,5 @@
use crate::{
diesel::DecoratableTarget,
newtypes::{CommunityId, DbUrl, PersonId},
schema::{community, community_follower, instance},
source::{
@ -17,9 +18,14 @@ use crate::{
post::Post,
},
traits::{ApubActor, Bannable, Crud, Followable, Joinable},
utils::{functions::lower, get_conn, DbPool},
utils::{
functions::{coalesce, lower},
get_conn,
DbPool,
},
SubscribedType,
};
use chrono::{DateTime, Utc};
use diesel::{
deserialize,
dsl,
@ -44,25 +50,15 @@ impl Crud for Community {
type IdType = CommunityId;
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
let is_new_community = match &form.actor_id {
Some(id) => Community::read_from_apub_id(pool, id).await?.is_none(),
None => true,
};
let conn = &mut get_conn(pool).await?;
// Can't do separate insert/update commands because InsertForm/UpdateForm aren't convertible
let community_ = insert_into(community::table)
.values(form)
.on_conflict(community::actor_id)
.do_update()
.set(form)
.get_result::<Self>(conn)
.await?;
// Initialize languages for new community
if is_new_community {
CommunityLanguage::update(pool, vec![], community_.id).await?;
}
CommunityLanguage::update(pool, vec![], community_.id).await?;
Ok(community_)
}
@ -116,6 +112,35 @@ pub enum CollectionType {
}
impl Community {
pub async fn insert_apub(
pool: &mut DbPool<'_>,
timestamp: DateTime<Utc>,
form: &CommunityInsertForm,
) -> Result<Self, Error> {
let is_new_community = match &form.actor_id {
Some(id) => Community::read_from_apub_id(pool, id).await?.is_none(),
None => true,
};
let conn = &mut get_conn(pool).await?;
// Can't do separate insert/update commands because InsertForm/UpdateForm aren't convertible
let community_ = insert_into(community::table)
.values(form)
.on_conflict(community::actor_id)
.filter_target(coalesce(community::updated, community::published).lt(timestamp))
.do_update()
.set(form)
.get_result::<Self>(conn)
.await?;
// Initialize languages for new community
if is_new_community {
CommunityLanguage::update(pool, vec![], community_.id).await?;
}
Ok(community_)
}
/// Get the community which has a given moderators or featured url, also return the collection type
pub async fn get_by_collection_url(
pool: &mut DbPool<'_>,

View file

@ -27,8 +27,15 @@ use crate::{
},
};
use ::url::Url;
use chrono::Utc;
use diesel::{dsl::insert_into, result::Error, ExpressionMethods, QueryDsl, TextExpressionMethods};
use chrono::{DateTime, Utc};
use diesel::{
dsl::insert_into,
result::Error,
DecoratableTarget,
ExpressionMethods,
QueryDsl,
TextExpressionMethods,
};
use diesel_async::RunQueryDsl;
use std::collections::HashSet;
@ -42,9 +49,6 @@ impl Crud for Post {
let conn = &mut get_conn(pool).await?;
insert_into(post::table)
.values(form)
.on_conflict(post::ap_id)
.do_update()
.set(form)
.get_result::<Self>(conn)
.await
}
@ -63,6 +67,22 @@ impl Crud for Post {
}
impl Post {
pub async fn insert_apub(
pool: &mut DbPool<'_>,
timestamp: DateTime<Utc>,
form: &PostInsertForm,
) -> Result<Self, Error> {
let conn = &mut get_conn(pool).await?;
insert_into(post::table)
.values(form)
.on_conflict(post::ap_id)
.filter_target(coalesce(post::updated, post::published).lt(timestamp))
.do_update()
.set(form)
.get_result::<Self>(conn)
.await
}
pub async fn list_for_community(
pool: &mut DbPool<'_>,
the_community_id: CommunityId,

View file

@ -1,10 +1,12 @@
use crate::{
diesel::DecoratableTarget,
newtypes::{DbUrl, PersonId, PrivateMessageId},
schema::private_message::dsl::{ap_id, private_message, read, recipient_id},
schema::private_message,
source::private_message::{PrivateMessage, PrivateMessageInsertForm, PrivateMessageUpdateForm},
traits::Crud,
utils::{get_conn, DbPool},
utils::{functions::coalesce, get_conn, DbPool},
};
use chrono::{DateTime, Utc};
use diesel::{dsl::insert_into, result::Error, ExpressionMethods, QueryDsl};
use diesel_async::RunQueryDsl;
use lemmy_utils::error::LemmyError;
@ -18,11 +20,8 @@ impl Crud for PrivateMessage {
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
let conn = &mut get_conn(pool).await?;
insert_into(private_message)
insert_into(private_message::table)
.values(form)
.on_conflict(ap_id)
.do_update()
.set(form)
.get_result::<Self>(conn)
.await
}
@ -33,7 +32,7 @@ impl Crud for PrivateMessage {
form: &Self::UpdateForm,
) -> Result<Self, Error> {
let conn = &mut get_conn(pool).await?;
diesel::update(private_message.find(private_message_id))
diesel::update(private_message::table.find(private_message_id))
.set(form)
.get_result::<Self>(conn)
.await
@ -41,17 +40,33 @@ impl Crud for PrivateMessage {
}
impl PrivateMessage {
pub async fn insert_apub(
pool: &mut DbPool<'_>,
timestamp: DateTime<Utc>,
form: &PrivateMessageInsertForm,
) -> Result<Self, Error> {
let conn = &mut get_conn(pool).await?;
insert_into(private_message::table)
.values(form)
.on_conflict(private_message::ap_id)
.filter_target(coalesce(private_message::updated, private_message::published).lt(timestamp))
.do_update()
.set(form)
.get_result::<Self>(conn)
.await
}
pub async fn mark_all_as_read(
pool: &mut DbPool<'_>,
for_recipient_id: PersonId,
) -> Result<Vec<PrivateMessage>, Error> {
let conn = &mut get_conn(pool).await?;
diesel::update(
private_message
.filter(recipient_id.eq(for_recipient_id))
.filter(read.eq(false)),
private_message::table
.filter(private_message::recipient_id.eq(for_recipient_id))
.filter(private_message::read.eq(false)),
)
.set(read.eq(true))
.set(private_message::read.eq(true))
.get_results::<Self>(conn)
.await
}
@ -63,8 +78,8 @@ impl PrivateMessage {
let conn = &mut get_conn(pool).await?;
let object_id: DbUrl = object_id.into();
Ok(
private_message
.filter(ap_id.eq(object_id))
private_message::table
.filter(private_message::ap_id.eq(object_id))
.first::<PrivateMessage>(conn)
.await
.ok()