Rewrite some API handlers to remove Perform trait (#3735)

* Rewrite some API handlers to remove Perform trait

* Convert CreateComment

* ci
This commit is contained in:
Nutomic 2023-07-28 16:39:38 +02:00 committed by GitHub
parent db76c5b7ff
commit 37998b3398
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 858 additions and 963 deletions

1
Cargo.lock generated
View file

@ -2573,6 +2573,7 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
name = "lemmy_api" name = "lemmy_api"
version = "0.18.1" version = "0.18.1"
dependencies = [ dependencies = [
"activitypub_federation",
"actix-web", "actix-web",
"anyhow", "anyhow",
"async-trait", "async-trait",

View file

@ -20,6 +20,7 @@ lemmy_db_views = { workspace = true, features = ["full"] }
lemmy_db_views_moderator = { workspace = true, features = ["full"] } lemmy_db_views_moderator = { workspace = true, features = ["full"] }
lemmy_db_views_actor = { workspace = true, features = ["full"] } lemmy_db_views_actor = { workspace = true, features = ["full"] }
lemmy_api_common = { workspace = true, features = ["full"] } lemmy_api_common = { workspace = true, features = ["full"] }
activitypub_federation = { workspace = true }
bcrypt = { workspace = true } bcrypt = { workspace = true }
serde = { workspace = true } serde = { workspace = true }
actix-web = { workspace = true } actix-web = { workspace = true }

View file

@ -1,5 +1,4 @@
use crate::Perform; use actix_web::web::{Data, Json};
use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
comment::{CommentResponse, DistinguishComment}, comment::{CommentResponse, DistinguishComment},
context::LemmyContext, context::LemmyContext,
@ -12,50 +11,47 @@ 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};
#[async_trait::async_trait(?Send)] #[tracing::instrument(skip(context))]
impl Perform for DistinguishComment { pub async fn distinguish_comment(
type Response = CommentResponse; data: Json<DistinguishComment>,
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: &DistinguishComment = 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 distinguish a comment
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 distinguish a comment // Update the Comment
is_mod_or_admin( let comment_id = data.comment_id;
&mut context.pool(), let form = CommentUpdateForm::builder()
local_user_view.person.id, .distinguished(Some(data.distinguished))
orig_comment.community.id, .build();
) Comment::update(&mut context.pool(), comment_id, &form)
.await?; .await
.with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
// Update the Comment let comment_id = data.comment_id;
let comment_id = data.comment_id; let person_id = local_user_view.person.id;
let form = CommentUpdateForm::builder() let comment_view = CommentView::read(&mut context.pool(), comment_id, Some(person_id)).await?;
.distinguished(Some(data.distinguished))
.build();
Comment::update(&mut context.pool(), comment_id, &form)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
let comment_id = data.comment_id; Ok(Json(CommentResponse {
let person_id = local_user_view.person.id; comment_view,
let comment_view = CommentView::read(&mut context.pool(), comment_id, Some(person_id)).await?; recipient_ids: Vec::new(),
form_id: None,
Ok(CommentResponse { }))
comment_view,
recipient_ids: Vec::new(),
form_id: None,
})
}
} }

View file

@ -1,3 +1,3 @@
mod distinguish; pub mod distinguish;
mod like; pub mod like;
mod save; pub mod save;

View file

@ -1,5 +1,4 @@
use crate::Perform; use actix_web::web::{Data, Json};
use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
comment::{CommentResponse, SaveComment}, comment::{CommentResponse, SaveComment},
context::LemmyContext, context::LemmyContext,
@ -12,38 +11,35 @@ 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};
#[async_trait::async_trait(?Send)] #[tracing::instrument(skip(context))]
impl Perform for SaveComment { pub async fn save_comment(
type Response = CommentResponse; data: Json<SaveComment>,
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_saved_form = CommentSavedForm {
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommentResponse, LemmyError> { comment_id: data.comment_id,
let data: &SaveComment = self; person_id: local_user_view.person.id,
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; };
let comment_saved_form = CommentSavedForm { if data.save {
comment_id: data.comment_id, CommentSaved::save(&mut context.pool(), &comment_saved_form)
person_id: local_user_view.person.id, .await
}; .with_lemmy_type(LemmyErrorType::CouldntSaveComment)?;
} else {
if data.save { CommentSaved::unsave(&mut context.pool(), &comment_saved_form)
CommentSaved::save(&mut context.pool(), &comment_saved_form) .await
.await .with_lemmy_type(LemmyErrorType::CouldntSaveComment)?;
.with_lemmy_type(LemmyErrorType::CouldntSaveComment)?;
} else {
CommentSaved::unsave(&mut context.pool(), &comment_saved_form)
.await
.with_lemmy_type(LemmyErrorType::CouldntSaveComment)?;
}
let comment_id = data.comment_id;
let person_id = local_user_view.person.id;
let comment_view = CommentView::read(&mut context.pool(), comment_id, Some(person_id)).await?;
Ok(CommentResponse {
comment_view,
recipient_ids: Vec::new(),
form_id: None,
})
} }
let comment_id = data.comment_id;
let person_id = local_user_view.person.id;
let comment_view = CommentView::read(&mut context.pool(), comment_id, Some(person_id)).await?;
Ok(Json(CommentResponse {
comment_view,
recipient_ids: Vec::new(),
form_id: None,
}))
} }

View file

@ -1,5 +1,4 @@
use crate::Perform; use actix_web::web::{Data, Json, Query};
use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
comment::{ListCommentReports, ListCommentReportsResponse}, comment::{ListCommentReports, ListCommentReportsResponse},
context::LemmyContext, context::LemmyContext,
@ -10,32 +9,26 @@ use lemmy_utils::error::LemmyError;
/// Lists comment reports for a community if an id is supplied /// Lists comment reports for a community if an id is supplied
/// or returns all comment reports for communities a user moderates /// or returns all comment reports for communities a user moderates
#[async_trait::async_trait(?Send)] #[tracing::instrument(skip(context))]
impl Perform for ListCommentReports { pub async fn list_comment_reports(
type Response = ListCommentReportsResponse; data: Query<ListCommentReports>,
context: Data<LemmyContext>,
) -> Result<Json<ListCommentReportsResponse>, LemmyError> {
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
#[tracing::instrument(skip(context))] let community_id = data.community_id;
async fn perform( let unresolved_only = data.unresolved_only;
&self,
context: &Data<LemmyContext>,
) -> Result<ListCommentReportsResponse, LemmyError> {
let data: &ListCommentReports = self;
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let community_id = data.community_id; let page = data.page;
let unresolved_only = data.unresolved_only; let limit = data.limit;
let comment_reports = CommentReportQuery {
let page = data.page; community_id,
let limit = data.limit; unresolved_only,
let comment_reports = CommentReportQuery { page,
community_id, limit,
unresolved_only,
page,
limit,
}
.list(&mut context.pool(), &local_user_view.person)
.await?;
Ok(ListCommentReportsResponse { comment_reports })
} }
.list(&mut context.pool(), &local_user_view.person)
.await?;
Ok(Json(ListCommentReportsResponse { comment_reports }))
} }

View file

@ -1,3 +1,3 @@
mod create; pub mod create;
mod list; pub mod list;
mod resolve; pub mod resolve;

View file

@ -1,5 +1,4 @@
use crate::Perform; use actix_web::web::{Data, Json};
use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
comment::{CommentReportResponse, ResolveCommentReport}, comment::{CommentReportResponse, ResolveCommentReport},
context::LemmyContext, context::LemmyContext,
@ -10,41 +9,35 @@ use lemmy_db_views::structs::CommentReportView;
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
/// Resolves or unresolves a comment report and notifies the moderators of the community /// Resolves or unresolves a comment report and notifies the moderators of the community
#[async_trait::async_trait(?Send)] #[tracing::instrument(skip(context))]
impl Perform for ResolveCommentReport { pub async fn resolve_comment_report(
type Response = CommentReportResponse; data: Json<ResolveCommentReport>,
context: Data<LemmyContext>,
) -> Result<Json<CommentReportResponse>, LemmyError> {
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
#[tracing::instrument(skip(context))] let report_id = data.report_id;
async fn perform( let person_id = local_user_view.person.id;
&self, let report = CommentReportView::read(&mut context.pool(), report_id, person_id).await?;
context: &Data<LemmyContext>,
) -> Result<CommentReportResponse, LemmyError> {
let data: &ResolveCommentReport = self;
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let report_id = data.report_id; let person_id = local_user_view.person.id;
let person_id = local_user_view.person.id; is_mod_or_admin(&mut context.pool(), person_id, report.community.id).await?;
let report = CommentReportView::read(&mut context.pool(), report_id, person_id).await?;
let person_id = local_user_view.person.id; if data.resolved {
is_mod_or_admin(&mut context.pool(), person_id, report.community.id).await?; CommentReport::resolve(&mut context.pool(), report_id, person_id)
.await
if data.resolved { .with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
CommentReport::resolve(&mut context.pool(), report_id, person_id) } else {
.await CommentReport::unresolve(&mut context.pool(), report_id, person_id)
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)?; .await
} else { .with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
CommentReport::unresolve(&mut context.pool(), report_id, person_id)
.await
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
}
let report_id = data.report_id;
let comment_report_view =
CommentReportView::read(&mut context.pool(), report_id, person_id).await?;
Ok(CommentReportResponse {
comment_report_view,
})
} }
let report_id = data.report_id;
let comment_report_view =
CommentReportView::read(&mut context.pool(), report_id, person_id).await?;
Ok(Json(CommentReportResponse {
comment_report_view,
}))
} }

