Rewrite some federation actions to remove Perform/SendActivity (ref #3670) (#3758)

This commit is contained in:
Nutomic 2023-08-01 15:53:36 +02:00 committed by GitHub
parent 05a7fced65
commit d82194cfe9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 460 additions and 521 deletions

View file

@ -1,9 +1,10 @@
use crate::Perform; use activitypub_federation::config::Data;
use actix_web::web::Data; use actix_web::web::Json;
use lemmy_api_common::{ use lemmy_api_common::{
build_response::build_comment_response, build_response::build_comment_response,
comment::{CommentResponse, CreateCommentLike}, comment::{CommentResponse, CreateCommentLike},
context::LemmyContext, context::LemmyContext,
send_activity::{ActivityChannel, SendActivityData},
utils::{check_community_ban, check_downvotes_enabled, local_user_view_from_jwt}, utils::{check_community_ban, check_downvotes_enabled, local_user_view_from_jwt},
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
@ -17,70 +18,80 @@ use lemmy_db_schema::{
}; };
use lemmy_db_views::structs::{CommentView, LocalUserView}; use lemmy_db_views::structs::{CommentView, LocalUserView};
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use std::ops::Deref;
#[async_trait::async_trait(?Send)] #[tracing::instrument(skip(context))]
impl Perform for CreateCommentLike { pub async fn like_comment(
type Response = CommentResponse; data: Json<CreateCommentLike>,
context: Data<LemmyContext>,
) -> Result<Json<CommentResponse>, LemmyError> {
let local_site = LocalSite::read(&mut context.pool()).await?;
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
#[tracing::instrument(skip(context))] let mut recipient_ids = Vec::<LocalUserId>::new();
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommentResponse, LemmyError> {
let data: &CreateCommentLike = self;
let local_site = LocalSite::read(&mut context.pool()).await?;
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let mut recipient_ids = Vec::<LocalUserId>::new(); // Don't do a downvote if site has downvotes disabled
check_downvotes_enabled(data.score, &local_site)?;
// Don't do a downvote if site has downvotes disabled let comment_id = data.comment_id;
check_downvotes_enabled(data.score, &local_site)?; let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
let comment_id = data.comment_id; check_community_ban(
let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?; local_user_view.person.id,
orig_comment.community.id,
&mut context.pool(),
)
.await?;
check_community_ban( // Add parent poster or commenter to recipients
local_user_view.person.id, let comment_reply = CommentReply::read_by_comment(&mut context.pool(), comment_id).await;
orig_comment.community.id, if let Ok(reply) = comment_reply {
&mut context.pool(), let recipient_id = reply.recipient_id;
) if let Ok(local_recipient) = LocalUserView::read_person(&mut context.pool(), recipient_id).await
.await?; {
recipient_ids.push(local_recipient.local_user.id);
// Add parent poster or commenter to recipients
let comment_reply = CommentReply::read_by_comment(&mut context.pool(), comment_id).await;
if let Ok(reply) = comment_reply {
let recipient_id = reply.recipient_id;
if let Ok(local_recipient) =
LocalUserView::read_person(&mut context.pool(), recipient_id).await
{
recipient_ids.push(local_recipient.local_user.id);
}
} }
}
let like_form = CommentLikeForm { let like_form = CommentLikeForm {
comment_id: data.comment_id, comment_id: data.comment_id,
post_id: orig_comment.post.id, post_id: orig_comment.post.id,
person_id: local_user_view.person.id, person_id: local_user_view.person.id,
score: data.score, score: data.score,
}; };
// Remove any likes first // Remove any likes first
let person_id = local_user_view.person.id; let person_id = local_user_view.person.id;
CommentLike::remove(&mut context.pool(), person_id, comment_id).await?; CommentLike::remove(&mut context.pool(), person_id, comment_id).await?;
// Only add the like if the score isnt 0 // Only add the like if the score isnt 0
let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1); let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
if do_add { if do_add {
CommentLike::like(&mut context.pool(), &like_form) CommentLike::like(&mut context.pool(), &like_form)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntLikeComment)?; .with_lemmy_type(LemmyErrorType::CouldntLikeComment)?;
} }
ActivityChannel::submit_activity(
SendActivityData::LikePostOrComment(
orig_comment.comment.ap_id,
local_user_view.person.clone(),
orig_comment.community,
data.score,
),
&context,
)
.await?;
Ok(Json(
build_comment_response( build_comment_response(
context, context.deref(),
comment_id, comment_id,
Some(local_user_view), Some(local_user_view),
None, None,
recipient_ids, recipient_ids,
) )
.await .await?,
} ))
} }

View file

@ -1,9 +1,10 @@
use crate::Perform; use activitypub_federation::config::Data;
use actix_web::web::Data; use actix_web::web::Json;
use lemmy_api_common::{ use lemmy_api_common::{
build_response::build_post_response, build_response::build_post_response,
context::LemmyContext, context::LemmyContext,
post::{CreatePostLike, PostResponse}, post::{CreatePostLike, PostResponse},
send_activity::{ActivityChannel, SendActivityData},
utils::{ utils::{
check_community_ban, check_community_ban,
check_community_deleted_or_removed, check_community_deleted_or_removed,
@ -14,66 +15,78 @@ use lemmy_api_common::{
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
community::Community,
local_site::LocalSite, local_site::LocalSite,
post::{Post, PostLike, PostLikeForm}, post::{Post, PostLike, PostLikeForm},
}, },
traits::{Crud, Likeable}, traits::{Crud, Likeable},
}; };
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use std::ops::Deref;
#[async_trait::async_trait(?Send)] #[tracing::instrument(skip(context))]
impl Perform for CreatePostLike { pub async fn like_post(
type Response = PostResponse; data: Json<CreatePostLike>,
context: Data<LemmyContext>,
) -> Result<Json<PostResponse>, LemmyError> {
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
let local_site = LocalSite::read(&mut context.pool()).await?;
#[tracing::instrument(skip(context))] // Don't do a downvote if site has downvotes disabled
async fn perform(&self, context: &Data<LemmyContext>) -> Result<PostResponse, LemmyError> { check_downvotes_enabled(data.score, &local_site)?;
let data: &CreatePostLike = self;
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let local_site = LocalSite::read(&mut context.pool()).await?;
// Don't do a downvote if site has downvotes disabled // Check for a community ban
check_downvotes_enabled(data.score, &local_site)?; let post_id = data.post_id;
let post = Post::read(&mut context.pool(), post_id).await?;
// Check for a community ban check_community_ban(
let post_id = data.post_id; local_user_view.person.id,
let post = Post::read(&mut context.pool(), post_id).await?; post.community_id,
&mut context.pool(),
)
.await?;
check_community_deleted_or_removed(post.community_id, &mut context.pool()).await?;
check_community_ban( let like_form = PostLikeForm {
local_user_view.person.id, post_id: data.post_id,
post.community_id, person_id: local_user_view.person.id,
&mut context.pool(), score: data.score,
) };
.await?;
check_community_deleted_or_removed(post.community_id, &mut context.pool()).await?;
let like_form = PostLikeForm { // Remove any likes first
post_id: data.post_id, let person_id = local_user_view.person.id;
person_id: local_user_view.person.id,
score: data.score,
};
// Remove any likes first PostLike::remove(&mut context.pool(), person_id, post_id).await?;
let person_id = local_user_view.person.id;
PostLike::remove(&mut context.pool(), person_id, post_id).await?; // Only add the like if the score isnt 0
let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
if do_add {
PostLike::like(&mut context.pool(), &like_form)
.await
.with_lemmy_type(LemmyErrorType::CouldntLikePost)?;
}
// Only add the like if the score isnt 0 // Mark the post as read
let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1); mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
if do_add {
PostLike::like(&mut context.pool(), &like_form)
.await
.with_lemmy_type(LemmyErrorType::CouldntLikePost)?;
}
// Mark the post as read ActivityChannel::submit_activity(
mark_post_as_read(person_id, post_id, &mut context.pool()).await?; SendActivityData::LikePostOrComment(
post.ap_id,
local_user_view.person.clone(),
Community::read(&mut context.pool(), post.community_id).await?,
data.score,
),
&context,
)
.await?;
Ok(Json(
build_post_response( build_post_response(
context, context.deref(),
post.community_id, post.community_id,
local_user_view.person.id, local_user_view.person.id,
post_id, post_id,
) )
.await .await?,
} ))
} }

View file

@ -1,6 +1,6 @@
mod feature; pub mod feature;
mod get_link_metadata; pub mod get_link_metadata;
mod like; pub mod like;
mod lock; pub mod lock;
mod mark_read; pub mod mark_read;
mod save; pub mod save;

View file