View file

@ -9,15 +9,15 @@ use lemmy_utils::{
}; };
use std::io::Cursor; use std::io::Cursor;
mod comment; pub mod comment;
mod comment_report; pub mod comment_report;
mod community; pub mod community;
mod local_user; pub mod local_user;
mod post; pub mod post;
mod post_report; pub mod post_report;
mod private_message; pub mod private_message;
mod private_message_report; pub mod private_message_report;
mod site; pub mod site;
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
pub trait Perform { pub trait Perform {

View file

@ -1,13 +1,13 @@
mod add_admin; pub mod add_admin;
mod ban_person; pub mod ban_person;
mod block; pub mod block;
mod change_password; pub mod change_password;
mod change_password_after_reset; pub mod change_password_after_reset;
mod get_captcha; pub mod get_captcha;
mod list_banned; pub mod list_banned;
mod login; pub mod login;
mod notifications; pub mod notifications;
mod report_count; pub mod report_count;
mod reset_password; pub mod reset_password;
mod save_settings; pub mod save_settings;
mod verify_email; pub mod verify_email;

View file

@ -1,5 +1,4 @@
use crate::Perform; use actix_web::web::{Data, Json};
use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
person::{CommentReplyResponse, MarkCommentReplyAsRead}, person::{CommentReplyResponse, MarkCommentReplyAsRead},
@ -12,41 +11,35 @@ use lemmy_db_schema::{
use lemmy_db_views_actor::structs::CommentReplyView; use lemmy_db_views_actor::structs::CommentReplyView;
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[tracing::instrument(skip(context))]
impl Perform for MarkCommentReplyAsRead { pub async fn mark_reply_as_read(
type Response = CommentReplyResponse; data: Json<MarkCommentReplyAsRead>,
context: Data<LemmyContext>,
) -> Result<Json<CommentReplyResponse>, LemmyError> {
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
#[tracing::instrument(skip(context))] let comment_reply_id = data.comment_reply_id;
async fn perform( let read_comment_reply = CommentReply::read(&mut context.pool(), comment_reply_id).await?;
&self,
context: &Data<LemmyContext>,
) -> Result<CommentReplyResponse, LemmyError> {
let data = self;
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let comment_reply_id = data.comment_reply_id; if local_user_view.person.id != read_comment_reply.recipient_id {
let read_comment_reply = CommentReply::read(&mut context.pool(), comment_reply_id).await?; return Err(LemmyErrorType::CouldntUpdateComment)?;
if local_user_view.person.id != read_comment_reply.recipient_id {
return Err(LemmyErrorType::CouldntUpdateComment)?;
}
let comment_reply_id = read_comment_reply.id;
let read = Some(data.read);
CommentReply::update(
&mut context.pool(),
comment_reply_id,
&CommentReplyUpdateForm { read },
)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
let comment_reply_id = read_comment_reply.id;
let person_id = local_user_view.person.id;
let comment_reply_view =
CommentReplyView::read(&mut context.pool(), comment_reply_id, Some(person_id)).await?;
Ok(CommentReplyResponse { comment_reply_view })
} }
let comment_reply_id = read_comment_reply.id;
let read = Some(data.read);
CommentReply::update(
&mut context.pool(),
comment_reply_id,
&CommentReplyUpdateForm { read },
)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
let comment_reply_id = read_comment_reply.id;
let person_id = local_user_view.person.id;
let comment_reply_view =
CommentReplyView::read(&mut context.pool(), comment_reply_id, Some(person_id)).await?;
Ok(Json(CommentReplyResponse { comment_reply_view }))
} }

View file

@ -1,6 +1,6 @@
mod list_mentions; pub mod list_mentions;
mod list_replies; pub mod list_replies;
mod mark_all_read; pub mod mark_all_read;
mod mark_mention_read; pub mod mark_mention_read;
mod mark_reply_read; pub mod mark_reply_read;
mod unread_count; pub mod unread_count;

View file

@ -23,7 +23,7 @@ use lemmy_db_views_actor::structs::CommunityView;
use lemmy_utils::{error::LemmyError, utils::mention::MentionData}; use lemmy_utils::{error::LemmyError, utils::mention::MentionData};
pub async fn build_comment_response( pub async fn build_comment_response(
context: &Data<LemmyContext>, context: &LemmyContext,
comment_id: CommentId, comment_id: CommentId,
local_user_view: Option<LocalUserView>, local_user_view: Option<LocalUserView>,
form_id: Option<String>, form_id: Option<String>,

View file

@ -1,7 +1,7 @@
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::post::Post; use lemmy_db_schema::source::{comment::Comment, 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,6 +22,7 @@ pub static MATCH_OUTGOING_ACTIVITIES: OnceCell<MatchOutgoingActivitiesBoxed> = O
#[derive(Debug)] #[derive(Debug)]
pub enum SendActivityData { pub enum SendActivityData {
CreatePost(Post), CreatePost(Post),
CreateComment(Comment),
} }
// 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

@ -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, CreateComment}, comment::{CommentResponse, CreateComment},
context::LemmyContext, context::LemmyContext,
send_activity::{ActivityChannel, SendActivityData},
utils::{ utils::{
check_community_ban, check_community_ban,
check_community_deleted_or_removed, check_community_deleted_or_removed,
@ -35,169 +36,174 @@ 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;
#[async_trait::async_trait(?Send)] #[tracing::instrument(skip(context))]
impl PerformCrud for CreateComment { pub async fn create_comment(
type Response = CommentResponse; data: Json<CreateComment>,
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 content = remove_slurs(
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommentResponse, LemmyError> { &data.content.clone(),
let data: &CreateComment = self; &local_site_to_slur_regex(&local_site),
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; );
let local_site = LocalSite::read(&mut context.pool()).await?; is_valid_body_field(&Some(content.clone()), false)?;
let content = sanitize_html(&content);
let content = remove_slurs( // Check for a community ban
&data.content.clone(), let post_id = data.post_id;
&local_site_to_slur_regex(&local_site), let post = get_post(post_id, &mut context.pool()).await?;
); let community_id = post.community_id;
is_valid_body_field(&Some(content.clone()), false)?;
let content = sanitize_html(&content);
// Check for a community ban check_community_ban(local_user_view.person.id, community_id, &mut context.pool()).await?;
let post_id = data.post_id; check_community_deleted_or_removed(community_id, &mut context.pool()).await?;
let post = get_post(post_id, &mut context.pool()).await?; check_post_deleted_or_removed(&post)?;
let community_id = post.community_id;
check_community_ban(local_user_view.person.id, community_id, &mut context.pool()).await?; // Check if post is locked, no new comments
check_community_deleted_or_removed(community_id, &mut context.pool()).await?; if post.locked {
check_post_deleted_or_removed(&post)?; return Err(LemmyErrorType::Locked)?;
}
// Check if post is locked, no new comments // Fetch the parent, if it exists
if post.locked { let parent_opt = if let Some(parent_id) = data.parent_id {
return Err(LemmyErrorType::Locked)?; Comment::read(&mut context.pool(), parent_id).await.ok()
} else {
None
};
// If there's a parent_id, check to make sure that comment is in that post
// Strange issue where sometimes the post ID of the parent comment is incorrect
if let Some(parent) = parent_opt.as_ref() {
if parent.post_id != post_id {
return Err(LemmyErrorType::CouldntCreateComment)?;
} }
check_comment_depth(parent)?;
}
// Fetch the parent, if it exists CommunityLanguage::is_allowed_community_language(
let parent_opt = if let Some(parent_id) = data.parent_id { &mut context.pool(),
Comment::read(&mut context.pool(), parent_id).await.ok() data.language_id,
} else { community_id,
None )
}; .await?;
// If there's a parent_id, check to make sure that comment is in that post // attempt to set default language if none was provided
// Strange issue where sometimes the post ID of the parent comment is incorrect let language_id = match data.language_id {
if let Some(parent) = parent_opt.as_ref() { Some(lid) => Some(lid),
if parent.post_id != post_id { None => {
return Err(LemmyErrorType::CouldntCreateComment)?; default_post_language(
} &mut context.pool(),
check_comment_depth(parent)?; community_id,
local_user_view.local_user.id,
)
.await?
} }
};
CommunityLanguage::is_allowed_community_language( let comment_form = CommentInsertForm::builder()
&mut context.pool(), .content(content.clone())
data.language_id, .post_id(data.post_id)
community_id, .creator_id(local_user_view.person.id)
) .language_id(language_id)
.await?; .build();
// attempt to set default language if none was provided // Create the comment
let language_id = match data.language_id { let parent_path = parent_opt.clone().map(|t| t.path);
Some(lid) => Some(lid), let inserted_comment = Comment::create(&mut context.pool(), &comment_form, parent_path.as_ref())
None => {
default_post_language(
&mut context.pool(),
community_id,
local_user_view.local_user.id,
)
.await?
}
};
let comment_form = CommentInsertForm::builder()
.content(content.clone())
.post_id(data.post_id)
.creator_id(local_user_view.person.id)
.language_id(language_id)
.build();
// Create the comment
let parent_path = parent_opt.clone().map(|t| t.path);
let inserted_comment =
Comment::create(&mut context.pool(), &comment_form, parent_path.as_ref())
.await
.with_lemmy_type(LemmyErrorType::CouldntCreateComment)?;
// Necessary to update the ap_id
let inserted_comment_id = inserted_comment.id;
let protocol_and_hostname = context.settings().get_protocol_and_hostname();
let apub_id = generate_local_apub_endpoint(
EndpointType::Comment,
&inserted_comment_id.to_string(),
&protocol_and_hostname,
)?;
let updated_comment = Comment::update(
&mut context.pool(),
inserted_comment_id,
&CommentUpdateForm::builder().ap_id(Some(apub_id)).build(),
)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntCreateComment)?; .with_lemmy_type(LemmyErrorType::CouldntCreateComment)?;
// Scan the comment for user mentions, add those rows // Necessary to update the ap_id
let mentions = scrape_text_for_mentions(&content); let inserted_comment_id = inserted_comment.id;
let recipient_ids = send_local_notifs( let protocol_and_hostname = context.settings().get_protocol_and_hostname();
mentions,
&updated_comment,
&local_user_view.person,
&post,
true,
context,
)
.await?;
// You like your own comment by default let apub_id = generate_local_apub_endpoint(
let like_form = CommentLikeForm { EndpointType::Comment,
comment_id: inserted_comment.id, &inserted_comment_id.to_string(),
post_id: post.id, &protocol_and_hostname,
person_id: local_user_view.person.id, )?;
score: 1, let updated_comment = Comment::update(
}; &mut context.pool(),
inserted_comment_id,
&CommentUpdateForm::builder().ap_id(Some(apub_id)).build(),
)
.await
.with_lemmy_type(LemmyErrorType::CouldntCreateComment)?;
CommentLike::like(&mut context.pool(), &like_form) // Scan the comment for user mentions, add those rows
let mentions = scrape_text_for_mentions(&content);
let recipient_ids = send_local_notifs(
mentions,
&updated_comment,
&local_user_view.person,
&post,
true,
&context,
)
.await?;
// You like your own comment by default
let like_form = CommentLikeForm {
comment_id: inserted_comment.id,
post_id: post.id,
person_id: local_user_view.person.id,
score: 1,
};
CommentLike::like(&mut context.pool(), &like_form)
.await
.with_lemmy_type(LemmyErrorType::CouldntLikeComment)?;
ActivityChannel::submit_activity(
SendActivityData::CreateComment(updated_comment.clone()),
&context,
)
.await?;
// If its a reply, mark the parent as read
if let Some(parent) = parent_opt {
let parent_id = parent.id;
let comment_reply = CommentReply::read_by_comment(&mut context.pool(), parent_id).await;
if let Ok(reply) = comment_reply {
CommentReply::update(
&mut context.pool(),
reply.id,
&CommentReplyUpdateForm { read: Some(true) },
)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntLikeComment)?; .with_lemmy_type(LemmyErrorType::CouldntUpdateReplies)?;
// If its a reply, mark the parent as read
if let Some(parent) = parent_opt {
let parent_id = parent.id;
let comment_reply = CommentReply::read_by_comment(&mut context.pool(), parent_id).await;
if let Ok(reply) = comment_reply {
CommentReply::update(
&mut context.pool(),
reply.id,
&CommentReplyUpdateForm { read: Some(true) },
)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdateReplies)?;
}
// 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 {
PersonMention::update(
&mut context.pool(),
mention.id,
&PersonMentionUpdateForm { read: Some(true) },
)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdatePersonMentions)?;
}
} }
// 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 {
PersonMention::update(
&mut context.pool(),
mention.id,
&PersonMentionUpdateForm { read: Some(true) },
)
.await
.with_lemmy_type(LemmyErrorType::CouldntUpdatePersonMentions)?;
}
}
Ok(Json(
build_comment_response( build_comment_response(
context, context.deref(),
inserted_comment.id, inserted_comment.id,
Some(local_user_view), Some(local_user_view),
self.form_id.clone(), data.form_id.clone(),
recipient_ids, recipient_ids,
) )
.await .await?,
} ))
} }
pub fn check_comment_depth(comment: &Comment) -> Result<(), LemmyError> { pub fn check_comment_depth(comment: &Comment) -> Result<(), LemmyError> {

View file

@ -15,6 +15,7 @@ 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)] #[async_trait::async_trait(?Send)]
impl PerformCrud for DeleteComment { impl PerformCrud for DeleteComment {
@ -68,7 +69,7 @@ impl PerformCrud for DeleteComment {
.await?; .await?;
build_comment_response( build_comment_response(
context, context.deref(),
updated_comment.id, updated_comment.id,
Some(local_user_view), Some(local_user_view),
None, None,

View file

@ -1,5 +1,5 @@
mod create; pub mod create;
mod delete; pub mod delete;
mod read; pub mod read;
mod remove; pub mod remove;
mod update; pub mod update;

View file

@ -1,5 +1,4 @@
use crate::PerformCrud; use actix_web::web::{Data, Json, Query};
use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
build_response::build_comment_response, build_response::build_comment_response,
comment::{CommentResponse, GetComment}, comment::{CommentResponse, GetComment},
@ -8,19 +7,19 @@ 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;
#[async_trait::async_trait(?Send)] #[tracing::instrument(skip(context))]
impl PerformCrud for GetComment { pub async fn get_comment(
type Response = CommentResponse; data: Query<GetComment>,
context: Data<LemmyContext>,
) -> Result<Json<CommentResponse>, LemmyError> {
let local_user_view = local_user_view_from_jwt_opt(data.auth.as_ref(), &context).await;
let local_site = LocalSite::read(&mut context.pool()).await?;
#[tracing::instrument(skip(context))] check_private_instance(&local_user_view, &local_site)?;
async fn perform(&self, context: &Data<LemmyContext>) -> Result<Self::Response, LemmyError> {
let data = self;
let local_user_view = local_user_view_from_jwt_opt(data.auth.as_ref(), context).await;
let local_site = LocalSite::read(&mut context.pool()).await?;
check_private_instance(&local_user_view, &local_site)?; 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

@ -16,6 +16,7 @@ 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)] #[async_trait::async_trait(?Send)]
impl PerformCrud for RemoveComment { impl PerformCrud for RemoveComment {
@ -76,7 +77,7 @@ impl PerformCrud for RemoveComment {
.await?; .await?;
build_comment_response( build_comment_response(
context, context.deref(),
updated_comment.id, updated_comment.id,
Some(local_user_view), Some(local_user_view),
None, None,

View file

@ -29,6 +29,7 @@ use lemmy_utils::{
validation::is_valid_body_field, validation::is_valid_body_field,
}, },
}; };
use std::ops::Deref;
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl PerformCrud for EditComment { impl PerformCrud for EditComment {
@ -95,7 +96,7 @@ impl PerformCrud for EditComment {
.await?; .await?;
build_comment_response( build_comment_response(
context, context.deref(),
updated_comment.id, updated_comment.id,
Some(local_user_view), Some(local_user_view),
self.form_id.clone(), self.form_id.clone(),

View file

@ -1,5 +1,4 @@
use crate::PerformCrud; use actix_web::web::{Data, Json, Query};
use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
community::{ListCommunities, ListCommunitiesResponse}, community::{ListCommunities, ListCommunitiesResponse},
context::LemmyContext, context::LemmyContext,
@ -9,42 +8,36 @@ use lemmy_db_schema::source::local_site::LocalSite;
use lemmy_db_views_actor::community_view::CommunityQuery; use lemmy_db_views_actor::community_view::CommunityQuery;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::LemmyError;
#[async_trait::async_trait(?Send)] #[tracing::instrument(skip(context))]
impl PerformCrud for ListCommunities { pub async fn list_communities(
type Response = ListCommunitiesResponse; data: Query<ListCommunities>,
context: Data<LemmyContext>,
) -> Result<Json<ListCommunitiesResponse>, LemmyError> {
let local_user_view = local_user_view_from_jwt_opt(data.auth.as_ref(), &context).await;
let local_site = LocalSite::read(&mut context.pool()).await?;
let is_admin = local_user_view.as_ref().map(|luv| is_admin(luv).is_ok());
#[tracing::instrument(skip(context))] check_private_instance(&local_user_view, &local_site)?;
async fn perform(
&self,
context: &Data<LemmyContext>,
) -> Result<ListCommunitiesResponse, LemmyError> {
let data: &ListCommunities = self;
let local_user_view = local_user_view_from_jwt_opt(data.auth.as_ref(), context).await;
let local_site = LocalSite::read(&mut context.pool()).await?;
let is_admin = local_user_view.as_ref().map(|luv| is_admin(luv).is_ok());
check_private_instance(&local_user_view, &local_site)?; let sort = data.sort;
let listing_type = data.type_;
let sort = data.sort; let show_nsfw = data.show_nsfw;
let listing_type = data.type_; let page = data.page;
let show_nsfw = data.show_nsfw; let limit = data.limit;
let page = data.page; let local_user = local_user_view.map(|l| l.local_user);
let limit = data.limit; let communities = CommunityQuery {
let local_user = local_user_view.map(|l| l.local_user); listing_type,
let communities = CommunityQuery { show_nsfw,
listing_type, sort,
show_nsfw, local_user: local_user.as_ref(),
sort, page,
local_user: local_user.as_ref(), limit,
page, is_mod_or_admin: is_admin,
limit, ..Default::default()
is_mod_or_admin: is_admin,
..Default::default()
}
.list(&mut context.pool())
.await?;
// Return the jwt
Ok(ListCommunitiesResponse { communities })
} }
.list(&mut context.pool())
.await?;
// Return the jwt
Ok(Json(ListCommunitiesResponse { communities }))
} }

View file

@ -1,5 +1,5 @@
mod create; pub mod create;
mod delete; pub mod delete;
mod list; pub mod list;
mod remove; pub mod remove;
mod update; pub mod update;

View file

@ -2,13 +2,13 @@ use actix_web::web::Data;
use lemmy_api_common::context::LemmyContext; use lemmy_api_common::context::LemmyContext;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::LemmyError;
mod comment; pub mod comment;
mod community; pub mod community;
mod custom_emoji; pub mod custom_emoji;
pub mod post; pub mod post;
mod private_message; pub mod private_message;
mod site; pub mod site;
mod user; pub mod user;
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
pub trait PerformCrud { pub trait PerformCrud {

View file

@ -1,5 +1,5 @@
pub mod create; pub mod create;
mod delete; pub mod delete;
mod read; pub mod read;
mod remove; pub mod remove;
mod update; pub mod update;

View file

@ -1,5 +1,4 @@
use crate::PerformCrud; use actix_web::web::{Data, Json, Query};
use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
post::{GetPost, GetPostResponse}, post::{GetPost, GetPostResponse},
@ -19,107 +18,103 @@ use lemmy_db_views::{post_view::PostQuery, structs::PostView};
use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView}; use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[tracing::instrument(skip(context))]
impl PerformCrud for GetPost { pub async fn get_post(
type Response = GetPostResponse; data: Query<GetPost>,
context: Data<LemmyContext>,
) -> Result<Json<GetPostResponse>, LemmyError> {
let local_user_view = local_user_view_from_jwt_opt(data.auth.as_ref(), &context).await;
let local_site = LocalSite::read(&mut context.pool()).await?;
#[tracing::instrument(skip(context))] check_private_instance(&local_user_view, &local_site)?;
async fn perform(&self, context: &Data<LemmyContext>) -> Result<GetPostResponse, LemmyError> {
let data: &GetPost = self;
let local_user_view = local_user_view_from_jwt_opt(data.auth.as_ref(), context).await;
let local_site = LocalSite::read(&mut context.pool()).await?;
check_private_instance(&local_user_view, &local_site)?; let person_id = local_user_view.as_ref().map(|u| u.person.id);
let person_id = local_user_view.as_ref().map(|u| u.person.id); // I'd prefer fetching the post_view by a comment join, but it adds a lot of boilerplate
let post_id = if let Some(id) = data.id {
id
} else if let Some(comment_id) = data.comment_id {
Comment::read(&mut context.pool(), comment_id)
.await
.with_lemmy_type(LemmyErrorType::CouldntFindPost)?
.post_id
} else {
Err(LemmyErrorType::CouldntFindPost)?
};
// I'd prefer fetching the post_view by a comment join, but it adds a lot of boilerplate // Check to see if the person is a mod or admin, to show deleted / removed
let post_id = if let Some(id) = data.id { let community_id = Post::read(&mut context.pool(), post_id).await?.community_id;
id let is_mod_or_admin = is_mod_or_admin_opt(
} else if let Some(comment_id) = data.comment_id { &mut context.pool(),
Comment::read(&mut context.pool(), comment_id) local_user_view.as_ref(),
.await Some(community_id),
.with_lemmy_type(LemmyErrorType::CouldntFindPost)? )
.post_id .await
} else { .is_ok();
Err(LemmyErrorType::CouldntFindPost)?
};
// Check to see if the person is a mod or admin, to show deleted / removed let post_view = PostView::read(
let community_id = Post::read(&mut context.pool(), post_id).await?.community_id; &mut context.pool(),
let is_mod_or_admin = is_mod_or_admin_opt( post_id,
&mut context.pool(), person_id,
local_user_view.as_ref(), Some(is_mod_or_admin),
Some(community_id), )
) .await
.await .with_lemmy_type(LemmyErrorType::CouldntFindPost)?;
.is_ok();
let post_view = PostView::read( // Mark the post as read
&mut context.pool(), let post_id = post_view.post.id;
post_id, if let Some(person_id) = person_id {
person_id, mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
Some(is_mod_or_admin),
)
.await
.with_lemmy_type(LemmyErrorType::CouldntFindPost)?;
// Mark the post as read
let post_id = post_view.post.id;
if let Some(person_id) = person_id {
mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
}
// Necessary for the sidebar subscribed
let community_view = CommunityView::read(
&mut context.pool(),
community_id,
person_id,
Some(is_mod_or_admin),
)
.await
.with_lemmy_type(LemmyErrorType::CouldntFindCommunity)?;
// Insert into PersonPostAggregates
// to update the read_comments count
if let Some(person_id) = person_id {
let read_comments = post_view.counts.comments;
let person_post_agg_form = PersonPostAggregatesForm {
person_id,
post_id,
read_comments,
..PersonPostAggregatesForm::default()
};
PersonPostAggregates::upsert(&mut context.pool(), &person_post_agg_form)
.await
.with_lemmy_type(LemmyErrorType::CouldntFindPost)?;
}
let moderators =
CommunityModeratorView::for_community(&mut context.pool(), community_id).await?;
// Fetch the cross_posts
let cross_posts = if let Some(url) = &post_view.post.url {
let mut x_posts = PostQuery {
url_search: Some(url.inner().as_str().into()),
..Default::default()
}
.list(&mut context.pool())
.await?;
// Don't return this post as one of the cross_posts
x_posts.retain(|x| x.post.id != post_id);
x_posts
} else {
Vec::new()
};
// Return the jwt
Ok(GetPostResponse {
post_view,
community_view,
moderators,
cross_posts,
})
} }
// Necessary for the sidebar subscribed
let community_view = CommunityView::read(
&mut context.pool(),
community_id,
person_id,
Some(is_mod_or_admin),
)
.await
.with_lemmy_type(LemmyErrorType::CouldntFindCommunity)?;
// Insert into PersonPostAggregates
// to update the read_comments count
if let Some(person_id) = person_id {
let read_comments = post_view.counts.comments;
let person_post_agg_form = PersonPostAggregatesForm {
person_id,
post_id,
read_comments,
..PersonPostAggregatesForm::default()
};
PersonPostAggregates::upsert(&mut context.pool(), &person_post_agg_form)
.await
.with_lemmy_type(LemmyErrorType::CouldntFindPost)?;
}
let moderators = CommunityModeratorView::for_community(&mut context.pool(), community_id).await?;
// Fetch the cross_posts
let cross_posts = if let Some(url) = &post_view.post.url {
let mut x_posts = PostQuery {
url_search: Some(url.inner().as_str().into()),
..Default::default()
}
.list(&mut context.pool())
.await?;
// Don't return this post as one of the cross_posts
x_posts.retain(|x| x.post.id != post_id);
x_posts
} else {
Vec::new()
};
// Return the jwt
Ok(Json(GetPostResponse {
post_view,
community_view,
moderators,
cross_posts,
}))
} }

View file

@ -1,4 +1,4 @@
mod create; pub mod create;
mod delete; pub mod delete;
mod read; pub mod read;
mod update; pub mod update;

View file

@ -1,5 +1,4 @@
use crate::PerformCrud; use actix_web::web::{Data, Json, Query};
use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
private_message::{GetPrivateMessages, PrivateMessagesResponse}, private_message::{GetPrivateMessages, PrivateMessagesResponse},
@ -8,40 +7,34 @@ use lemmy_api_common::{
use lemmy_db_views::private_message_view::PrivateMessageQuery; use lemmy_db_views::private_message_view::PrivateMessageQuery;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::LemmyError;
#[async_trait::async_trait(?Send)] #[tracing::instrument(skip(context))]
impl PerformCrud for GetPrivateMessages { pub async fn get_private_message(
type Response = PrivateMessagesResponse; data: Query<GetPrivateMessages>,
context: Data<LemmyContext>,
) -> Result<Json<PrivateMessagesResponse>, LemmyError> {
let local_user_view = local_user_view_from_jwt(data.auth.as_ref(), &context).await?;
let person_id = local_user_view.person.id;
#[tracing::instrument(skip(self, context))] let page = data.page;
async fn perform( let limit = data.limit;
&self, let unread_only = data.unread_only;
context: &Data<LemmyContext>, let mut messages = PrivateMessageQuery {
) -> Result<PrivateMessagesResponse, LemmyError> { page,
let data: &GetPrivateMessages = self; limit,
let local_user_view = local_user_view_from_jwt(data.auth.as_ref(), context).await?; unread_only,
let person_id = local_user_view.person.id;
let page = data.page;
let limit = data.limit;
let unread_only = data.unread_only;
let mut messages = PrivateMessageQuery {
page,
limit,
unread_only,
}
.list(&mut context.pool(), person_id)
.await?;
// Messages sent by ourselves should be marked as read. The `read` column in database is only
// for the recipient, and shouldnt be exposed to sender.
messages.iter_mut().for_each(|pmv| {
if pmv.creator.id == person_id {
pmv.private_message.read = true
}
});
Ok(PrivateMessagesResponse {
private_messages: messages,
})
} }
.list(&mut context.pool(), person_id)
.await?;
// Messages sent by ourselves should be marked as read. The `read` column in database is only
// for the recipient, and shouldnt be exposed to sender.
messages.iter_mut().for_each(|pmv| {
if pmv.creator.id == person_id {
pmv.private_message.read = true
}
});
Ok(Json(PrivateMessagesResponse {
private_messages: messages,
}))
} }

View file

@ -1,9 +1,6 @@
use crate::{ use crate::site::{application_question_check, site_default_post_listing_type_check};
site::{application_question_check, site_default_post_listing_type_check},
PerformCrud,
};
use activitypub_federation::http_signatures::generate_actor_keypair; use activitypub_federation::http_signatures::generate_actor_keypair;
use actix_web::web::Data; use actix_web::web::{Data, Json};
use lemmy_api_common::{ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
site::{CreateSite, SiteResponse}, site::{CreateSite, SiteResponse},
@ -43,108 +40,105 @@ use lemmy_utils::{
}; };
use url::Url; use url::Url;
#[async_trait::async_trait(?Send)] #[tracing::instrument(skip(context))]
impl PerformCrud for CreateSite { pub async fn create_site(
type Response = SiteResponse; data: Json<CreateSite>,
context: Data<LemmyContext>,
) -> Result<Json<SiteResponse>, 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))] // Make sure user is an admin; other types of users should not create site data...
async fn perform(&self, context: &Data<LemmyContext>) -> Result<SiteResponse, LemmyError> { is_admin(&local_user_view)?;
let data: &CreateSite = self;
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let local_site = LocalSite::read(&mut context.pool()).await?;
// Make sure user is an admin; other types of users should not create site data... validate_create_payload(&local_site, &data)?;
is_admin(&local_user_view)?;
validate_create_payload(&local_site, data)?; let actor_id: DbUrl = Url::parse(&context.settings().get_protocol_and_hostname())?.into();
let inbox_url = Some(generate_site_inbox_url(&actor_id)?);
let keypair = generate_actor_keypair()?;
let name = sanitize_html(&data.name);
let sidebar = sanitize_html_opt(&data.sidebar);
let description = sanitize_html_opt(&data.description);
let actor_id: DbUrl = Url::parse(&context.settings().get_protocol_and_hostname())?.into(); let site_form = SiteUpdateForm::builder()
let inbox_url = Some(generate_site_inbox_url(&actor_id)?); .name(Some(name))
let keypair = generate_actor_keypair()?; .sidebar(diesel_option_overwrite(sidebar))
let name = sanitize_html(&data.name); .description(diesel_option_overwrite(description))
let sidebar = sanitize_html_opt(&data.sidebar); .icon(diesel_option_overwrite_to_url(&data.icon)?)
let description = sanitize_html_opt(&data.description); .banner(diesel_option_overwrite_to_url(&data.banner)?)
.actor_id(Some(actor_id))
.last_refreshed_at(Some(naive_now()))
.inbox_url(inbox_url)
.private_key(Some(Some(keypair.private_key)))
.public_key(Some(keypair.public_key))
.build();
let site_form = SiteUpdateForm::builder() let site_id = local_site.site_id;
.name(Some(name))
.sidebar(diesel_option_overwrite(sidebar))
.description(diesel_option_overwrite(description))
.icon(diesel_option_overwrite_to_url(&data.icon)?)
.banner(diesel_option_overwrite_to_url(&data.banner)?)
.actor_id(Some(actor_id))
.last_refreshed_at(Some(naive_now()))
.inbox_url(inbox_url)
.private_key(Some(Some(keypair.private_key)))
.public_key(Some(keypair.public_key))
.build();
let site_id = local_site.site_id; Site::update(&mut context.pool(), site_id, &site_form).await?;
Site::update(&mut context.pool(), site_id, &site_form).await?; let application_question = sanitize_html_opt(&data.application_question);
let default_theme = sanitize_html_opt(&data.default_theme);
let legal_information = sanitize_html_opt(&data.legal_information);
let application_question = sanitize_html_opt(&data.application_question); let local_site_form = LocalSiteUpdateForm::builder()
let default_theme = sanitize_html_opt(&data.default_theme); // Set the site setup to true
let legal_information = sanitize_html_opt(&data.legal_information); .site_setup(Some(true))
.enable_downvotes(data.enable_downvotes)
.registration_mode(data.registration_mode)
.enable_nsfw(data.enable_nsfw)
.community_creation_admin_only(data.community_creation_admin_only)
.require_email_verification(data.require_email_verification)
.application_question(diesel_option_overwrite(application_question))
.private_instance(data.private_instance)
.default_theme(default_theme)
.default_post_listing_type(data.default_post_listing_type)
.legal_information(diesel_option_overwrite(legal_information))
.application_email_admins(data.application_email_admins)
.hide_modlog_mod_names(data.hide_modlog_mod_names)
.updated(Some(Some(naive_now())))
.slur_filter_regex(diesel_option_overwrite(data.slur_filter_regex.clone()))
.actor_name_max_length(data.actor_name_max_length)
.federation_enabled(data.federation_enabled)
.captcha_enabled(data.captcha_enabled)
.captcha_difficulty(data.captcha_difficulty.clone())
.build();
let local_site_form = LocalSiteUpdateForm::builder() LocalSite::update(&mut context.pool(), &local_site_form).await?;
// Set the site setup to true
.site_setup(Some(true))
.enable_downvotes(data.enable_downvotes)
.registration_mode(data.registration_mode)
.enable_nsfw(data.enable_nsfw)
.community_creation_admin_only(data.community_creation_admin_only)
.require_email_verification(data.require_email_verification)
.application_question(diesel_option_overwrite(application_question))
.private_instance(data.private_instance)
.default_theme(default_theme)
.default_post_listing_type(data.default_post_listing_type)
.legal_information(diesel_option_overwrite(legal_information))
.application_email_admins(data.application_email_admins)
.hide_modlog_mod_names(data.hide_modlog_mod_names)
.updated(Some(Some(naive_now())))
.slur_filter_regex(diesel_option_overwrite(data.slur_filter_regex.clone()))
.actor_name_max_length(data.actor_name_max_length)
.federation_enabled(data.federation_enabled)
.captcha_enabled(data.captcha_enabled)
.captcha_difficulty(data.captcha_difficulty.clone())
.build();
LocalSite::update(&mut context.pool(), &local_site_form).await?; let local_site_rate_limit_form = LocalSiteRateLimitUpdateForm::builder()
.message(data.rate_limit_message)
.message_per_second(data.rate_limit_message_per_second)
.post(data.rate_limit_post)
.post_per_second(data.rate_limit_post_per_second)
.register(data.rate_limit_register)
.register_per_second(data.rate_limit_register_per_second)
.image(data.rate_limit_image)
.image_per_second(data.rate_limit_image_per_second)
.comment(data.rate_limit_comment)
.comment_per_second(data.rate_limit_comment_per_second)
.search(data.rate_limit_search)
.search_per_second(data.rate_limit_search_per_second)
.build();
let local_site_rate_limit_form = LocalSiteRateLimitUpdateForm::builder() LocalSiteRateLimit::update(&mut context.pool(), &local_site_rate_limit_form).await?;
.message(data.rate_limit_message)
.message_per_second(data.rate_limit_message_per_second)
.post(data.rate_limit_post)
.post_per_second(data.rate_limit_post_per_second)
.register(data.rate_limit_register)
.register_per_second(data.rate_limit_register_per_second)
.image(data.rate_limit_image)
.image_per_second(data.rate_limit_image_per_second)
.comment(data.rate_limit_comment)
.comment_per_second(data.rate_limit_comment_per_second)
.search(data.rate_limit_search)
.search_per_second(data.rate_limit_search_per_second)
.build();
LocalSiteRateLimit::update(&mut context.pool(), &local_site_rate_limit_form).await?; let site_view = SiteView::read_local(&mut context.pool()).await?;
let site_view = SiteView::read_local(&mut context.pool()).await?; let new_taglines = data.taglines.clone();
let taglines = Tagline::replace(&mut context.pool(), local_site.id, new_taglines).await?;
let new_taglines = data.taglines.clone(); let rate_limit_config =
let taglines = Tagline::replace(&mut context.pool(), local_site.id, new_taglines).await?; local_site_rate_limit_to_rate_limit_config(&site_view.local_site_rate_limit);
context
.settings_updated_channel()
.send(rate_limit_config)
.await?;
let rate_limit_config = Ok(Json(SiteResponse {
local_site_rate_limit_to_rate_limit_config(&site_view.local_site_rate_limit); site_view,
context taglines,
.settings_updated_channel() }))
.send(rate_limit_config)
.await?;
Ok(SiteResponse {
site_view,
taglines,
})
}
} }
fn validate_create_payload(local_site: &LocalSite, create_site: &CreateSite) -> LemmyResult<()> { fn validate_create_payload(local_site: &LocalSite, create_site: &CreateSite) -> LemmyResult<()> {

View file

@ -1,9 +1,9 @@
use lemmy_db_schema::{ListingType, RegistrationMode}; use lemmy_db_schema::{ListingType, RegistrationMode};
use lemmy_utils::error::{LemmyErrorType, LemmyResult}; use lemmy_utils::error::{LemmyErrorType, LemmyResult};
mod create; pub mod create;
mod read; pub mod read;
mod update; pub mod update;
/// Checks whether the default post listing type is valid for a site. /// Checks whether the default post listing type is valid for a site.
pub fn site_default_post_listing_type_check( pub fn site_default_post_listing_type_check(

View file

@ -1,5 +1,4 @@
use crate::PerformCrud; use actix_web::web::{Data, Json, Query};
use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
sensitive::Sensitive, sensitive::Sensitive,
@ -28,76 +27,72 @@ use lemmy_utils::{
version, version,
}; };
#[async_trait::async_trait(?Send)] #[tracing::instrument(skip(context))]
impl PerformCrud for GetSite { pub async fn get_site(
type Response = GetSiteResponse; data: Query<GetSite>,
context: Data<LemmyContext>,
) -> Result<Json<GetSiteResponse>, LemmyError> {
let site_view = SiteView::read_local(&mut context.pool()).await?;
#[tracing::instrument(skip(context))] let admins = PersonView::admins(&mut context.pool()).await?;
async fn perform(&self, context: &Data<LemmyContext>) -> Result<GetSiteResponse, LemmyError> {
let data: &GetSite = self;
let site_view = SiteView::read_local(&mut context.pool()).await?; // Build the local user
let my_user = if let Some(local_user_view) =
local_user_settings_view_from_jwt_opt(data.auth.as_ref(), &context).await
{
let person_id = local_user_view.person.id;
let local_user_id = local_user_view.local_user.id;
let admins = PersonView::admins(&mut context.pool()).await?; let follows = CommunityFollowerView::for_person(&mut context.pool(), person_id)
.await
.with_lemmy_type(LemmyErrorType::SystemErrLogin)?;
// Build the local user let person_id = local_user_view.person.id;
let my_user = if let Some(local_user_view) = let community_blocks = CommunityBlockView::for_person(&mut context.pool(), person_id)
local_user_settings_view_from_jwt_opt(data.auth.as_ref(), context).await .await
{ .with_lemmy_type(LemmyErrorType::SystemErrLogin)?;
let person_id = local_user_view.person.id;
let local_user_id = local_user_view.local_user.id;
let follows = CommunityFollowerView::for_person(&mut context.pool(), person_id) let person_id = local_user_view.person.id;
.await let person_blocks = PersonBlockView::for_person(&mut context.pool(), person_id)
.with_lemmy_type(LemmyErrorType::SystemErrLogin)?; .await
.with_lemmy_type(LemmyErrorType::SystemErrLogin)?;
let person_id = local_user_view.person.id; let moderates = CommunityModeratorView::for_person(&mut context.pool(), person_id)
let community_blocks = CommunityBlockView::for_person(&mut context.pool(), person_id) .await
.await .with_lemmy_type(LemmyErrorType::SystemErrLogin)?;
.with_lemmy_type(LemmyErrorType::SystemErrLogin)?;
let person_id = local_user_view.person.id; let discussion_languages = LocalUserLanguage::read(&mut context.pool(), local_user_id)
let person_blocks = PersonBlockView::for_person(&mut context.pool(), person_id) .await
.await .with_lemmy_type(LemmyErrorType::SystemErrLogin)?;
.with_lemmy_type(LemmyErrorType::SystemErrLogin)?;
let moderates = CommunityModeratorView::for_person(&mut context.pool(), person_id) Some(MyUserInfo {
.await local_user_view,
.with_lemmy_type(LemmyErrorType::SystemErrLogin)?; follows,
moderates,
let discussion_languages = LocalUserLanguage::read(&mut context.pool(), local_user_id) community_blocks,
.await person_blocks,
.with_lemmy_type(LemmyErrorType::SystemErrLogin)?;
Some(MyUserInfo {
local_user_view,
follows,
moderates,
community_blocks,
person_blocks,
discussion_languages,
})
} else {
None
};
let all_languages = Language::read_all(&mut context.pool()).await?;
let discussion_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?;
let taglines = Tagline::get_all(&mut context.pool(), site_view.local_site.id).await?;
let custom_emojis =
CustomEmojiView::get_all(&mut context.pool(), site_view.local_site.id).await?;
Ok(GetSiteResponse {
site_view,
admins,
version: version::VERSION.to_string(),
my_user,
all_languages,
discussion_languages, discussion_languages,
taglines,
custom_emojis,
}) })
} } else {
None
};
let all_languages = Language::read_all(&mut context.pool()).await?;
let discussion_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?;
let taglines = Tagline::get_all(&mut context.pool(), site_view.local_site.id).await?;
let custom_emojis =
CustomEmojiView::get_all(&mut context.pool(), site_view.local_site.id).await?;
Ok(Json(GetSiteResponse {
site_view,
admins,
version: version::VERSION.to_string(),
my_user,
all_languages,
discussion_languages,
taglines,
custom_emojis,
}))
} }
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]

View file

@ -1,8 +1,5 @@
use crate::{ use crate::site::{application_question_check, site_default_post_listing_type_check};
site::{application_question_check, site_default_post_listing_type_check}, use actix_web::web::{Data, Json};
PerformCrud,
};
use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
site::{EditSite, SiteResponse}, site::{EditSite, SiteResponse},
@ -43,147 +40,142 @@ use lemmy_utils::{
}, },
}; };
#[async_trait::async_trait(?Send)] #[tracing::instrument(skip(context))]
impl PerformCrud for EditSite { pub async fn update_site(
type Response = SiteResponse; data: Json<EditSite>,
context: Data<LemmyContext>,
) -> Result<Json<SiteResponse>, LemmyError> {
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
let site_view = SiteView::read_local(&mut context.pool()).await?;
let local_site = site_view.local_site;
let site = site_view.site;
#[tracing::instrument(skip(context))] // Make sure user is an admin; other types of users should not update site data...
async fn perform(&self, context: &Data<LemmyContext>) -> Result<SiteResponse, LemmyError> { is_admin(&local_user_view)?;
let data: &EditSite = self;
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let site_view = SiteView::read_local(&mut context.pool()).await?;
let local_site = site_view.local_site;
let site = site_view.site;
// Make sure user is an admin; other types of users should not update site data... validate_update_payload(&local_site, &data)?;
is_admin(&local_user_view)?;
validate_update_payload(&local_site, data)?; if let Some(discussion_languages) = data.discussion_languages.clone() {
SiteLanguage::update(&mut context.pool(), discussion_languages.clone(), &site).await?;
if let Some(discussion_languages) = data.discussion_languages.clone() {
SiteLanguage::update(&mut context.pool(), discussion_languages.clone(), &site).await?;
}
let name = sanitize_html_opt(&data.name);
let sidebar = sanitize_html_opt(&data.sidebar);
let description = sanitize_html_opt(&data.description);
let site_form = SiteUpdateForm::builder()
.name(name)
.sidebar(diesel_option_overwrite(sidebar))
.description(diesel_option_overwrite(description))
.icon(diesel_option_overwrite_to_url(&data.icon)?)
.banner(diesel_option_overwrite_to_url(&data.banner)?)
.updated(Some(Some(naive_now())))
.build();
Site::update(&mut context.pool(), site.id, &site_form)
.await
// Ignore errors for all these, so as to not throw errors if no update occurs
// Diesel will throw an error for empty update forms
.ok();
let application_question = sanitize_html_opt(&data.application_question);
let default_theme = sanitize_html_opt(&data.default_theme);
let legal_information = sanitize_html_opt(&data.legal_information);
let local_site_form = LocalSiteUpdateForm::builder()
.enable_downvotes(data.enable_downvotes)
.registration_mode(data.registration_mode)
.enable_nsfw(data.enable_nsfw)
.community_creation_admin_only(data.community_creation_admin_only)
.require_email_verification(data.require_email_verification)
.application_question(diesel_option_overwrite(application_question))
.private_instance(data.private_instance)
.default_theme(default_theme)
.default_post_listing_type(data.default_post_listing_type)
.legal_information(diesel_option_overwrite(legal_information))
.application_email_admins(data.application_email_admins)
.hide_modlog_mod_names(data.hide_modlog_mod_names)
.updated(Some(Some(naive_now())))
.slur_filter_regex(diesel_option_overwrite(data.slur_filter_regex.clone()))
.actor_name_max_length(data.actor_name_max_length)
.federation_enabled(data.federation_enabled)
.captcha_enabled(data.captcha_enabled)
.captcha_difficulty(data.captcha_difficulty.clone())
.reports_email_admins(data.reports_email_admins)
.build();
let update_local_site = LocalSite::update(&mut context.pool(), &local_site_form)
.await
.ok();
let local_site_rate_limit_form = LocalSiteRateLimitUpdateForm::builder()
.message(data.rate_limit_message)
.message_per_second(data.rate_limit_message_per_second)
.post(data.rate_limit_post)
.post_per_second(data.rate_limit_post_per_second)
.register(data.rate_limit_register)
.register_per_second(data.rate_limit_register_per_second)
.image(data.rate_limit_image)
.image_per_second(data.rate_limit_image_per_second)
.comment(data.rate_limit_comment)
.comment_per_second(data.rate_limit_comment_per_second)
.search(data.rate_limit_search)
.search_per_second(data.rate_limit_search_per_second)
.build();
LocalSiteRateLimit::update(&mut context.pool(), &local_site_rate_limit_form)
.await
.ok();
// Replace the blocked and allowed instances
let allowed = data.allowed_instances.clone();
FederationAllowList::replace(&mut context.pool(), allowed).await?;
let blocked = data.blocked_instances.clone();
FederationBlockList::replace(&mut context.pool(), blocked).await?;
// TODO can't think of a better way to do this.
// If the server suddenly requires email verification, or required applications, no old users
// will be able to log in. It really only wants this to be a requirement for NEW signups.
// So if it was set from false, to true, you need to update all current users columns to be verified.
let old_require_application =
local_site.registration_mode == RegistrationMode::RequireApplication;
let new_require_application = update_local_site
.as_ref()
.map(|ols| ols.registration_mode == RegistrationMode::RequireApplication)
.unwrap_or(false);
if !old_require_application && new_require_application {
LocalUser::set_all_users_registration_applications_accepted(&mut context.pool())
.await
.with_lemmy_type(LemmyErrorType::CouldntSetAllRegistrationsAccepted)?;
}
let new_require_email_verification = update_local_site
.as_ref()
.map(|ols| ols.require_email_verification)
.unwrap_or(false);
if !local_site.require_email_verification && new_require_email_verification {
LocalUser::set_all_users_email_verified(&mut context.pool())
.await
.with_lemmy_type(LemmyErrorType::CouldntSetAllEmailVerified)?;
}
let new_taglines = data.taglines.clone();
let taglines = Tagline::replace(&mut context.pool(), local_site.id, new_taglines).await?;
let site_view = SiteView::read_local(&mut context.pool()).await?;
let rate_limit_config =
local_site_rate_limit_to_rate_limit_config(&site_view.local_site_rate_limit);
context
.settings_updated_channel()
.send(rate_limit_config)
.await?;
let res = SiteResponse {
site_view,
taglines,
};
Ok(res)
} }
let name = sanitize_html_opt(&data.name);
let sidebar = sanitize_html_opt(&data.sidebar);
let description = sanitize_html_opt(&data.description);
let site_form = SiteUpdateForm::builder()
.name(name)
.sidebar(diesel_option_overwrite(sidebar))
.description(diesel_option_overwrite(description))
.icon(diesel_option_overwrite_to_url(&data.icon)?)
.banner(diesel_option_overwrite_to_url(&data.banner)?)
.updated(Some(Some(naive_now())))
.build();
Site::update(&mut context.pool(), site.id, &site_form)
.await
// Ignore errors for all these, so as to not throw errors if no update occurs
// Diesel will throw an error for empty update forms
.ok();
let application_question = sanitize_html_opt(&data.application_question);
let default_theme = sanitize_html_opt(&data.default_theme);
let legal_information = sanitize_html_opt(&data.legal_information);
let local_site_form = LocalSiteUpdateForm::builder()
.enable_downvotes(data.enable_downvotes)
.registration_mode(data.registration_mode)
.enable_nsfw(data.enable_nsfw)
.community_creation_admin_only(data.community_creation_admin_only)
.require_email_verification(data.require_email_verification)
.application_question(diesel_option_overwrite(application_question))
.private_instance(data.private_instance)
.default_theme(default_theme)
.default_post_listing_type(data.default_post_listing_type)
.legal_information(diesel_option_overwrite(legal_information))
.application_email_admins(data.application_email_admins)
.hide_modlog_mod_names(data.hide_modlog_mod_names)
.updated(Some(Some(naive_now())))
.slur_filter_regex(diesel_option_overwrite(data.slur_filter_regex.clone()))
.actor_name_max_length(data.actor_name_max_length)
.federation_enabled(data.federation_enabled)
.captcha_enabled(data.captcha_enabled)
.captcha_difficulty(data.captcha_difficulty.clone())
.reports_email_admins(data.reports_email_admins)
.build();
let update_local_site = LocalSite::update(&mut context.pool(), &local_site_form)
.await
.ok();
let local_site_rate_limit_form = LocalSiteRateLimitUpdateForm::builder()
.message(data.rate_limit_message)
.message_per_second(data.rate_limit_message_per_second)
.post(data.rate_limit_post)
.post_per_second(data.rate_limit_post_per_second)
.register(data.rate_limit_register)
.register_per_second(data.rate_limit_register_per_second)
.image(data.rate_limit_image)
.image_per_second(data.rate_limit_image_per_second)
.comment(data.rate_limit_comment)
.comment_per_second(data.rate_limit_comment_per_second)
.search(data.rate_limit_search)
.search_per_second(data.rate_limit_search_per_second)
.build();
LocalSiteRateLimit::update(&mut context.pool(), &local_site_rate_limit_form)
.await
.ok();
// Replace the blocked and allowed instances
let allowed = data.allowed_instances.clone();
FederationAllowList::replace(&mut context.pool(), allowed).await?;
let blocked = data.blocked_instances.clone();
FederationBlockList::replace(&mut context.pool(), blocked).await?;
// TODO can't think of a better way to do this.
// If the server suddenly requires email verification, or required applications, no old users
// will be able to log in. It really only wants this to be a requirement for NEW signups.
// So if it was set from false, to true, you need to update all current users columns to be verified.
let old_require_application =
local_site.registration_mode == RegistrationMode::RequireApplication;
let new_require_application = update_local_site
.as_ref()
.map(|ols| ols.registration_mode == RegistrationMode::RequireApplication)
.unwrap_or(false);
if !old_require_application && new_require_application {
LocalUser::set_all_users_registration_applications_accepted(&mut context.pool())
.await
.with_lemmy_type(LemmyErrorType::CouldntSetAllRegistrationsAccepted)?;
}
let new_require_email_verification = update_local_site
.as_ref()
.map(|ols| ols.require_email_verification)
.unwrap_or(false);
if !local_site.require_email_verification && new_require_email_verification {
LocalUser::set_all_users_email_verified(&mut context.pool())
.await
.with_lemmy_type(LemmyErrorType::CouldntSetAllEmailVerified)?;
}
let new_taglines = data.taglines.clone();
let taglines = Tagline::replace(&mut context.pool(), local_site.id, new_taglines).await?;
let site_view = SiteView::read_local(&mut context.pool()).await?;
let rate_limit_config =
local_site_rate_limit_to_rate_limit_config(&site_view.local_site_rate_limit);
context
.settings_updated_channel()
.send(rate_limit_config)
.await?;
Ok(Json(SiteResponse {
site_view,
taglines,
}))
} }
fn validate_update_payload(local_site: &LocalSite, edit_site: &EditSite) -> LemmyResult<()> { fn validate_update_payload(local_site: &LocalSite, edit_site: &EditSite) -> LemmyResult<()> {

View file

@ -25,7 +25,7 @@ use activitypub_federation::{
}; };
use lemmy_api_common::{ use lemmy_api_common::{
build_response::send_local_notifs, build_response::send_local_notifs,
comment::{CommentResponse, CreateComment, EditComment}, 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 +43,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 CreateComment {
type Response = CommentResponse;
async fn send_activity(
_request: &Self,
response: &Self::Response,
context: &Data<LemmyContext>,
) -> Result<(), LemmyError> {
CreateOrUpdateNote::send(
&response.comment_view.comment,
response.comment_view.creator.id,
CreateOrUpdateType::Create,
context,
)
.await
}
}
#[async_trait::async_trait] #[async_trait::async_trait]
impl SendActivity for EditComment { impl SendActivity for EditComment {
type Response = CommentResponse; type Response = CommentResponse;
@ -72,10 +53,10 @@ impl SendActivity for EditComment {
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
CreateOrUpdateNote::send( CreateOrUpdateNote::send(
&response.comment_view.comment, response.comment_view.comment.clone(),
response.comment_view.creator.id, response.comment_view.creator.id,
CreateOrUpdateType::Update, CreateOrUpdateType::Update,
context, context.reset_request_count(),
) )
.await .await
} }
@ -83,11 +64,11 @@ impl SendActivity for EditComment {
impl CreateOrUpdateNote { impl CreateOrUpdateNote {
#[tracing::instrument(skip(comment, person_id, kind, context))] #[tracing::instrument(skip(comment, person_id, kind, context))]
async fn send( pub(crate) async fn send(
comment: &Comment, comment: Comment,
person_id: PersonId, person_id: PersonId,
kind: CreateOrUpdateType, kind: CreateOrUpdateType,
context: &Data<LemmyContext>, context: Data<LemmyContext>,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
// TODO: might be helpful to add a comment method to retrieve community directly // TODO: might be helpful to add a comment method to retrieve community directly
let post_id = comment.post_id; let post_id = comment.post_id;
@ -102,7 +83,7 @@ impl CreateOrUpdateNote {
kind.clone(), kind.clone(),
&context.settings().get_protocol_and_hostname(), &context.settings().get_protocol_and_hostname(),
)?; )?;
let note = ApubComment(comment.clone()).into_json(context).await?; let note = ApubComment(comment).into_json(&context).await?;
let create_or_update = CreateOrUpdateNote { let create_or_update = CreateOrUpdateNote {
actor: person.id().into(), actor: person.id().into(),
@ -130,12 +111,12 @@ impl CreateOrUpdateNote {
.collect(); .collect();
let mut inboxes = vec![]; let mut inboxes = vec![];
for t in tagged_users { for t in tagged_users {
let person = t.dereference(context).await?; let person = t.dereference(&context).await?;
inboxes.push(person.shared_inbox_or_inbox()); inboxes.push(person.shared_inbox_or_inbox());
} }
let activity = AnnouncableActivities::CreateOrUpdateComment(create_or_update); let activity = AnnouncableActivities::CreateOrUpdateComment(create_or_update);
send_activity_in_community(activity, &person, &community, inboxes, false, context).await send_activity_in_community(activity, &person, &community, inboxes, false, &context).await
} }
} }

View file

@ -1,6 +1,9 @@
use crate::{ use crate::{
objects::{community::ApubCommunity, person::ApubPerson}, objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::{create_or_update::page::CreateOrUpdatePage, CreateOrUpdateType}, protocol::activities::{
create_or_update::{note::CreateOrUpdateNote, page::CreateOrUpdatePage},
CreateOrUpdateType,
},
CONTEXT, CONTEXT,
}; };
use activitypub_federation::{ use activitypub_federation::{
@ -217,15 +220,17 @@ pub async fn match_outgoing_activities(
data: SendActivityData, data: SendActivityData,
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
) -> LemmyResult<()> { ) -> LemmyResult<()> {
let fed_task = match data { let context = context.reset_request_count();
SendActivityData::CreatePost(post) => { let fed_task = async {
let creator_id = post.creator_id; match data {
CreateOrUpdatePage::send( SendActivityData::CreatePost(post) => {
post, let creator_id = post.creator_id;
creator_id, CreateOrUpdatePage::send(post, creator_id, CreateOrUpdateType::Create, context).await
CreateOrUpdateType::Create, }
context.reset_request_count(), SendActivityData::CreateComment(comment) => {
) let creator_id = comment.creator_id;
CreateOrUpdateNote::send(comment, creator_id, CreateOrUpdateType::Create, context).await
}
} }
}; };
if *SYNCHRONOUS_FEDERATION { if *SYNCHRONOUS_FEDERATION {

View file

@ -16,7 +16,7 @@ use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorExt2, LemmyErrorType}; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorExt2, LemmyErrorType};
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
pub async fn read_community( pub async fn get_community(
data: Query<GetCommunity>, data: Query<GetCommunity>,
context: Data<LemmyContext>, context: Data<LemmyContext>,
) -> Result<Json<GetCommunityResponse>, LemmyError> { ) -> Result<Json<GetCommunityResponse>, LemmyError> {

@ -1 +1 @@
Subproject commit 713ceed9c7ef84deaa222e68361e670e0763cd83 Subproject commit 1c42c579460871de7b4ea18e58dc25543b80d289

View file

@ -1,19 +1,12 @@
use actix_web::{guard, web, Error, HttpResponse, Result}; use actix_web::{guard, web, Error, HttpResponse, Result};
use lemmy_api::Perform; use lemmy_api::{
comment::{distinguish::distinguish_comment, save::save_comment},
comment_report::{list::list_comment_reports, resolve::resolve_comment_report},
local_user::notifications::mark_reply_read::mark_reply_as_read,
Perform,
};
use lemmy_api_common::{ use lemmy_api_common::{
comment::{ comment::{CreateCommentLike, CreateCommentReport, DeleteComment, EditComment, RemoveComment},
CreateComment,
CreateCommentLike,
CreateCommentReport,
DeleteComment,
DistinguishComment,
EditComment,
GetComment,
ListCommentReports,
RemoveComment,
ResolveCommentReport,
SaveComment,
},
community::{ community::{
AddModToCommunity, AddModToCommunity,
BanFromCommunity, BanFromCommunity,
@ -23,7 +16,6 @@ use lemmy_api_common::{
EditCommunity, EditCommunity,
FollowCommunity, FollowCommunity,
HideCommunity, HideCommunity,
ListCommunities,
RemoveCommunity, RemoveCommunity,
TransferCommunity, TransferCommunity,
}, },
@ -43,7 +35,6 @@ use lemmy_api_common::{
GetUnreadCount, GetUnreadCount,
Login, Login,
MarkAllAsRead, MarkAllAsRead,
MarkCommentReplyAsRead,
MarkPersonMentionAsRead, MarkPersonMentionAsRead,
PasswordChangeAfterReset, PasswordChangeAfterReset,
PasswordReset, PasswordReset,
@ -57,7 +48,6 @@ use lemmy_api_common::{
DeletePost, DeletePost,
EditPost, EditPost,
FeaturePost, FeaturePost,
GetPost,
GetSiteMetadata, GetSiteMetadata,
ListPostReports, ListPostReports,
LockPost, LockPost,
@ -71,18 +61,14 @@ use lemmy_api_common::{
CreatePrivateMessageReport, CreatePrivateMessageReport,
DeletePrivateMessage, DeletePrivateMessage,
EditPrivateMessage, EditPrivateMessage,
GetPrivateMessages,
ListPrivateMessageReports, ListPrivateMessageReports,
MarkPrivateMessageAsRead, MarkPrivateMessageAsRead,
ResolvePrivateMessageReport, ResolvePrivateMessageReport,
}, },
site::{ site::{
ApproveRegistrationApplication, ApproveRegistrationApplication,
CreateSite,
EditSite,
GetFederatedInstances, GetFederatedInstances,
GetModlog, GetModlog,
GetSite,
GetUnreadRegistrationApplicationCount, GetUnreadRegistrationApplicationCount,
LeaveAdmin, LeaveAdmin,
ListRegistrationApplications, ListRegistrationApplications,
@ -92,12 +78,19 @@ use lemmy_api_common::{
PurgePost, PurgePost,
}, },
}; };
use lemmy_api_crud::{post::create::create_post, PerformCrud}; use lemmy_api_crud::{
comment::{create::create_comment, read::get_comment},
community::list::list_communities,
post::{create::create_post, read::get_post},
private_message::read::get_private_message,
site::{create::create_site, read::get_site, update::update_site},
PerformCrud,
};
use lemmy_apub::{ use lemmy_apub::{
api::{ api::{
list_comments::list_comments, list_comments::list_comments,
list_posts::list_posts, list_posts::list_posts,
read_community::read_community, read_community::get_community,
read_person::read_person, read_person::read_person,
resolve_object::resolve_object, resolve_object::resolve_object,
search::search, search::search,
@ -114,10 +107,10 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
.service( .service(
web::scope("/site") web::scope("/site")
.wrap(rate_limit.message()) .wrap(rate_limit.message())
.route("", web::get().to(route_get_crud::<GetSite>)) .route("", web::get().to(get_site))
// Admin Actions // Admin Actions
.route("", web::post().to(route_post_crud::<CreateSite>)) .route("", web::post().to(create_site))
.route("", web::put().to(route_post_crud::<EditSite>)), .route("", web::put().to(update_site)),
) )
.service( .service(
web::resource("/modlog") web::resource("/modlog")
@ -144,10 +137,10 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
.service( .service(
web::scope("/community") web::scope("/community")
.wrap(rate_limit.message()) .wrap(rate_limit.message())
.route("", web::get().to(read_community)) .route("", web::get().to(get_community))
.route("", web::put().to(route_post_crud::<EditCommunity>)) .route("", web::put().to(route_post_crud::<EditCommunity>))
.route("/hide", web::put().to(route_post::<HideCommunity>)) .route("/hide", web::put().to(route_post::<HideCommunity>))
.route("/list", web::get().to(route_get_crud::<ListCommunities>)) .route("/list", web::get().to(list_communities))
.route("/follow", web::post().to(route_post::<FollowCommunity>)) .route("/follow", web::post().to(route_post::<FollowCommunity>))
.route("/block", web::post().to(route_post::<BlockCommunity>)) .route("/block", web::post().to(route_post::<BlockCommunity>))
.route( .route(
@ -179,7 +172,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
.service( .service(
web::scope("/post") web::scope("/post")
.wrap(rate_limit.message()) .wrap(rate_limit.message())
.route("", web::get().to(route_get_crud::<GetPost>)) .route("", web::get().to(get_post))
.route("", web::put().to(route_post_crud::<EditPost>)) .route("", web::put().to(route_post_crud::<EditPost>))
.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>))
@ -209,41 +202,29 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
web::resource("/comment") web::resource("/comment")
.guard(guard::Post()) .guard(guard::Post())
.wrap(rate_limit.comment()) .wrap(rate_limit.comment())
.route(web::post().to(route_post_crud::<CreateComment>)), .route(web::post().to(create_comment)),
) )
.service( .service(
web::scope("/comment") web::scope("/comment")
.wrap(rate_limit.message()) .wrap(rate_limit.message())
.route("", web::get().to(route_get_crud::<GetComment>)) .route("", web::get().to(get_comment))
.route("", web::put().to(route_post_crud::<EditComment>)) .route("", web::put().to(route_post_crud::<EditComment>))
.route("/delete", web::post().to(route_post_crud::<DeleteComment>)) .route("/delete", web::post().to(route_post_crud::<DeleteComment>))
.route("/remove", web::post().to(route_post_crud::<RemoveComment>)) .route("/remove", web::post().to(route_post_crud::<RemoveComment>))
.route( .route("/mark_as_read", web::post().to(mark_reply_as_read))
"/mark_as_read", .route("/distinguish", web::post().to(distinguish_comment))
web::post().to(route_post::<MarkCommentReplyAsRead>),
)
.route(
"/distinguish",
web::post().to(route_post::<DistinguishComment>),
)
.route("/like", web::post().to(route_post::<CreateCommentLike>)) .route("/like", web::post().to(route_post::<CreateCommentLike>))
.route("/save", web::put().to(route_post::<SaveComment>)) .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>))
.route( .route("/report/resolve", web::put().to(resolve_comment_report))
"/report/resolve", .route("/report/list", web::get().to(list_comment_reports)),
web::put().to(route_post::<ResolveCommentReport>),
)
.route(
"/report/list",
web::get().to(route_get::<ListCommentReports>),
),
) )
// Private Message // Private Message
.service( .service(
web::scope("/private_message") web::scope("/private_message")
.wrap(rate_limit.message()) .wrap(rate_limit.message())
.route("/list", web::get().to(route_get_crud::<GetPrivateMessages>)) .route("/list", web::get().to(get_private_message))
.route("", web::post().to(route_post_crud::<CreatePrivateMessage>)) .route("", web::post().to(route_post_crud::<CreatePrivateMessage>))
.route("", web::put().to(route_post_crud::<EditPrivateMessage>)) .route("", web::put().to(route_post_crud::<EditPrivateMessage>))
.route( .route(
@ -447,22 +428,6 @@ where
Ok(HttpResponse::Ok().json(&res)) Ok(HttpResponse::Ok().json(&res))
} }
async fn route_get_crud<'a, Data>(
data: web::Query<Data>,
context: web::Data<LemmyContext>,
apub_data: activitypub_federation::config::Data<LemmyContext>,
) -> Result<HttpResponse, Error>
where
Data: PerformCrud
+ SendActivity<Response = <Data as PerformCrud>::Response>
+ Clone
+ Deserialize<'a>
+ Send
+ 'static,
{
perform_crud::<Data>(data.0, context, apub_data).await
}
async fn route_post_crud<'a, Data>( async fn route_post_crud<'a, Data>(
data: web::Json<Data>, data: web::Json<Data>,
context: web::Data<LemmyContext>, context: web::Data<LemmyContext>,