@ -1,7 +1,10 @@
use crate::context::LemmyContext; use crate::context::LemmyContext;
use activitypub_federation::config::Data; use activitypub_federation::config::Data;
use futures::future::BoxFuture; use futures::future::BoxFuture;
use lemmy_db_schema::source::{comment::Comment, post::Post}; use lemmy_db_schema::{
newtypes::DbUrl,
source::{comment::Comment, community::Community, person::Person, post::Post},
};
use lemmy_utils::{error::LemmyResult, SYNCHRONOUS_FEDERATION}; use lemmy_utils::{error::LemmyResult, SYNCHRONOUS_FEDERATION};
use once_cell::sync::{Lazy, OnceCell}; use once_cell::sync::{Lazy, OnceCell};
use tokio::{ use tokio::{
@ -22,7 +25,12 @@ pub static MATCH_OUTGOING_ACTIVITIES: OnceCell<MatchOutgoingActivitiesBoxed> = O
#[derive(Debug)] #[derive(Debug)]
pub enum SendActivityData { pub enum SendActivityData {
CreatePost(Post), CreatePost(Post),
UpdatePost(Post),
CreateComment(Comment), CreateComment(Comment),
DeleteComment(Comment, Person, Community),
RemoveComment(Comment, Person, Community, Option<String>),
UpdateComment(Comment),
LikePostOrComment(DbUrl, Person, Community, i16),
} }
// TODO: instead of static, move this into LemmyContext. make sure that stopping the process with // TODO: instead of static, move this into LemmyContext. make sure that stopping the process with

View file

@ -36,7 +36,6 @@ use lemmy_utils::{
validation::is_valid_body_field, validation::is_valid_body_field,
}, },
}; };
use std::ops::Deref;
const MAX_COMMENT_DEPTH_LIMIT: usize = 100; const MAX_COMMENT_DEPTH_LIMIT: usize = 100;
@ -196,7 +195,7 @@ pub async fn create_comment(
Ok(Json( Ok(Json(
build_comment_response( build_comment_response(
context.deref(), &context,
inserted_comment.id, inserted_comment.id,
Some(local_user_view), Some(local_user_view),
data.form_id.clone(), data.form_id.clone(),

View file

@ -1,9 +1,10 @@
use crate::PerformCrud; use activitypub_federation::config::Data;
use actix_web::web::Data; use actix_web::web::Json;
use lemmy_api_common::{ use lemmy_api_common::{
build_response::{build_comment_response, send_local_notifs}, build_response::{build_comment_response, send_local_notifs},
comment::{CommentResponse, DeleteComment}, comment::{CommentResponse, DeleteComment},
context::LemmyContext, context::LemmyContext,
send_activity::{ActivityChannel, SendActivityData},
utils::{check_community_ban, local_user_view_from_jwt}, utils::{check_community_ban, local_user_view_from_jwt},
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
@ -15,66 +16,75 @@ use lemmy_db_schema::{
}; };
use lemmy_db_views::structs::CommentView; use lemmy_db_views::structs::CommentView;
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use std::ops::Deref;
#[async_trait::async_trait(?Send)] #[tracing::instrument(skip(context))]
impl PerformCrud for DeleteComment { pub async fn delete_comment(
type Response = CommentResponse; data: Json<DeleteComment>,
context: Data<LemmyContext>,
) -> Result<Json<CommentResponse>, LemmyError> {
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
#[tracing::instrument(skip(context))] let comment_id = data.comment_id;
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommentResponse, LemmyError> { let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
let data: &DeleteComment = self;
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let comment_id = data.comment_id; // Dont delete it if its already been deleted.
let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?; if orig_comment.comment.deleted == data.deleted {
return Err(LemmyErrorType::CouldntUpdateComment)?;
}
// Dont delete it if its already been deleted. check_community_ban(
if orig_comment.comment.deleted == data.deleted { local_user_view.person.id,
return Err(LemmyErrorType::CouldntUpdateComment)?; orig_comment.community.id,
} &mut context.pool(),
)
.await?;
check_community_ban( // Verify that only the creator can delete
local_user_view.person.id, if local_user_view.person.id != orig_comment.creator.id {
orig_comment.community.id, return Err(LemmyErrorType::NoCommentEditAllowed)?;
&mut context.pool(), }
)
.await?;
// Verify that only the creator can delete // Do the delete
if local_user_view.person.id != orig_comment.creator.id { let deleted = data.deleted;
return Err(LemmyErrorType::NoCommentEditAllowed)?; let updated_comment = Comment::update(
} &mut context.pool(),
comment_id,
&CommentUpdateForm::builder().deleted(Some(deleted)).build(),
)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
// Do the delete let post_id = updated_comment.post_id;
let deleted = data.deleted; let post = Post::read(&mut context.pool(), post_id).await?;
let updated_comment = Comment::update( let recipient_ids = send_local_notifs(
&mut context.pool(), vec![],
comment_id, &updated_comment,
&CommentUpdateForm::builder().deleted(Some(deleted)).build(), &local_user_view.person,
) &post,
.await false,
.with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?; &context,
)
.await?;
let updated_comment_id = updated_comment.id;
let post_id = updated_comment.post_id; ActivityChannel::submit_activity(
let post = Post::read(&mut context.pool(), post_id).await?; SendActivityData::DeleteComment(
let recipient_ids = send_local_notifs( updated_comment,
vec![], local_user_view.person.clone(),
&updated_comment, orig_comment.community,
&local_user_view.person, ),
&post, &context,
false, )
context, .await?;
)
.await?;
Ok(Json(
build_comment_response( build_comment_response(
context.deref(), &context,
updated_comment.id, updated_comment_id,
Some(local_user_view), Some(local_user_view),
None, None,
recipient_ids, recipient_ids,
) )
.await .await?,
} ))
} }

View file

@ -7,7 +7,6 @@ use lemmy_api_common::{
}; };
use lemmy_db_schema::source::local_site::LocalSite; use lemmy_db_schema::source::local_site::LocalSite;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::LemmyError;
use std::ops::Deref;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
pub async fn get_comment( pub async fn get_comment(
@ -20,6 +19,6 @@ pub async fn get_comment(
check_private_instance(&local_user_view, &local_site)?; check_private_instance(&local_user_view, &local_site)?;
Ok(Json( Ok(Json(
build_comment_response(context.deref(), data.id, local_user_view, None, vec![]).await?, build_comment_response(&context, data.id, local_user_view, None, vec![]).await?,
)) ))
} }

View file

@ -1,9 +1,10 @@
use crate::PerformCrud; use activitypub_federation::config::Data;
use actix_web::web::Data; use actix_web::web::Json;
use lemmy_api_common::{ use lemmy_api_common::{
build_response::{build_comment_response, send_local_notifs}, build_response::{build_comment_response, send_local_notifs},
comment::{CommentResponse, RemoveComment}, comment::{CommentResponse, RemoveComment},
context::LemmyContext, context::LemmyContext,
send_activity::{ActivityChannel, SendActivityData},
utils::{check_community_ban, is_mod_or_admin, local_user_view_from_jwt}, utils::{check_community_ban, is_mod_or_admin, local_user_view_from_jwt},
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
@ -16,73 +17,83 @@ use lemmy_db_schema::{
}; };
use lemmy_db_views::structs::CommentView; use lemmy_db_views::structs::CommentView;
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use std::ops::Deref;
#[async_trait::async_trait(?Send)] #[tracing::instrument(skip(context))]
impl PerformCrud for RemoveComment { pub async fn remove_comment(
type Response = CommentResponse; data: Json<RemoveComment>,
context: Data<LemmyContext>,
) -> Result<Json<CommentResponse>, LemmyError> {
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
#[tracing::instrument(skip(context))] let comment_id = data.comment_id;
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommentResponse, LemmyError> { let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
let data: &RemoveComment = self;
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let comment_id = data.comment_id; check_community_ban(
let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?; local_user_view.person.id,
orig_comment.community.id,
&mut context.pool(),
)
.await?;
check_community_ban( // Verify that only a mod or admin can remove
local_user_view.person.id, is_mod_or_admin(
orig_comment.community.id, &mut context.pool(),
&mut context.pool(), local_user_view.person.id,
) orig_comment.community.id,
.await?; )
.await?;
// Verify that only a mod or admin can remove // Do the remove
is_mod_or_admin( let removed = data.removed;
&mut context.pool(), let updated_comment = Comment::update(
local_user_view.person.id, &mut context.pool(),
orig_comment.community.id, comment_id,
) &CommentUpdateForm::builder().removed(Some(removed)).build(),
.await?; )
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
// Do the remove // Mod tables
let removed = data.removed; let form = ModRemoveCommentForm {
let updated_comment = Comment::update( mod_person_id: local_user_view.person.id,
&mut context.pool(), comment_id: data.comment_id,
comment_id, removed: Some(removed),
&CommentUpdateForm::builder().removed(Some(removed)).build(), reason: data.reason.clone(),
) };
.await ModRemoveComment::create(&mut context.pool(), &form).await?;
.with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
// Mod tables let post_id = updated_comment.post_id;
let form = ModRemoveCommentForm { let post = Post::read(&mut context.pool(), post_id).await?;
mod_person_id: local_user_view.person.id, let recipient_ids = send_local_notifs(
comment_id: data.comment_id, vec![],
removed: Some(removed), &updated_comment,
reason: data.reason.clone(), &local_user_view.person.clone(),
}; &post,
ModRemoveComment::create(&mut context.pool(), &form).await?; false,
&context,
)
.await?;
let updated_comment_id = updated_comment.id;
let post_id = updated_comment.post_id; ActivityChannel::submit_activity(
let post = Post::read(&mut context.pool(), post_id).await?; SendActivityData::RemoveComment(
let recipient_ids = send_local_notifs( updated_comment,
vec![], local_user_view.person.clone(),
&updated_comment, orig_comment.community,
&local_user_view.person.clone(), data.reason.clone(),
&post, ),
false, &context,
context, )
) .await?;
.await?;
Ok(Json(
build_comment_response( build_comment_response(
context.deref(), &context,
updated_comment.id, updated_comment_id,
Some(local_user_view), Some(local_user_view),
None, None,
recipient_ids, recipient_ids,
) )
.await .await?,
} ))
} }

View file

@ -1,9 +1,10 @@
use crate::PerformCrud; use activitypub_federation::config::Data;
use actix_web::web::Data; use actix_web::web::Json;
use lemmy_api_common::{ use lemmy_api_common::{
build_response::{build_comment_response, send_local_notifs}, build_response::{build_comment_response, send_local_notifs},
comment::{CommentResponse, EditComment}, comment::{CommentResponse, EditComment},
context::LemmyContext, context::LemmyContext,
send_activity::{ActivityChannel, SendActivityData},
utils::{ utils::{
check_community_ban, check_community_ban,
local_site_to_slur_regex, local_site_to_slur_regex,
@ -29,79 +30,83 @@ use lemmy_utils::{
validation::is_valid_body_field, validation::is_valid_body_field,
}, },
}; };
use std::ops::Deref;
#[async_trait::async_trait(?Send)] #[tracing::instrument(skip(context))]
impl PerformCrud for EditComment { pub async fn update_comment(
type Response = CommentResponse; data: Json<EditComment>,
context: Data<LemmyContext>,
) -> Result<Json<CommentResponse>, LemmyError> {
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
let local_site = LocalSite::read(&mut context.pool()).await?;
#[tracing::instrument(skip(context))] let comment_id = data.comment_id;
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommentResponse, LemmyError> { let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
let data: &EditComment = self;
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let local_site = LocalSite::read(&mut context.pool()).await?;
let comment_id = data.comment_id; check_community_ban(
let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?; local_user_view.person.id,
orig_comment.community.id,
&mut context.pool(),
)
.await?;
check_community_ban( // Verify that only the creator can edit
local_user_view.person.id, if local_user_view.person.id != orig_comment.creator.id {
orig_comment.community.id, return Err(LemmyErrorType::NoCommentEditAllowed)?;
&mut context.pool(), }
)
.await?;
// Verify that only the creator can edit let language_id = data.language_id;
if local_user_view.person.id != orig_comment.creator.id { CommunityLanguage::is_allowed_community_language(
return Err(LemmyErrorType::NoCommentEditAllowed)?; &mut context.pool(),
} language_id,
orig_comment.community.id,
)
.await?;
let language_id = self.language_id; // Update the Content
CommunityLanguage::is_allowed_community_language( let content = data
&mut context.pool(), .content
language_id, .as_ref()
orig_comment.community.id, .map(|c| remove_slurs(c, &local_site_to_slur_regex(&local_site)));
) is_valid_body_field(&content, false)?;
.await?; let content = sanitize_html_opt(&content);
// Update the Content let comment_id = data.comment_id;
let content = data let form = CommentUpdateForm::builder()
.content .content(content)
.as_ref() .language_id(data.language_id)
.map(|c| remove_slurs(c, &local_site_to_slur_regex(&local_site))); .updated(Some(Some(naive_now())))
is_valid_body_field(&content, false)?; .build();
let content = sanitize_html_opt(&content); let updated_comment = Comment::update(&mut context.pool(), comment_id, &form)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
let comment_id = data.comment_id; // Do the mentions / recipients
let form = CommentUpdateForm::builder() let updated_comment_content = updated_comment.content.clone();
.content(content) let mentions = scrape_text_for_mentions(&updated_comment_content);
.language_id(data.language_id) let recipient_ids = send_local_notifs(
.updated(Some(Some(naive_now()))) mentions,
.build(); &updated_comment,
let updated_comment = Comment::update(&mut context.pool(), comment_id, &form) &local_user_view.person,
.await &orig_comment.post,
.with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?; false,
&context,
)
.await?;
// Do the mentions / recipients ActivityChannel::submit_activity(
let updated_comment_content = updated_comment.content.clone(); SendActivityData::UpdateComment(updated_comment.clone()),
let mentions = scrape_text_for_mentions(&updated_comment_content); &context,
let recipient_ids = send_local_notifs( )
mentions, .await?;
&updated_comment,
&local_user_view.person,
&orig_comment.post,
false,
context,
)
.await?;
Ok(Json(
build_comment_response( build_comment_response(
context.deref(), &context,
updated_comment.id, updated_comment.id,
Some(local_user_view), Some(local_user_view),
self.form_id.clone(), data.form_id.clone(),
recipient_ids, recipient_ids,
) )
.await .await?,
} ))
} }

View file

@ -1,10 +1,11 @@
use crate::PerformCrud; use activitypub_federation::config::Data;
use actix_web::web::Data; use actix_web::web::Json;
use lemmy_api_common::{ use lemmy_api_common::{
build_response::build_post_response, build_response::build_post_response,
context::LemmyContext, context::LemmyContext,
post::{EditPost, PostResponse}, post::{EditPost, PostResponse},
request::fetch_site_data, request::fetch_site_data,
send_activity::{ActivityChannel, SendActivityData},
utils::{ utils::{
check_community_ban, check_community_ban,
local_site_to_slur_regex, local_site_to_slur_regex,
@ -28,95 +29,97 @@ use lemmy_utils::{
validation::{check_url_scheme, clean_url_params, is_valid_body_field, is_valid_post_title}, validation::{check_url_scheme, clean_url_params, is_valid_body_field, is_valid_post_title},
}, },
}; };
use std::ops::Deref;
#[async_trait::async_trait(?Send)] #[tracing::instrument(skip(context))]
impl PerformCrud for EditPost { pub async fn update_post(
type Response = PostResponse; data: Json<EditPost>,
context: Data<LemmyContext>,
) -> Result<Json<PostResponse>, LemmyError> {
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
let local_site = LocalSite::read(&mut context.pool()).await?;
#[tracing::instrument(skip(context))] let data_url = data.url.as_ref();
async fn perform(&self, context: &Data<LemmyContext>) -> Result<PostResponse, LemmyError> {
let data: &EditPost = self;
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let local_site = LocalSite::read(&mut context.pool()).await?;
let data_url = data.url.as_ref(); // TODO No good way to handle a clear.
// Issue link: https://github.com/LemmyNet/lemmy/issues/2287
let url = Some(data_url.map(clean_url_params).map(Into::into));
// TODO No good way to handle a clear. let slur_regex = local_site_to_slur_regex(&local_site);
// Issue link: https://github.com/LemmyNet/lemmy/issues/2287 check_slurs_opt(&data.name, &slur_regex)?;
let url = Some(data_url.map(clean_url_params).map(Into::into)); check_slurs_opt(&data.body, &slur_regex)?;
let slur_regex = local_site_to_slur_regex(&local_site); if let Some(name) = &data.name {
check_slurs_opt(&data.name, &slur_regex)?; is_valid_post_title(name)?;
check_slurs_opt(&data.body, &slur_regex)?; }
if let Some(name) = &data.name { is_valid_body_field(&data.body, true)?;
is_valid_post_title(name)?; check_url_scheme(&data.url)?;
}
is_valid_body_field(&data.body, true)?; let post_id = data.post_id;
check_url_scheme(&data.url)?; let orig_post = Post::read(&mut context.pool(), post_id).await?;
let post_id = data.post_id; check_community_ban(
let orig_post = Post::read(&mut context.pool(), post_id).await?; local_user_view.person.id,
orig_post.community_id,
&mut context.pool(),
)
.await?;
check_community_ban( // Verify that only the creator can edit
local_user_view.person.id, if !Post::is_post_creator(local_user_view.person.id, orig_post.creator_id) {
orig_post.community_id, return Err(LemmyErrorType::NoPostEditAllowed)?;
&mut context.pool(), }
)
.await?;
// Verify that only the creator can edit // Fetch post links and Pictrs cached image
if !Post::is_post_creator(local_user_view.person.id, orig_post.creator_id) { let data_url = data.url.as_ref();
return Err(LemmyErrorType::NoPostEditAllowed)?; let (metadata_res, thumbnail_url) =
} fetch_site_data(context.client(), context.settings(), data_url, true).await;
let (embed_title, embed_description, embed_video_url) = metadata_res
.map(|u| (Some(u.title), Some(u.description), Some(u.embed_video_url)))
.unwrap_or_default();
// Fetch post links and Pictrs cached image let name = sanitize_html_opt(&data.name);
let data_url = data.url.as_ref(); let body = sanitize_html_opt(&data.body);
let (metadata_res, thumbnail_url) = let body = diesel_option_overwrite(body);
fetch_site_data(context.client(), context.settings(), data_url, true).await; let embed_title = embed_title.map(|e| sanitize_html_opt(&e));
let (embed_title, embed_description, embed_video_url) = metadata_res let embed_description = embed_description.map(|e| sanitize_html_opt(&e));
.map(|u| (Some(u.title), Some(u.description), Some(u.embed_video_url)))
.unwrap_or_default();
let name = sanitize_html_opt(&data.name); let language_id = data.language_id;
let body = sanitize_html_opt(&data.body); CommunityLanguage::is_allowed_community_language(
let body = diesel_option_overwrite(body); &mut context.pool(),
let embed_title = embed_title.map(|e| sanitize_html_opt(&e)); language_id,
let embed_description = embed_description.map(|e| sanitize_html_opt(&e)); orig_post.community_id,
)
.await?;
let language_id = self.language_id; let post_form = PostUpdateForm::builder()
CommunityLanguage::is_allowed_community_language( .name(name)
&mut context.pool(), .url(url)
language_id, .body(body)
orig_post.community_id, .nsfw(data.nsfw)
) .embed_title(embed_title)
.await?; .embed_description(embed_description)
.embed_video_url(embed_video_url)
.language_id(data.language_id)
.thumbnail_url(Some(thumbnail_url))
.updated(Some(Some(naive_now())))
.build();
let post_form = PostUpdateForm::builder() let post_id = data.post_id;
.name(name) let updated_post = Post::update(&mut context.pool(), post_id, &post_form)
.url(url) .await
.body(body) .with_lemmy_type(LemmyErrorType::CouldntUpdatePost)?;
.nsfw(data.nsfw)
.embed_title(embed_title)
.embed_description(embed_description)
.embed_video_url(embed_video_url)
.language_id(data.language_id)
.thumbnail_url(Some(thumbnail_url))
.updated(Some(Some(naive_now())))
.build();
let post_id = data.post_id; ActivityChannel::submit_activity(SendActivityData::UpdatePost(updated_post), &context).await?;
Post::update(&mut context.pool(), post_id, &post_form)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdatePost)?;
Ok(Json(
build_post_response( build_post_response(
context, context.deref(),
orig_post.community_id, orig_post.community_id,
local_user_view.person.id, local_user_view.person.id,
post_id, post_id,
) )
.await .await?,
} ))
} }

View file

@ -14,7 +14,6 @@ use crate::{
activities::{create_or_update::note::CreateOrUpdateNote, CreateOrUpdateType}, activities::{create_or_update::note::CreateOrUpdateNote, CreateOrUpdateType},
InCommunity, InCommunity,
}, },
SendActivity,
}; };
use activitypub_federation::{ use activitypub_federation::{
config::Data, config::Data,
@ -25,7 +24,6 @@ use activitypub_federation::{
}; };
use lemmy_api_common::{ use lemmy_api_common::{
build_response::send_local_notifs, build_response::send_local_notifs,
comment::{CommentResponse, EditComment},
context::LemmyContext, context::LemmyContext,
utils::{check_post_deleted_or_removed, is_mod_or_admin}, utils::{check_post_deleted_or_removed, is_mod_or_admin},
}; };
@ -43,25 +41,6 @@ use lemmy_db_schema::{
use lemmy_utils::{error::LemmyError, utils::mention::scrape_text_for_mentions}; use lemmy_utils::{error::LemmyError, utils::mention::scrape_text_for_mentions};
use url::Url; use url::Url;
#[async_trait::async_trait]
impl SendActivity for EditComment {
type Response = CommentResponse;
async fn send_activity(
_request: &Self,
response: &Self::Response,
context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
CreateOrUpdateNote::send(
response.comment_view.comment.clone(),
response.comment_view.creator.id,
CreateOrUpdateType::Update,
context.reset_request_count(),
)
.await
}
}
impl CreateOrUpdateNote { impl CreateOrUpdateNote {
#[tracing::instrument(skip(comment, person_id, kind, context))] #[tracing::instrument(skip(comment, person_id, kind, context))]
pub(crate) async fn send( pub(crate) async fn send(

View file

@ -14,7 +14,6 @@ use crate::{
activities::{create_or_update::page::CreateOrUpdatePage, CreateOrUpdateType}, activities::{create_or_update::page::CreateOrUpdatePage, CreateOrUpdateType},
InCommunity, InCommunity,
}, },
SendActivity,
}; };
use activitypub_federation::{ use activitypub_federation::{
config::Data, config::Data,
@ -22,10 +21,7 @@ use activitypub_federation::{
protocol::verification::{verify_domains_match, verify_urls_match}, protocol::verification::{verify_domains_match, verify_urls_match},
traits::{ActivityHandler, Actor, Object}, traits::{ActivityHandler, Actor, Object},
}; };
use lemmy_api_common::{ use lemmy_api_common::context::LemmyContext;
context::LemmyContext,
post::{EditPost, PostResponse},
};
use lemmy_db_schema::{ use lemmy_db_schema::{
aggregates::structs::PostAggregates, aggregates::structs::PostAggregates,
newtypes::PersonId, newtypes::PersonId,
@ -39,25 +35,6 @@ use lemmy_db_schema::{
use lemmy_utils::error::{LemmyError, LemmyErrorType}; use lemmy_utils::error::{LemmyError, LemmyErrorType};
use url::Url; use url::Url;
#[async_trait::async_trait]
impl SendActivity for EditPost {
type Response = PostResponse;
async fn send_activity(
_request: &Self,
response: &Self::Response,
context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
CreateOrUpdatePage::send(
response.post_view.post.clone(),
response.post_view.creator.id,
CreateOrUpdateType::Update,
context.reset_request_count(),
)
.await
}
}
impl CreateOrUpdatePage { impl CreateOrUpdatePage {
pub(crate) async fn new( pub(crate) async fn new(
post: ApubPost, post: ApubPost,

View file

@ -29,7 +29,6 @@ use activitypub_federation::{
traits::{Actor, Object}, traits::{Actor, Object},
}; };
use lemmy_api_common::{ use lemmy_api_common::{
comment::{CommentResponse, DeleteComment, RemoveComment},
community::{CommunityResponse, DeleteCommunity, RemoveCommunity}, community::{CommunityResponse, DeleteCommunity, RemoveCommunity},
context::LemmyContext, context::LemmyContext,
post::{DeletePost, PostResponse, RemovePost}, post::{DeletePost, PostResponse, RemovePost},
@ -102,50 +101,6 @@ impl SendActivity for RemovePost {
} }
} }
#[async_trait::async_trait]
impl SendActivity for DeleteComment {
type Response = CommentResponse;
async fn send_activity(
request: &Self,
response: &Self::Response,
context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let community_id = response.comment_view.community.id;
let community = Community::read(&mut context.pool(), community_id).await?;
let person = Person::read(&mut context.pool(), response.comment_view.creator.id).await?;
let deletable = DeletableObjects::Comment(response.comment_view.comment.clone().into());
send_apub_delete_in_community(person, community, deletable, None, request.deleted, context)
.await
}
}
#[async_trait::async_trait]
impl SendActivity for RemoveComment {
type Response = CommentResponse;
async fn send_activity(
request: &Self,
response: &Self::Response,
context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
let comment = Comment::read(&mut context.pool(), request.comment_id).await?;
let community =
Community::read(&mut context.pool(), response.comment_view.community.id).await?;
let deletable = DeletableObjects::Comment(comment.into());
send_apub_delete_in_community(
local_user_view.person,
community,
deletable,
request.reason.clone().or_else(|| Some(String::new())),
request.removed,
context,
)
.await
}
}
#[async_trait::async_trait] #[async_trait::async_trait]
impl SendActivity for DeletePrivateMessage { impl SendActivity for DeletePrivateMessage {
type Response = PrivateMessageResponse; type Response = PrivateMessageResponse;
@ -217,7 +172,7 @@ impl SendActivity for RemoveCommunity {
/// Parameter `reason` being set indicates that this is a removal by a mod. If its unset, this /// Parameter `reason` being set indicates that this is a removal by a mod. If its unset, this
/// action was done by a normal user. /// action was done by a normal user.
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
async fn send_apub_delete_in_community( pub(crate) async fn send_apub_delete_in_community(
actor: Person, actor: Person,
community: Community, community: Community,
object: DeletableObjects, object: DeletableObjects,

View file

@ -1,4 +1,8 @@
use crate::{ use crate::{
activities::{
deletion::{send_apub_delete_in_community, DeletableObjects},
voting::send_like_activity,
},
objects::{community::ApubCommunity, person::ApubPerson}, objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::{ protocol::activities::{
create_or_update::{note::CreateOrUpdateNote, page::CreateOrUpdatePage}, create_or_update::{note::CreateOrUpdateNote, page::CreateOrUpdatePage},
@ -222,15 +226,30 @@ pub async fn match_outgoing_activities(
) -> LemmyResult<()> { ) -> LemmyResult<()> {
let context = context.reset_request_count(); let context = context.reset_request_count();
let fed_task = async { let fed_task = async {
use SendActivityData::*;
match data { match data {
SendActivityData::CreatePost(post) => { CreatePost(post) | UpdatePost(post) => {
let creator_id = post.creator_id; let creator_id = post.creator_id;
CreateOrUpdatePage::send(post, creator_id, CreateOrUpdateType::Create, context).await CreateOrUpdatePage::send(post, creator_id, CreateOrUpdateType::Create, context).await
} }
SendActivityData::CreateComment(comment) => { CreateComment(comment) | UpdateComment(comment) => {
let creator_id = comment.creator_id; let creator_id = comment.creator_id;
CreateOrUpdateNote::send(comment, creator_id, CreateOrUpdateType::Create, context).await CreateOrUpdateNote::send(comment, creator_id, CreateOrUpdateType::Create, context).await
} }
DeleteComment(comment, actor, community) => {
let is_deleted = comment.deleted;
let deletable = DeletableObjects::Comment(comment.into());
send_apub_delete_in_community(actor, community, deletable, None, is_deleted, &context).await
}
RemoveComment(comment, actor, community, reason) => {
let is_removed = comment.removed;
let deletable = DeletableObjects::Comment(comment.into());
send_apub_delete_in_community(actor, community, deletable, reason, is_removed, &context)
.await
}
LikePostOrComment(object_id, person, community, score) => {
send_like_activity(object_id, person, community, score, context).await
}
} }
}; };
if *SYNCHRONOUS_FEDERATION { if *SYNCHRONOUS_FEDERATION {

View file

@ -2,106 +2,51 @@ use crate::{
activities::community::send_activity_in_community, activities::community::send_activity_in_community,
activity_lists::AnnouncableActivities, activity_lists::AnnouncableActivities,
fetcher::post_or_comment::PostOrComment, fetcher::post_or_comment::PostOrComment,
objects::{comment::ApubComment, person::ApubPerson, post::ApubPost}, objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost},
protocol::activities::voting::{ protocol::activities::voting::{
undo_vote::UndoVote, undo_vote::UndoVote,
vote::{Vote, VoteType}, vote::{Vote, VoteType},
}, },
SendActivity,
}; };
use activitypub_federation::{config::Data, fetch::object_id::ObjectId}; use activitypub_federation::{config::Data, fetch::object_id::ObjectId};
use lemmy_api_common::{ use lemmy_api_common::context::LemmyContext;
comment::{CommentResponse, CreateCommentLike},
context::LemmyContext,
post::{CreatePostLike, PostResponse},
sensitive::Sensitive,
utils::local_user_view_from_jwt,
};
use lemmy_db_schema::{ use lemmy_db_schema::{
newtypes::CommunityId, newtypes::DbUrl,
source::{ source::{
comment::{CommentLike, CommentLikeForm}, comment::{CommentLike, CommentLikeForm},
community::Community, community::Community,
person::Person, person::Person,
post::{PostLike, PostLikeForm}, post::{PostLike, PostLikeForm},
}, },
traits::{Crud, Likeable}, traits::Likeable,
}; };
use lemmy_utils::error::LemmyError; use lemmy_utils::error::LemmyError;
pub mod undo_vote; pub mod undo_vote;
pub mod vote; pub mod vote;
#[async_trait::async_trait] pub(crate) async fn send_like_activity(
impl SendActivity for CreatePostLike { object_id: DbUrl,
type Response = PostResponse; actor: Person,
community: Community,
async fn send_activity(
request: &Self,
response: &Self::Response,
context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let object_id = ObjectId::from(response.post_view.post.ap_id.clone());
let community_id = response.post_view.community.id;
send_activity(
object_id,
community_id,
request.score,
&request.auth,
context,
)
.await
}
}
#[async_trait::async_trait]
impl SendActivity for CreateCommentLike {
type Response = CommentResponse;
async fn send_activity(
request: &Self,
response: &Self::Response,
context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
let object_id = ObjectId::from(response.comment_view.comment.ap_id.clone());
let community_id = response.comment_view.community.id;
send_activity(
object_id,
community_id,
request.score,
&request.auth,
context,
)
.await
}
}
async fn send_activity(
object_id: ObjectId<PostOrComment>,
community_id: CommunityId,
score: i16, score: i16,
jwt: &Sensitive<String>, context: Data<LemmyContext>,
context: &Data<LemmyContext>,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let community = Community::read(&mut context.pool(), community_id) let object_id: ObjectId<PostOrComment> = object_id.try_into()?;
.await? let actor: ApubPerson = actor.into();
.into(); let community: ApubCommunity = community.into();
let local_user_view = local_user_view_from_jwt(jwt, context).await?;
let actor = Person::read(&mut context.pool(), local_user_view.person.id)
.await?
.into();
// score of 1 means upvote, -1 downvote, 0 undo a previous vote // score of 1 means upvote, -1 downvote, 0 undo a previous vote
if score != 0 { if score != 0 {
let vote = Vote::new(object_id, &actor, &community, score.try_into()?, context)?; let vote = Vote::new(object_id, &actor, &community, score.try_into()?, &context)?;
let activity = AnnouncableActivities::Vote(vote); let activity = AnnouncableActivities::Vote(vote);
send_activity_in_community(activity, &actor, &community, vec![], false, context).await send_activity_in_community(activity, &actor, &community, vec![], false, &context).await
} else { } else {
// Lemmy API doesnt distinguish between Undo/Like and Undo/Dislike, so we hardcode it here. // Lemmy API doesnt distinguish between Undo/Like and Undo/Dislike, so we hardcode it here.
let vote = Vote::new(object_id, &actor, &community, VoteType::Like, context)?; let vote = Vote::new(object_id, &actor, &community, VoteType::Like, &context)?;
let undo_vote = UndoVote::new(vote, &actor, &community, context)?; let undo_vote = UndoVote::new(vote, &actor, &community, &context)?;
let activity = AnnouncableActivities::UndoVote(undo_vote); let activity = AnnouncableActivities::UndoVote(undo_vote);
send_activity_in_community(activity, &actor, &community, vec![], false, context).await send_activity_in_community(activity, &actor, &community, vec![], false, &context).await
} }
} }

View file

@ -1,12 +1,13 @@
use actix_web::{guard, web, Error, HttpResponse, Result}; use actix_web::{guard, web, Error, HttpResponse, Result};
use lemmy_api::{ use lemmy_api::{
comment::{distinguish::distinguish_comment, save::save_comment}, comment::{distinguish::distinguish_comment, like::like_comment, save::save_comment},
comment_report::{list::list_comment_reports, resolve::resolve_comment_report}, comment_report::{list::list_comment_reports, resolve::resolve_comment_report},
local_user::notifications::mark_reply_read::mark_reply_as_read, local_user::notifications::mark_reply_read::mark_reply_as_read,
post::like::like_post,
Perform, Perform,
}; };
use lemmy_api_common::{ use lemmy_api_common::{
comment::{CreateCommentLike, CreateCommentReport, DeleteComment, EditComment, RemoveComment}, comment::CreateCommentReport,
community::{ community::{
AddModToCommunity, AddModToCommunity,
BanFromCommunity, BanFromCommunity,
@ -43,10 +44,8 @@ use lemmy_api_common::{
VerifyEmail, VerifyEmail,
}, },
post::{ post::{
CreatePostLike,
CreatePostReport, CreatePostReport,
DeletePost, DeletePost,
EditPost,
FeaturePost, FeaturePost,
GetSiteMetadata, GetSiteMetadata,
ListPostReports, ListPostReports,
@ -79,9 +78,15 @@ use lemmy_api_common::{
}, },
}; };
use lemmy_api_crud::{ use lemmy_api_crud::{
comment::{create::create_comment, read::get_comment}, comment::{
create::create_comment,
delete::delete_comment,
read::get_comment,
remove::remove_comment,
update::update_comment,
},
community::list::list_communities, community::list::list_communities,
post::{create::create_post, read::get_post}, post::{create::create_post, read::get_post, update::update_post},
private_message::read::get_private_message, private_message::read::get_private_message,
site::{create::create_site, read::get_site, update::update_site}, site::{create::create_site, read::get_site, update::update_site},
PerformCrud, PerformCrud,
@ -173,7 +178,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
web::scope("/post") web::scope("/post")
.wrap(rate_limit.message()) .wrap(rate_limit.message())
.route("", web::get().to(get_post)) .route("", web::get().to(get_post))
.route("", web::put().to(route_post_crud::<EditPost>)) .route("", web::put().to(update_post))
.route("/delete", web::post().to(route_post_crud::<DeletePost>)) .route("/delete", web::post().to(route_post_crud::<DeletePost>))
.route("/remove", web::post().to(route_post_crud::<RemovePost>)) .route("/remove", web::post().to(route_post_crud::<RemovePost>))
.route( .route(
@ -183,7 +188,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
.route("/lock", web::post().to(route_post::<LockPost>)) .route("/lock", web::post().to(route_post::<LockPost>))
.route("/feature", web::post().to(route_post::<FeaturePost>)) .route("/feature", web::post().to(route_post::<FeaturePost>))
.route("/list", web::get().to(list_posts)) .route("/list", web::get().to(list_posts))
.route("/like", web::post().to(route_post::<CreatePostLike>)) .route("/like", web::post().to(like_post))
.route("/save", web::put().to(route_post::<SavePost>)) .route("/save", web::put().to(route_post::<SavePost>))
.route("/report", web::post().to(route_post::<CreatePostReport>)) .route("/report", web::post().to(route_post::<CreatePostReport>))
.route( .route(
@ -208,12 +213,12 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
web::scope("/comment") web::scope("/comment")
.wrap(rate_limit.message()) .wrap(rate_limit.message())
.route("", web::get().to(get_comment)) .route("", web::get().to(get_comment))
.route("", web::put().to(route_post_crud::<EditComment>)) .route("", web::put().to(update_comment))
.route("/delete", web::post().to(route_post_crud::<DeleteComment>)) .route("/delete", web::post().to(delete_comment))
.route("/remove", web::post().to(route_post_crud::<RemoveComment>)) .route("/remove", web::post().to(remove_comment))
.route("/mark_as_read", web::post().to(mark_reply_as_read)) .route("/mark_as_read", web::post().to(mark_reply_as_read))
.route("/distinguish", web::post().to(distinguish_comment)) .route("/distinguish", web::post().to(distinguish_comment))
.route("/like", web::post().to(route_post::<CreateCommentLike>)) .route("/like", web::post().to(like_comment))
.route("/save", web::put().to(save_comment)) .route("/save", web::put().to(save_comment))
.route("/list", web::get().to(list_comments)) .route("/list", web::get().to(list_comments))
.route("/report", web::post().to(route_post::<CreateCommentReport>)) .route("/report", web::post().to(route_post::<CreateCommentReport>))