-use std::convert::TryInto;
-
+use crate::Perform;
use actix_web::web::Data;
-
use lemmy_api_common::{
blocking,
check_community_ban,
check_downvotes_enabled,
- comment::*,
+ comment::{CommentResponse, CreateCommentLike},
get_local_user_view_from_jwt,
};
use lemmy_apub::{
};
use lemmy_db_schema::{
newtypes::LocalUserId,
- source::comment::*,
- traits::{Likeable, Saveable},
+ source::comment::{CommentLike, CommentLikeForm},
+ traits::Likeable,
};
use lemmy_db_views::{comment_view::CommentView, local_user_view::LocalUserView};
use lemmy_utils::{ConnectionId, LemmyError};
use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperation};
-
-use crate::Perform;
-
-#[async_trait::async_trait(?Send)]
-impl Perform for MarkCommentAsRead {
- type Response = CommentResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<CommentResponse, LemmyError> {
- let data: &MarkCommentAsRead = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- let comment_id = data.comment_id;
- let orig_comment = blocking(context.pool(), move |conn| {
- CommentView::read(conn, comment_id, None)
- })
- .await??;
-
- // Verify that only the recipient can mark as read
- if local_user_view.person.id != orig_comment.get_recipient_id() {
- return Err(LemmyError::from_message("no_comment_edit_allowed"));
- }
-
- // Do the mark as read
- let read = data.read;
- blocking(context.pool(), move |conn| {
- Comment::update_read(conn, comment_id, read)
- })
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?;
-
- // Refetch it
- let comment_id = data.comment_id;
- let person_id = local_user_view.person.id;
- let comment_view = blocking(context.pool(), move |conn| {
- CommentView::read(conn, comment_id, Some(person_id))
- })
- .await??;
-
- let res = CommentResponse {
- comment_view,
- recipient_ids: Vec::new(),
- form_id: None,
- };
-
- Ok(res)
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for SaveComment {
- type Response = CommentResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<CommentResponse, LemmyError> {
- let data: &SaveComment = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- let comment_saved_form = CommentSavedForm {
- comment_id: data.comment_id,
- person_id: local_user_view.person.id,
- };
-
- if data.save {
- let save_comment = move |conn: &'_ _| CommentSaved::save(conn, &comment_saved_form);
- blocking(context.pool(), save_comment)
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "couldnt_save_comment"))?;
- } else {
- let unsave_comment = move |conn: &'_ _| CommentSaved::unsave(conn, &comment_saved_form);
- blocking(context.pool(), unsave_comment)
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "couldnt_save_comment"))?;
- }
-
- let comment_id = data.comment_id;
- let person_id = local_user_view.person.id;
- let comment_view = blocking(context.pool(), move |conn| {
- CommentView::read(conn, comment_id, Some(person_id))
- })
- .await??;
-
- Ok(CommentResponse {
- comment_view,
- recipient_ids: Vec::new(),
- form_id: None,
- })
- }
-}
+use std::convert::TryInto;
#[async_trait::async_trait(?Send)]
impl Perform for CreateCommentLike {
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ comment::{CommentResponse, MarkCommentAsRead},
+ get_local_user_view_from_jwt,
+};
+use lemmy_db_schema::source::comment::Comment;
+use lemmy_db_views::comment_view::CommentView;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for MarkCommentAsRead {
+ type Response = CommentResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<CommentResponse, LemmyError> {
+ let data: &MarkCommentAsRead = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ let comment_id = data.comment_id;
+ let orig_comment = blocking(context.pool(), move |conn| {
+ CommentView::read(conn, comment_id, None)
+ })
+ .await??;
+
+ // Verify that only the recipient can mark as read
+ if local_user_view.person.id != orig_comment.get_recipient_id() {
+ return Err(LemmyError::from_message("no_comment_edit_allowed"));
+ }
+
+ // Do the mark as read
+ let read = data.read;
+ blocking(context.pool(), move |conn| {
+ Comment::update_read(conn, comment_id, read)
+ })
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?;
+
+ // Refetch it
+ let comment_id = data.comment_id;
+ let person_id = local_user_view.person.id;
+ let comment_view = blocking(context.pool(), move |conn| {
+ CommentView::read(conn, comment_id, Some(person_id))
+ })
+ .await??;
+
+ let res = CommentResponse {
+ comment_view,
+ recipient_ids: Vec::new(),
+ form_id: None,
+ };
+
+ Ok(res)
+ }
+}
--- /dev/null
+mod like;
+mod mark_as_read;
+mod save;
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ comment::{CommentResponse, SaveComment},
+ get_local_user_view_from_jwt,
+};
+use lemmy_db_schema::{
+ source::comment::{CommentSaved, CommentSavedForm},
+ traits::Saveable,
+};
+use lemmy_db_views::comment_view::CommentView;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for SaveComment {
+ type Response = CommentResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<CommentResponse, LemmyError> {
+ let data: &SaveComment = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ let comment_saved_form = CommentSavedForm {
+ comment_id: data.comment_id,
+ person_id: local_user_view.person.id,
+ };
+
+ if data.save {
+ let save_comment = move |conn: &'_ _| CommentSaved::save(conn, &comment_saved_form);
+ blocking(context.pool(), save_comment)
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_save_comment"))?;
+ } else {
+ let unsave_comment = move |conn: &'_ _| CommentSaved::unsave(conn, &comment_saved_form);
+ blocking(context.pool(), unsave_comment)
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_save_comment"))?;
+ }
+
+ let comment_id = data.comment_id;
+ let person_id = local_user_view.person.id;
+ let comment_view = blocking(context.pool(), move |conn| {
+ CommentView::read(conn, comment_id, Some(person_id))
+ })
+ .await??;
+
+ Ok(CommentResponse {
+ comment_view,
+ recipient_ids: Vec::new(),
+ form_id: None,
+ })
+ }
+}
+++ /dev/null
-use crate::Perform;
-use actix_web::web::Data;
-use lemmy_api_common::{
- blocking,
- check_community_ban,
- comment::*,
- get_local_user_view_from_jwt,
- is_mod_or_admin,
-};
-use lemmy_apub::protocol::activities::community::report::Report;
-use lemmy_apub_lib::object_id::ObjectId;
-use lemmy_db_schema::{source::comment_report::*, traits::Reportable};
-use lemmy_db_views::{
- comment_report_view::{CommentReportQueryBuilder, CommentReportView},
- comment_view::CommentView,
-};
-use lemmy_utils::{ConnectionId, LemmyError};
-use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
-
-/// Creates a comment report and notifies the moderators of the community
-#[async_trait::async_trait(?Send)]
-impl Perform for CreateCommentReport {
- type Response = CommentReportResponse;
-
- #[tracing::instrument(skip(context, websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- websocket_id: Option<ConnectionId>,
- ) -> Result<CommentReportResponse, LemmyError> {
- let data: &CreateCommentReport = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- // check size of report and check for whitespace
- let reason = data.reason.trim();
- if reason.is_empty() {
- return Err(LemmyError::from_message("report_reason_required"));
- }
- if reason.chars().count() > 1000 {
- return Err(LemmyError::from_message("report_too_long"));
- }
-
- let person_id = local_user_view.person.id;
- let comment_id = data.comment_id;
- let comment_view = blocking(context.pool(), move |conn| {
- CommentView::read(conn, comment_id, None)
- })
- .await??;
-
- check_community_ban(person_id, comment_view.community.id, context.pool()).await?;
-
- let report_form = CommentReportForm {
- creator_id: person_id,
- comment_id,
- original_comment_text: comment_view.comment.content,
- reason: data.reason.to_owned(),
- };
-
- let report = blocking(context.pool(), move |conn| {
- CommentReport::report(conn, &report_form)
- })
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "couldnt_create_report"))?;
-
- let comment_report_view = blocking(context.pool(), move |conn| {
- CommentReportView::read(conn, report.id, person_id)
- })
- .await??;
-
- let res = CommentReportResponse {
- comment_report_view,
- };
-
- context.chat_server().do_send(SendModRoomMessage {
- op: UserOperation::CreateCommentReport,
- response: res.clone(),
- community_id: comment_view.community.id,
- websocket_id,
- });
-
- Report::send(
- ObjectId::new(comment_view.comment.ap_id),
- &local_user_view.person.into(),
- ObjectId::new(comment_view.community.actor_id),
- reason.to_string(),
- context,
- )
- .await?;
-
- Ok(res)
- }
-}
-
-/// Resolves or unresolves a comment report and notifies the moderators of the community
-#[async_trait::async_trait(?Send)]
-impl Perform for ResolveCommentReport {
- type Response = CommentReportResponse;
-
- #[tracing::instrument(skip(context, websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- websocket_id: Option<ConnectionId>,
- ) -> Result<CommentReportResponse, LemmyError> {
- let data: &ResolveCommentReport = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- let report_id = data.report_id;
- let person_id = local_user_view.person.id;
- let report = blocking(context.pool(), move |conn| {
- CommentReportView::read(conn, report_id, person_id)
- })
- .await??;
-
- let person_id = local_user_view.person.id;
- is_mod_or_admin(context.pool(), person_id, report.community.id).await?;
-
- let resolved = data.resolved;
- let resolve_fun = move |conn: &'_ _| {
- if resolved {
- CommentReport::resolve(conn, report_id, person_id)
- } else {
- CommentReport::unresolve(conn, report_id, person_id)
- }
- };
-
- blocking(context.pool(), resolve_fun)
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "couldnt_resolve_report"))?;
-
- let report_id = data.report_id;
- let comment_report_view = blocking(context.pool(), move |conn| {
- CommentReportView::read(conn, report_id, person_id)
- })
- .await??;
-
- let res = CommentReportResponse {
- comment_report_view,
- };
-
- context.chat_server().do_send(SendModRoomMessage {
- op: UserOperation::ResolveCommentReport,
- response: res.clone(),
- community_id: report.community.id,
- websocket_id,
- });
-
- Ok(res)
- }
-}
-
-/// Lists comment reports for a community if an id is supplied
-/// or returns all comment reports for communities a user moderates
-#[async_trait::async_trait(?Send)]
-impl Perform for ListCommentReports {
- type Response = ListCommentReportsResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<ListCommentReportsResponse, LemmyError> {
- let data: &ListCommentReports = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- let person_id = local_user_view.person.id;
- let admin = local_user_view.person.admin;
- let community_id = data.community_id;
- let unresolved_only = data.unresolved_only;
-
- let page = data.page;
- let limit = data.limit;
- let comment_reports = blocking(context.pool(), move |conn| {
- CommentReportQueryBuilder::create(conn, person_id, admin)
- .community_id(community_id)
- .unresolved_only(unresolved_only)
- .page(page)
- .limit(limit)
- .list()
- })
- .await??;
-
- let res = ListCommentReportsResponse { comment_reports };
-
- Ok(res)
- }
-}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ check_community_ban,
+ comment::{CommentReportResponse, CreateCommentReport},
+ get_local_user_view_from_jwt,
+};
+use lemmy_apub::protocol::activities::community::report::Report;
+use lemmy_apub_lib::object_id::ObjectId;
+use lemmy_db_schema::{
+ source::comment_report::{CommentReport, CommentReportForm},
+ traits::Reportable,
+};
+use lemmy_db_views::{comment_report_view::CommentReportView, comment_view::CommentView};
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
+
+/// Creates a comment report and notifies the moderators of the community
+#[async_trait::async_trait(?Send)]
+impl Perform for CreateCommentReport {
+ type Response = CommentReportResponse;
+
+ #[tracing::instrument(skip(context, websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ websocket_id: Option<ConnectionId>,
+ ) -> Result<CommentReportResponse, LemmyError> {
+ let data: &CreateCommentReport = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ // check size of report and check for whitespace
+ let reason = data.reason.trim();
+ if reason.is_empty() {
+ return Err(LemmyError::from_message("report_reason_required"));
+ }
+ if reason.chars().count() > 1000 {
+ return Err(LemmyError::from_message("report_too_long"));
+ }
+
+ let person_id = local_user_view.person.id;
+ let comment_id = data.comment_id;
+ let comment_view = blocking(context.pool(), move |conn| {
+ CommentView::read(conn, comment_id, None)
+ })
+ .await??;
+
+ check_community_ban(person_id, comment_view.community.id, context.pool()).await?;
+
+ let report_form = CommentReportForm {
+ creator_id: person_id,
+ comment_id,
+ original_comment_text: comment_view.comment.content,
+ reason: data.reason.to_owned(),
+ };
+
+ let report = blocking(context.pool(), move |conn| {
+ CommentReport::report(conn, &report_form)
+ })
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_create_report"))?;
+
+ let comment_report_view = blocking(context.pool(), move |conn| {
+ CommentReportView::read(conn, report.id, person_id)
+ })
+ .await??;
+
+ let res = CommentReportResponse {
+ comment_report_view,
+ };
+
+ context.chat_server().do_send(SendModRoomMessage {
+ op: UserOperation::CreateCommentReport,
+ response: res.clone(),
+ community_id: comment_view.community.id,
+ websocket_id,
+ });
+
+ Report::send(
+ ObjectId::new(comment_view.comment.ap_id),
+ &local_user_view.person.into(),
+ ObjectId::new(comment_view.community.actor_id),
+ reason.to_string(),
+ context,
+ )
+ .await?;
+
+ Ok(res)
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ comment::{ListCommentReports, ListCommentReportsResponse},
+ get_local_user_view_from_jwt,
+};
+use lemmy_db_views::comment_report_view::CommentReportQueryBuilder;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+/// Lists comment reports for a community if an id is supplied
+/// or returns all comment reports for communities a user moderates
+#[async_trait::async_trait(?Send)]
+impl Perform for ListCommentReports {
+ type Response = ListCommentReportsResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<ListCommentReportsResponse, LemmyError> {
+ let data: &ListCommentReports = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ let person_id = local_user_view.person.id;
+ let admin = local_user_view.person.admin;
+ let community_id = data.community_id;
+ let unresolved_only = data.unresolved_only;
+
+ let page = data.page;
+ let limit = data.limit;
+ let comment_reports = blocking(context.pool(), move |conn| {
+ CommentReportQueryBuilder::create(conn, person_id, admin)
+ .community_id(community_id)
+ .unresolved_only(unresolved_only)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
+
+ let res = ListCommentReportsResponse { comment_reports };
+
+ Ok(res)
+ }
+}
--- /dev/null
+mod create;
+mod list;
+mod resolve;
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ comment::{CommentReportResponse, ResolveCommentReport},
+ get_local_user_view_from_jwt,
+ is_mod_or_admin,
+};
+use lemmy_db_schema::{source::comment_report::CommentReport, traits::Reportable};
+use lemmy_db_views::comment_report_view::CommentReportView;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
+
+/// Resolves or unresolves a comment report and notifies the moderators of the community
+#[async_trait::async_trait(?Send)]
+impl Perform for ResolveCommentReport {
+ type Response = CommentReportResponse;
+
+ #[tracing::instrument(skip(context, websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ websocket_id: Option<ConnectionId>,
+ ) -> Result<CommentReportResponse, LemmyError> {
+ let data: &ResolveCommentReport = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ let report_id = data.report_id;
+ let person_id = local_user_view.person.id;
+ let report = blocking(context.pool(), move |conn| {
+ CommentReportView::read(conn, report_id, person_id)
+ })
+ .await??;
+
+ let person_id = local_user_view.person.id;
+ is_mod_or_admin(context.pool(), person_id, report.community.id).await?;
+
+ let resolved = data.resolved;
+ let resolve_fun = move |conn: &'_ _| {
+ if resolved {
+ CommentReport::resolve(conn, report_id, person_id)
+ } else {
+ CommentReport::unresolve(conn, report_id, person_id)
+ }
+ };
+
+ blocking(context.pool(), resolve_fun)
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_resolve_report"))?;
+
+ let report_id = data.report_id;
+ let comment_report_view = blocking(context.pool(), move |conn| {
+ CommentReportView::read(conn, report_id, person_id)
+ })
+ .await??;
+
+ let res = CommentReportResponse {
+ comment_report_view,
+ };
+
+ context.chat_server().do_send(SendModRoomMessage {
+ op: UserOperation::ResolveCommentReport,
+ response: res.clone(),
+ community_id: report.community.id,
+ websocket_id,
+ });
+
+ Ok(res)
+ }
+}
+++ /dev/null
-use crate::Perform;
-use actix_web::web::Data;
-use anyhow::Context;
-use lemmy_api_common::{
- blocking,
- check_community_ban,
- check_community_deleted_or_removed,
- community::*,
- get_local_user_view_from_jwt,
- is_mod_or_admin,
- remove_user_data_in_community,
-};
-use lemmy_apub::{
- activities::block::SiteOrCommunity,
- objects::{community::ApubCommunity, person::ApubPerson},
- protocol::activities::{
- block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
- community::{add_mod::AddMod, remove_mod::RemoveMod},
- following::{follow::FollowCommunity as FollowCommunityApub, undo_follow::UndoFollowCommunity},
- },
-};
-use lemmy_db_schema::{
- source::{
- community::{
- Community,
- CommunityFollower,
- CommunityFollowerForm,
- CommunityModerator,
- CommunityModeratorForm,
- CommunityPersonBan,
- CommunityPersonBanForm,
- },
- community_block::{CommunityBlock, CommunityBlockForm},
- moderator::{
- ModAddCommunity,
- ModAddCommunityForm,
- ModBanFromCommunity,
- ModBanFromCommunityForm,
- ModTransferCommunity,
- ModTransferCommunityForm,
- },
- person::Person,
- },
- traits::{Bannable, Blockable, Crud, Followable, Joinable},
-};
-use lemmy_db_views_actor::{
- community_moderator_view::CommunityModeratorView,
- community_view::CommunityView,
- person_view::PersonViewSafe,
-};
-use lemmy_utils::{location_info, utils::naive_from_unix, ConnectionId, LemmyError};
-use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext, UserOperation};
-
-#[async_trait::async_trait(?Send)]
-impl Perform for FollowCommunity {
- type Response = CommunityResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<CommunityResponse, LemmyError> {
- let data: &FollowCommunity = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- let community_id = data.community_id;
- let community: ApubCommunity = blocking(context.pool(), move |conn| {
- Community::read(conn, community_id)
- })
- .await??
- .into();
- let community_follower_form = CommunityFollowerForm {
- community_id: data.community_id,
- person_id: local_user_view.person.id,
- pending: false,
- };
-
- if community.local {
- if data.follow {
- check_community_ban(local_user_view.person.id, community_id, context.pool()).await?;
- check_community_deleted_or_removed(community_id, context.pool()).await?;
-
- let follow = move |conn: &'_ _| CommunityFollower::follow(conn, &community_follower_form);
- blocking(context.pool(), follow)
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "community_follower_already_exists"))?;
- } else {
- let unfollow =
- move |conn: &'_ _| CommunityFollower::unfollow(conn, &community_follower_form);
- blocking(context.pool(), unfollow)
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "community_follower_already_exists"))?;
- }
- } else if data.follow {
- // Dont actually add to the community followers here, because you need
- // to wait for the accept
- FollowCommunityApub::send(&local_user_view.person.clone().into(), &community, context)
- .await?;
- } else {
- UndoFollowCommunity::send(&local_user_view.person.clone().into(), &community, context)
- .await?;
- let unfollow = move |conn: &'_ _| CommunityFollower::unfollow(conn, &community_follower_form);
- blocking(context.pool(), unfollow)
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "community_follower_already_exists"))?;
- }
-
- let community_id = data.community_id;
- let person_id = local_user_view.person.id;
- let mut community_view = blocking(context.pool(), move |conn| {
- CommunityView::read(conn, community_id, Some(person_id))
- })
- .await??;
-
- // TODO: this needs to return a "pending" state, until Accept is received from the remote server
- // For now, just assume that remote follows are accepted.
- // Otherwise, the subscribed will be null
- if !community.local {
- community_view.subscribed = data.follow;
- }
-
- Ok(CommunityResponse { community_view })
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for BlockCommunity {
- type Response = BlockCommunityResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<BlockCommunityResponse, LemmyError> {
- let data: &BlockCommunity = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- let community_id = data.community_id;
- let person_id = local_user_view.person.id;
- let community_block_form = CommunityBlockForm {
- person_id,
- community_id,
- };
-
- if data.block {
- let block = move |conn: &'_ _| CommunityBlock::block(conn, &community_block_form);
- blocking(context.pool(), block)
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "community_block_already_exists"))?;
-
- // Also, unfollow the community, and send a federated unfollow
- let community_follower_form = CommunityFollowerForm {
- community_id: data.community_id,
- person_id,
- pending: false,
- };
- blocking(context.pool(), move |conn: &'_ _| {
- CommunityFollower::unfollow(conn, &community_follower_form)
- })
- .await?
- .ok();
- let community = blocking(context.pool(), move |conn| {
- Community::read(conn, community_id)
- })
- .await??;
- UndoFollowCommunity::send(&local_user_view.person.into(), &community.into(), context).await?;
- } else {
- let unblock = move |conn: &'_ _| CommunityBlock::unblock(conn, &community_block_form);
- blocking(context.pool(), unblock)
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "community_block_already_exists"))?;
- }
-
- let community_view = blocking(context.pool(), move |conn| {
- CommunityView::read(conn, community_id, Some(person_id))
- })
- .await??;
-
- Ok(BlockCommunityResponse {
- blocked: data.block,
- community_view,
- })
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for BanFromCommunity {
- type Response = BanFromCommunityResponse;
-
- #[tracing::instrument(skip(context, websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- websocket_id: Option<ConnectionId>,
- ) -> Result<BanFromCommunityResponse, LemmyError> {
- let data: &BanFromCommunity = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- let community_id = data.community_id;
- let banned_person_id = data.person_id;
- let remove_data = data.remove_data.unwrap_or(false);
- let expires = data.expires.map(naive_from_unix);
-
- // Verify that only mods or admins can ban
- is_mod_or_admin(context.pool(), local_user_view.person.id, community_id).await?;
-
- let community_user_ban_form = CommunityPersonBanForm {
- community_id: data.community_id,
- person_id: data.person_id,
- expires: Some(expires),
- };
-
- let community: ApubCommunity = blocking(context.pool(), move |conn: &'_ _| {
- Community::read(conn, community_id)
- })
- .await??
- .into();
- let banned_person: ApubPerson = blocking(context.pool(), move |conn: &'_ _| {
- Person::read(conn, banned_person_id)
- })
- .await??
- .into();
-
- if data.ban {
- let ban = move |conn: &'_ _| CommunityPersonBan::ban(conn, &community_user_ban_form);
- blocking(context.pool(), ban)
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "community_user_already_banned"))?;
-
- // Also unsubscribe them from the community, if they are subscribed
- let community_follower_form = CommunityFollowerForm {
- community_id: data.community_id,
- person_id: banned_person_id,
- pending: false,
- };
- blocking(context.pool(), move |conn: &'_ _| {
- CommunityFollower::unfollow(conn, &community_follower_form)
- })
- .await?
- .ok();
-
- BlockUser::send(
- &SiteOrCommunity::Community(community),
- &banned_person,
- &local_user_view.person.clone().into(),
- remove_data,
- data.reason.clone(),
- expires,
- context,
- )
- .await?;
- } else {
- let unban = move |conn: &'_ _| CommunityPersonBan::unban(conn, &community_user_ban_form);
- blocking(context.pool(), unban)
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "community_user_already_banned"))?;
- UndoBlockUser::send(
- &SiteOrCommunity::Community(community),
- &banned_person,
- &local_user_view.person.clone().into(),
- data.reason.clone(),
- context,
- )
- .await?;
- }
-
- // Remove/Restore their data if that's desired
- if remove_data {
- remove_user_data_in_community(community_id, banned_person_id, context.pool()).await?;
- }
-
- // Mod tables
- let form = ModBanFromCommunityForm {
- mod_person_id: local_user_view.person.id,
- other_person_id: data.person_id,
- community_id: data.community_id,
- reason: data.reason.to_owned(),
- banned: Some(data.ban),
- expires,
- };
- blocking(context.pool(), move |conn| {
- ModBanFromCommunity::create(conn, &form)
- })
- .await??;
-
- let person_id = data.person_id;
- let person_view = blocking(context.pool(), move |conn| {
- PersonViewSafe::read(conn, person_id)
- })
- .await??;
-
- let res = BanFromCommunityResponse {
- person_view,
- banned: data.ban,
- };
-
- context.chat_server().do_send(SendCommunityRoomMessage {
- op: UserOperation::BanFromCommunity,
- response: res.clone(),
- community_id,
- websocket_id,
- });
-
- Ok(res)
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for AddModToCommunity {
- type Response = AddModToCommunityResponse;
-
- #[tracing::instrument(skip(context, websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- websocket_id: Option<ConnectionId>,
- ) -> Result<AddModToCommunityResponse, LemmyError> {
- let data: &AddModToCommunity = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- let community_id = data.community_id;
-
- // Verify that only mods or admins can add mod
- is_mod_or_admin(context.pool(), local_user_view.person.id, community_id).await?;
- let community = blocking(context.pool(), move |conn| {
- Community::read(conn, community_id)
- })
- .await??;
- if local_user_view.person.admin && !community.local {
- return Err(LemmyError::from_message("not_a_moderator"));
- }
-
- // Update in local database
- let community_moderator_form = CommunityModeratorForm {
- community_id: data.community_id,
- person_id: data.person_id,
- };
- if data.added {
- let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
- blocking(context.pool(), join)
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "community_moderator_already_exists"))?;
- } else {
- let leave = move |conn: &'_ _| CommunityModerator::leave(conn, &community_moderator_form);
- blocking(context.pool(), leave)
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "community_moderator_already_exists"))?;
- }
-
- // Mod tables
- let form = ModAddCommunityForm {
- mod_person_id: local_user_view.person.id,
- other_person_id: data.person_id,
- community_id: data.community_id,
- removed: Some(!data.added),
- };
- blocking(context.pool(), move |conn| {
- ModAddCommunity::create(conn, &form)
- })
- .await??;
-
- // Send to federated instances
- let updated_mod_id = data.person_id;
- let updated_mod: ApubPerson = blocking(context.pool(), move |conn| {
- Person::read(conn, updated_mod_id)
- })
- .await??
- .into();
- let community: ApubCommunity = community.into();
- if data.added {
- AddMod::send(
- &community,
- &updated_mod,
- &local_user_view.person.into(),
- context,
- )
- .await?;
- } else {
- RemoveMod::send(
- &community,
- &updated_mod,
- &local_user_view.person.into(),
- context,
- )
- .await?;
- }
-
- // Note: in case a remote mod is added, this returns the old moderators list, it will only get
- // updated once we receive an activity from the community (like `Announce/Add/Moderator`)
- let community_id = data.community_id;
- let moderators = blocking(context.pool(), move |conn| {
- CommunityModeratorView::for_community(conn, community_id)
- })
- .await??;
-
- let res = AddModToCommunityResponse { moderators };
- context.chat_server().do_send(SendCommunityRoomMessage {
- op: UserOperation::AddModToCommunity,
- response: res.clone(),
- community_id,
- websocket_id,
- });
- Ok(res)
- }
-}
-
-// TODO: we dont do anything for federation here, it should be updated the next time the community
-// gets fetched. i hope we can get rid of the community creator role soon.
-#[async_trait::async_trait(?Send)]
-impl Perform for TransferCommunity {
- type Response = GetCommunityResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<GetCommunityResponse, LemmyError> {
- let data: &TransferCommunity = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- let admins = blocking(context.pool(), PersonViewSafe::admins).await??;
-
- // Fetch the community mods
- let community_id = data.community_id;
- let mut community_mods = blocking(context.pool(), move |conn| {
- CommunityModeratorView::for_community(conn, community_id)
- })
- .await??;
-
- // Make sure transferrer is either the top community mod, or an admin
- if local_user_view.person.id != community_mods[0].moderator.id
- && !admins
- .iter()
- .map(|a| a.person.id)
- .any(|x| x == local_user_view.person.id)
- {
- return Err(LemmyError::from_message("not_an_admin"));
- }
-
- // You have to re-do the community_moderator table, reordering it.
- // Add the transferee to the top
- let creator_index = community_mods
- .iter()
- .position(|r| r.moderator.id == data.person_id)
- .context(location_info!())?;
- let creator_person = community_mods.remove(creator_index);
- community_mods.insert(0, creator_person);
-
- // Delete all the mods
- let community_id = data.community_id;
- blocking(context.pool(), move |conn| {
- CommunityModerator::delete_for_community(conn, community_id)
- })
- .await??;
-
- // TODO: this should probably be a bulk operation
- // Re-add the mods, in the new order
- for cmod in &community_mods {
- let community_moderator_form = CommunityModeratorForm {
- community_id: cmod.community.id,
- person_id: cmod.moderator.id,
- };
-
- let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
- blocking(context.pool(), join)
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "community_moderator_already_exists"))?;
- }
-
- // Mod tables
- let form = ModTransferCommunityForm {
- mod_person_id: local_user_view.person.id,
- other_person_id: data.person_id,
- community_id: data.community_id,
- removed: Some(false),
- };
- blocking(context.pool(), move |conn| {
- ModTransferCommunity::create(conn, &form)
- })
- .await??;
-
- let community_id = data.community_id;
- let person_id = local_user_view.person.id;
- let community_view = blocking(context.pool(), move |conn| {
- CommunityView::read(conn, community_id, Some(person_id))
- })
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?;
-
- let community_id = data.community_id;
- let moderators = blocking(context.pool(), move |conn| {
- CommunityModeratorView::for_community(conn, community_id)
- })
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?;
-
- // Return the jwt
- Ok(GetCommunityResponse {
- community_view,
- site: None,
- moderators,
- online: 0,
- })
- }
-}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ community::{AddModToCommunity, AddModToCommunityResponse},
+ get_local_user_view_from_jwt,
+ is_mod_or_admin,
+};
+use lemmy_apub::{
+ objects::{community::ApubCommunity, person::ApubPerson},
+ protocol::activities::community::{add_mod::AddMod, remove_mod::RemoveMod},
+};
+use lemmy_db_schema::{
+ source::{
+ community::{Community, CommunityModerator, CommunityModeratorForm},
+ moderator::{ModAddCommunity, ModAddCommunityForm},
+ person::Person,
+ },
+ traits::{Crud, Joinable},
+};
+use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext, UserOperation};
+
+#[async_trait::async_trait(?Send)]
+impl Perform for AddModToCommunity {
+ type Response = AddModToCommunityResponse;
+
+ #[tracing::instrument(skip(context, websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ websocket_id: Option<ConnectionId>,
+ ) -> Result<AddModToCommunityResponse, LemmyError> {
+ let data: &AddModToCommunity = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ let community_id = data.community_id;
+
+ // Verify that only mods or admins can add mod
+ is_mod_or_admin(context.pool(), local_user_view.person.id, community_id).await?;
+ let community = blocking(context.pool(), move |conn| {
+ Community::read(conn, community_id)
+ })
+ .await??;
+ if local_user_view.person.admin && !community.local {
+ return Err(LemmyError::from_message("not_a_moderator"));
+ }
+
+ // Update in local database
+ let community_moderator_form = CommunityModeratorForm {
+ community_id: data.community_id,
+ person_id: data.person_id,
+ };
+ if data.added {
+ let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
+ blocking(context.pool(), join)
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "community_moderator_already_exists"))?;
+ } else {
+ let leave = move |conn: &'_ _| CommunityModerator::leave(conn, &community_moderator_form);
+ blocking(context.pool(), leave)
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "community_moderator_already_exists"))?;
+ }
+
+ // Mod tables
+ let form = ModAddCommunityForm {
+ mod_person_id: local_user_view.person.id,
+ other_person_id: data.person_id,
+ community_id: data.community_id,
+ removed: Some(!data.added),
+ };
+ blocking(context.pool(), move |conn| {
+ ModAddCommunity::create(conn, &form)
+ })
+ .await??;
+
+ // Send to federated instances
+ let updated_mod_id = data.person_id;
+ let updated_mod: ApubPerson = blocking(context.pool(), move |conn| {
+ Person::read(conn, updated_mod_id)
+ })
+ .await??
+ .into();
+ let community: ApubCommunity = community.into();
+ if data.added {
+ AddMod::send(
+ &community,
+ &updated_mod,
+ &local_user_view.person.into(),
+ context,
+ )
+ .await?;
+ } else {
+ RemoveMod::send(
+ &community,
+ &updated_mod,
+ &local_user_view.person.into(),
+ context,
+ )
+ .await?;
+ }
+
+ // Note: in case a remote mod is added, this returns the old moderators list, it will only get
+ // updated once we receive an activity from the community (like `Announce/Add/Moderator`)
+ let community_id = data.community_id;
+ let moderators = blocking(context.pool(), move |conn| {
+ CommunityModeratorView::for_community(conn, community_id)
+ })
+ .await??;
+
+ let res = AddModToCommunityResponse { moderators };
+ context.chat_server().do_send(SendCommunityRoomMessage {
+ op: UserOperation::AddModToCommunity,
+ response: res.clone(),
+ community_id,
+ websocket_id,
+ });
+ Ok(res)
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ community::{BanFromCommunity, BanFromCommunityResponse},
+ get_local_user_view_from_jwt,
+ is_mod_or_admin,
+ remove_user_data_in_community,
+};
+use lemmy_apub::{
+ activities::block::SiteOrCommunity,
+ objects::{community::ApubCommunity, person::ApubPerson},
+ protocol::activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
+};
+use lemmy_db_schema::{
+ source::{
+ community::{
+ Community,
+ CommunityFollower,
+ CommunityFollowerForm,
+ CommunityPersonBan,
+ CommunityPersonBanForm,
+ },
+ moderator::{ModBanFromCommunity, ModBanFromCommunityForm},
+ person::Person,
+ },
+ traits::{Bannable, Crud, Followable},
+};
+use lemmy_db_views_actor::person_view::PersonViewSafe;
+use lemmy_utils::{utils::naive_from_unix, ConnectionId, LemmyError};
+use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext, UserOperation};
+
+#[async_trait::async_trait(?Send)]
+impl Perform for BanFromCommunity {
+ type Response = BanFromCommunityResponse;
+
+ #[tracing::instrument(skip(context, websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ websocket_id: Option<ConnectionId>,
+ ) -> Result<BanFromCommunityResponse, LemmyError> {
+ let data: &BanFromCommunity = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ let community_id = data.community_id;
+ let banned_person_id = data.person_id;
+ let remove_data = data.remove_data.unwrap_or(false);
+ let expires = data.expires.map(naive_from_unix);
+
+ // Verify that only mods or admins can ban
+ is_mod_or_admin(context.pool(), local_user_view.person.id, community_id).await?;
+
+ let community_user_ban_form = CommunityPersonBanForm {
+ community_id: data.community_id,
+ person_id: data.person_id,
+ expires: Some(expires),
+ };
+
+ let community: ApubCommunity = blocking(context.pool(), move |conn: &'_ _| {
+ Community::read(conn, community_id)
+ })
+ .await??
+ .into();
+ let banned_person: ApubPerson = blocking(context.pool(), move |conn: &'_ _| {
+ Person::read(conn, banned_person_id)
+ })
+ .await??
+ .into();
+
+ if data.ban {
+ let ban = move |conn: &'_ _| CommunityPersonBan::ban(conn, &community_user_ban_form);
+ blocking(context.pool(), ban)
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "community_user_already_banned"))?;
+
+ // Also unsubscribe them from the community, if they are subscribed
+ let community_follower_form = CommunityFollowerForm {
+ community_id: data.community_id,
+ person_id: banned_person_id,
+ pending: false,
+ };
+ blocking(context.pool(), move |conn: &'_ _| {
+ CommunityFollower::unfollow(conn, &community_follower_form)
+ })
+ .await?
+ .ok();
+
+ BlockUser::send(
+ &SiteOrCommunity::Community(community),
+ &banned_person,
+ &local_user_view.person.clone().into(),
+ remove_data,
+ data.reason.clone(),
+ expires,
+ context,
+ )
+ .await?;
+ } else {
+ let unban = move |conn: &'_ _| CommunityPersonBan::unban(conn, &community_user_ban_form);
+ blocking(context.pool(), unban)
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "community_user_already_banned"))?;
+ UndoBlockUser::send(
+ &SiteOrCommunity::Community(community),
+ &banned_person,
+ &local_user_view.person.clone().into(),
+ data.reason.clone(),
+ context,
+ )
+ .await?;
+ }
+
+ // Remove/Restore their data if that's desired
+ if remove_data {
+ remove_user_data_in_community(community_id, banned_person_id, context.pool()).await?;
+ }
+
+ // Mod tables
+ let form = ModBanFromCommunityForm {
+ mod_person_id: local_user_view.person.id,
+ other_person_id: data.person_id,
+ community_id: data.community_id,
+ reason: data.reason.to_owned(),
+ banned: Some(data.ban),
+ expires,
+ };
+ blocking(context.pool(), move |conn| {
+ ModBanFromCommunity::create(conn, &form)
+ })
+ .await??;
+
+ let person_id = data.person_id;
+ let person_view = blocking(context.pool(), move |conn| {
+ PersonViewSafe::read(conn, person_id)
+ })
+ .await??;
+
+ let res = BanFromCommunityResponse {
+ person_view,
+ banned: data.ban,
+ };
+
+ context.chat_server().do_send(SendCommunityRoomMessage {
+ op: UserOperation::BanFromCommunity,
+ response: res.clone(),
+ community_id,
+ websocket_id,
+ });
+
+ Ok(res)
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ community::{BlockCommunity, BlockCommunityResponse},
+ get_local_user_view_from_jwt,
+};
+use lemmy_apub::protocol::activities::following::undo_follow::UndoFollowCommunity;
+use lemmy_db_schema::{
+ source::{
+ community::{Community, CommunityFollower, CommunityFollowerForm},
+ community_block::{CommunityBlock, CommunityBlockForm},
+ },
+ traits::{Blockable, Crud, Followable},
+};
+use lemmy_db_views_actor::community_view::CommunityView;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for BlockCommunity {
+ type Response = BlockCommunityResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<BlockCommunityResponse, LemmyError> {
+ let data: &BlockCommunity = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ let community_id = data.community_id;
+ let person_id = local_user_view.person.id;
+ let community_block_form = CommunityBlockForm {
+ person_id,
+ community_id,
+ };
+
+ if data.block {
+ let block = move |conn: &'_ _| CommunityBlock::block(conn, &community_block_form);
+ blocking(context.pool(), block)
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "community_block_already_exists"))?;
+
+ // Also, unfollow the community, and send a federated unfollow
+ let community_follower_form = CommunityFollowerForm {
+ community_id: data.community_id,
+ person_id,
+ pending: false,
+ };
+ blocking(context.pool(), move |conn: &'_ _| {
+ CommunityFollower::unfollow(conn, &community_follower_form)
+ })
+ .await?
+ .ok();
+ let community = blocking(context.pool(), move |conn| {
+ Community::read(conn, community_id)
+ })
+ .await??;
+ UndoFollowCommunity::send(&local_user_view.person.into(), &community.into(), context).await?;
+ } else {
+ let unblock = move |conn: &'_ _| CommunityBlock::unblock(conn, &community_block_form);
+ blocking(context.pool(), unblock)
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "community_block_already_exists"))?;
+ }
+
+ let community_view = blocking(context.pool(), move |conn| {
+ CommunityView::read(conn, community_id, Some(person_id))
+ })
+ .await??;
+
+ Ok(BlockCommunityResponse {
+ blocked: data.block,
+ community_view,
+ })
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ check_community_ban,
+ check_community_deleted_or_removed,
+ community::{CommunityResponse, FollowCommunity},
+ get_local_user_view_from_jwt,
+};
+use lemmy_apub::{
+ objects::community::ApubCommunity,
+ protocol::activities::following::{
+ follow::FollowCommunity as FollowCommunityApub,
+ undo_follow::UndoFollowCommunity,
+ },
+};
+use lemmy_db_schema::{
+ source::community::{Community, CommunityFollower, CommunityFollowerForm},
+ traits::{Crud, Followable},
+};
+use lemmy_db_views_actor::community_view::CommunityView;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for FollowCommunity {
+ type Response = CommunityResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<CommunityResponse, LemmyError> {
+ let data: &FollowCommunity = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ let community_id = data.community_id;
+ let community: ApubCommunity = blocking(context.pool(), move |conn| {
+ Community::read(conn, community_id)
+ })
+ .await??
+ .into();
+ let community_follower_form = CommunityFollowerForm {
+ community_id: data.community_id,
+ person_id: local_user_view.person.id,
+ pending: false,
+ };
+
+ if community.local {
+ if data.follow {
+ check_community_ban(local_user_view.person.id, community_id, context.pool()).await?;
+ check_community_deleted_or_removed(community_id, context.pool()).await?;
+
+ let follow = move |conn: &'_ _| CommunityFollower::follow(conn, &community_follower_form);
+ blocking(context.pool(), follow)
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "community_follower_already_exists"))?;
+ } else {
+ let unfollow =
+ move |conn: &'_ _| CommunityFollower::unfollow(conn, &community_follower_form);
+ blocking(context.pool(), unfollow)
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "community_follower_already_exists"))?;
+ }
+ } else if data.follow {
+ // Dont actually add to the community followers here, because you need
+ // to wait for the accept
+ FollowCommunityApub::send(&local_user_view.person.clone().into(), &community, context)
+ .await?;
+ } else {
+ UndoFollowCommunity::send(&local_user_view.person.clone().into(), &community, context)
+ .await?;
+ let unfollow = move |conn: &'_ _| CommunityFollower::unfollow(conn, &community_follower_form);
+ blocking(context.pool(), unfollow)
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "community_follower_already_exists"))?;
+ }
+
+ let community_id = data.community_id;
+ let person_id = local_user_view.person.id;
+ let mut community_view = blocking(context.pool(), move |conn| {
+ CommunityView::read(conn, community_id, Some(person_id))
+ })
+ .await??;
+
+ // TODO: this needs to return a "pending" state, until Accept is received from the remote server
+ // For now, just assume that remote follows are accepted.
+ // Otherwise, the subscribed will be null
+ if !community.local {
+ community_view.subscribed = data.follow;
+ }
+
+ Ok(CommunityResponse { community_view })
+ }
+}
--- /dev/null
+mod add_mod;
+mod ban;
+mod block;
+mod follow;
+mod transfer;
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use anyhow::Context;
+use lemmy_api_common::{
+ blocking,
+ community::{GetCommunityResponse, TransferCommunity},
+ get_local_user_view_from_jwt,
+};
+use lemmy_db_schema::{
+ source::{
+ community::{CommunityModerator, CommunityModeratorForm},
+ moderator::{ModTransferCommunity, ModTransferCommunityForm},
+ },
+ traits::{Crud, Joinable},
+};
+use lemmy_db_views_actor::{
+ community_moderator_view::CommunityModeratorView,
+ community_view::CommunityView,
+ person_view::PersonViewSafe,
+};
+use lemmy_utils::{location_info, ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+// TODO: we dont do anything for federation here, it should be updated the next time the community
+// gets fetched. i hope we can get rid of the community creator role soon.
+#[async_trait::async_trait(?Send)]
+impl Perform for TransferCommunity {
+ type Response = GetCommunityResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<GetCommunityResponse, LemmyError> {
+ let data: &TransferCommunity = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ let admins = blocking(context.pool(), PersonViewSafe::admins).await??;
+
+ // Fetch the community mods
+ let community_id = data.community_id;
+ let mut community_mods = blocking(context.pool(), move |conn| {
+ CommunityModeratorView::for_community(conn, community_id)
+ })
+ .await??;
+
+ // Make sure transferrer is either the top community mod, or an admin
+ if local_user_view.person.id != community_mods[0].moderator.id
+ && !admins
+ .iter()
+ .map(|a| a.person.id)
+ .any(|x| x == local_user_view.person.id)
+ {
+ return Err(LemmyError::from_message("not_an_admin"));
+ }
+
+ // You have to re-do the community_moderator table, reordering it.
+ // Add the transferee to the top
+ let creator_index = community_mods
+ .iter()
+ .position(|r| r.moderator.id == data.person_id)
+ .context(location_info!())?;
+ let creator_person = community_mods.remove(creator_index);
+ community_mods.insert(0, creator_person);
+
+ // Delete all the mods
+ let community_id = data.community_id;
+ blocking(context.pool(), move |conn| {
+ CommunityModerator::delete_for_community(conn, community_id)
+ })
+ .await??;
+
+ // TODO: this should probably be a bulk operation
+ // Re-add the mods, in the new order
+ for cmod in &community_mods {
+ let community_moderator_form = CommunityModeratorForm {
+ community_id: cmod.community.id,
+ person_id: cmod.moderator.id,
+ };
+
+ let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
+ blocking(context.pool(), join)
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "community_moderator_already_exists"))?;
+ }
+
+ // Mod tables
+ let form = ModTransferCommunityForm {
+ mod_person_id: local_user_view.person.id,
+ other_person_id: data.person_id,
+ community_id: data.community_id,
+ removed: Some(false),
+ };
+ blocking(context.pool(), move |conn| {
+ ModTransferCommunity::create(conn, &form)
+ })
+ .await??;
+
+ let community_id = data.community_id;
+ let person_id = local_user_view.person.id;
+ let community_view = blocking(context.pool(), move |conn| {
+ CommunityView::read(conn, community_id, Some(person_id))
+ })
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?;
+
+ let community_id = data.community_id;
+ let moderators = blocking(context.pool(), move |conn| {
+ CommunityModeratorView::for_community(conn, community_id)
+ })
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?;
+
+ // Return the jwt
+ Ok(GetCommunityResponse {
+ community_view,
+ site: None,
+ moderators,
+ online: 0,
+ })
+ }
+}
do_websocket_operation::<PasswordReset>(context, id, op, data).await
}
UserOperation::PasswordChange => {
- do_websocket_operation::<PasswordChange>(context, id, op, data).await
+ do_websocket_operation::<PasswordChangeAfterReset>(context, id, op, data).await
}
UserOperation::UserJoin => do_websocket_operation::<UserJoin>(context, id, op, data).await,
UserOperation::PostJoin => do_websocket_operation::<PostJoin>(context, id, op, data).await,
+++ /dev/null
-use crate::{captcha_as_wav_base64, Perform};
-use actix_web::web::Data;
-use bcrypt::verify;
-use captcha::{gen, Difficulty};
-use chrono::Duration;
-use lemmy_api_common::{
- blocking,
- check_image_has_local_domain,
- check_registration_application,
- get_local_user_view_from_jwt,
- is_admin,
- password_length_check,
- person::*,
- remove_user_data,
- send_email_verification_success,
- send_password_reset_email,
- send_verification_email,
-};
-use lemmy_apub::{
- activities::block::SiteOrCommunity,
- protocol::activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
-};
-use lemmy_db_schema::{
- diesel_option_overwrite,
- diesel_option_overwrite_to_url,
- from_opt_str_to_opt_enum,
- naive_now,
- source::{
- comment::Comment,
- email_verification::EmailVerification,
- local_user::{LocalUser, LocalUserForm},
- moderator::*,
- password_reset_request::*,
- person::*,
- person_block::{PersonBlock, PersonBlockForm},
- person_mention::*,
- private_message::PrivateMessage,
- site::*,
- },
- traits::{Blockable, Crud},
- SortType,
-};
-use lemmy_db_views::{
- comment_report_view::CommentReportView,
- comment_view::{CommentQueryBuilder, CommentView},
- local_user_view::LocalUserView,
- post_report_view::PostReportView,
- private_message_view::PrivateMessageView,
-};
-use lemmy_db_views_actor::{
- person_mention_view::{PersonMentionQueryBuilder, PersonMentionView},
- person_view::PersonViewSafe,
-};
-use lemmy_utils::{
- claims::Claims,
- utils::{is_valid_display_name, is_valid_matrix_id, naive_from_unix},
- ConnectionId,
- LemmyError,
-};
-use lemmy_websocket::{
- messages::{CaptchaItem, SendAllMessage},
- LemmyContext,
- UserOperation,
-};
-
-#[async_trait::async_trait(?Send)]
-impl Perform for Login {
- type Response = LoginResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<LoginResponse, LemmyError> {
- let data: &Login = self;
-
- // Fetch that username / email
- let username_or_email = data.username_or_email.clone();
- let local_user_view = blocking(context.pool(), move |conn| {
- LocalUserView::find_by_email_or_name(conn, &username_or_email)
- })
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_that_username_or_email"))?;
-
- // Verify the password
- let valid: bool = verify(
- &data.password,
- &local_user_view.local_user.password_encrypted,
- )
- .unwrap_or(false);
- if !valid {
- return Err(LemmyError::from_message("password_incorrect"));
- }
-
- let site = blocking(context.pool(), Site::read_local_site).await??;
- if site.require_email_verification && !local_user_view.local_user.email_verified {
- return Err(LemmyError::from_message("email_not_verified"));
- }
-
- check_registration_application(&site, &local_user_view, context.pool()).await?;
-
- // Return the jwt
- Ok(LoginResponse {
- jwt: Some(
- Claims::jwt(
- local_user_view.local_user.id.0,
- &context.secret().jwt_secret,
- &context.settings().hostname,
- )?
- .into(),
- ),
- verify_email_sent: false,
- registration_created: false,
- })
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for GetCaptcha {
- type Response = GetCaptchaResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<Self::Response, LemmyError> {
- let captcha_settings = context.settings().captcha;
-
- if !captcha_settings.enabled {
- return Ok(GetCaptchaResponse { ok: None });
- }
-
- let captcha = match captcha_settings.difficulty.as_str() {
- "easy" => gen(Difficulty::Easy),
- "medium" => gen(Difficulty::Medium),
- "hard" => gen(Difficulty::Hard),
- _ => gen(Difficulty::Medium),
- };
-
- let answer = captcha.chars_as_string();
-
- let png = captcha.as_base64().expect("failed to generate captcha");
-
- let uuid = uuid::Uuid::new_v4().to_string();
-
- let wav = captcha_as_wav_base64(&captcha);
-
- let captcha_item = CaptchaItem {
- answer,
- uuid: uuid.to_owned(),
- expires: naive_now() + Duration::minutes(10), // expires in 10 minutes
- };
-
- // Stores the captcha item on the queue
- context.chat_server().do_send(captcha_item);
-
- Ok(GetCaptchaResponse {
- ok: Some(CaptchaResponse { png, wav, uuid }),
- })
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for SaveUserSettings {
- type Response = LoginResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<LoginResponse, LemmyError> {
- let data: &SaveUserSettings = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- let avatar = diesel_option_overwrite_to_url(&data.avatar)?;
- let banner = diesel_option_overwrite_to_url(&data.banner)?;
- let bio = diesel_option_overwrite(&data.bio);
- let display_name = diesel_option_overwrite(&data.display_name);
- let matrix_user_id = diesel_option_overwrite(&data.matrix_user_id);
- let bot_account = data.bot_account;
- let email_deref = data.email.as_deref().map(|e| e.to_owned());
- let email = diesel_option_overwrite(&email_deref);
-
- check_image_has_local_domain(avatar.as_ref().unwrap_or(&None))?;
- check_image_has_local_domain(banner.as_ref().unwrap_or(&None))?;
-
- if let Some(Some(email)) = &email {
- let previous_email = local_user_view.local_user.email.clone().unwrap_or_default();
- // Only send the verification email if there was an email change
- if previous_email.ne(email) {
- send_verification_email(&local_user_view, email, context.pool(), &context.settings())
- .await?;
- }
- }
-
- // When the site requires email, make sure email is not Some(None). IE, an overwrite to a None value
- if let Some(email) = &email {
- let site_fut = blocking(context.pool(), Site::read_local_site);
- if email.is_none() && site_fut.await??.require_email_verification {
- return Err(LemmyError::from_message("email_required"));
- }
- }
-
- if let Some(Some(bio)) = &bio {
- if bio.chars().count() > 300 {
- return Err(LemmyError::from_message("bio_length_overflow"));
- }
- }
-
- if let Some(Some(display_name)) = &display_name {
- if !is_valid_display_name(
- display_name.trim(),
- context.settings().actor_name_max_length,
- ) {
- return Err(LemmyError::from_message("invalid_username"));
- }
- }
-
- if let Some(Some(matrix_user_id)) = &matrix_user_id {
- if !is_valid_matrix_id(matrix_user_id) {
- return Err(LemmyError::from_message("invalid_matrix_id"));
- }
- }
-
- let local_user_id = local_user_view.local_user.id;
- let person_id = local_user_view.person.id;
- let default_listing_type = data.default_listing_type;
- let default_sort_type = data.default_sort_type;
- let password_encrypted = local_user_view.local_user.password_encrypted;
- let public_key = local_user_view.person.public_key;
-
- let person_form = PersonForm {
- name: local_user_view.person.name,
- avatar,
- banner,
- inbox_url: None,
- display_name,
- published: None,
- updated: Some(naive_now()),
- banned: None,
- deleted: None,
- actor_id: None,
- bio,
- local: None,
- admin: None,
- private_key: None,
- public_key,
- last_refreshed_at: None,
- shared_inbox_url: None,
- matrix_user_id,
- bot_account,
- ban_expires: None,
- };
-
- blocking(context.pool(), move |conn| {
- Person::update(conn, person_id, &person_form)
- })
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "user_already_exists"))?;
-
- let local_user_form = LocalUserForm {
- person_id: Some(person_id),
- email,
- password_encrypted: Some(password_encrypted),
- show_nsfw: data.show_nsfw,
- show_bot_accounts: data.show_bot_accounts,
- show_scores: data.show_scores,
- theme: data.theme.to_owned(),
- default_sort_type,
- default_listing_type,
- lang: data.lang.to_owned(),
- show_avatars: data.show_avatars,
- show_read_posts: data.show_read_posts,
- show_new_post_notifs: data.show_new_post_notifs,
- send_notifications_to_email: data.send_notifications_to_email,
- email_verified: None,
- accepted_application: None,
- };
-
- let local_user_res = blocking(context.pool(), move |conn| {
- LocalUser::update(conn, local_user_id, &local_user_form)
- })
- .await?;
- let updated_local_user = match local_user_res {
- Ok(u) => u,
- Err(e) => {
- let err_type = if e.to_string()
- == "duplicate key value violates unique constraint \"local_user_email_key\""
- {
- "email_already_exists"
- } else {
- "user_already_exists"
- };
-
- return Err(LemmyError::from_error_message(e, err_type));
- }
- };
-
- // Return the jwt
- Ok(LoginResponse {
- jwt: Some(
- Claims::jwt(
- updated_local_user.id.0,
- &context.secret().jwt_secret,
- &context.settings().hostname,
- )?
- .into(),
- ),
- verify_email_sent: false,
- registration_created: false,
- })
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for ChangePassword {
- type Response = LoginResponse;
-
- #[tracing::instrument(skip(self, context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<LoginResponse, LemmyError> {
- let data: &ChangePassword = self;
- let local_user_view =
- get_local_user_view_from_jwt(data.auth.as_ref(), context.pool(), context.secret()).await?;
-
- password_length_check(&data.new_password)?;
-
- // Make sure passwords match
- if data.new_password != data.new_password_verify {
- return Err(LemmyError::from_message("passwords_dont_match"));
- }
-
- // Check the old password
- let valid: bool = verify(
- &data.old_password,
- &local_user_view.local_user.password_encrypted,
- )
- .unwrap_or(false);
- if !valid {
- return Err(LemmyError::from_message("password_incorrect"));
- }
-
- let local_user_id = local_user_view.local_user.id;
- let new_password = data.new_password.to_owned();
- let updated_local_user = blocking(context.pool(), move |conn| {
- LocalUser::update_password(conn, local_user_id, &new_password)
- })
- .await??;
-
- // Return the jwt
- Ok(LoginResponse {
- jwt: Some(
- Claims::jwt(
- updated_local_user.id.0,
- &context.secret().jwt_secret,
- &context.settings().hostname,
- )?
- .into(),
- ),
- verify_email_sent: false,
- registration_created: false,
- })
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for AddAdmin {
- type Response = AddAdminResponse;
-
- #[tracing::instrument(skip(context, websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- websocket_id: Option<ConnectionId>,
- ) -> Result<AddAdminResponse, LemmyError> {
- let data: &AddAdmin = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- // Make sure user is an admin
- is_admin(&local_user_view)?;
-
- let added = data.added;
- let added_person_id = data.person_id;
- let added_admin = blocking(context.pool(), move |conn| {
- Person::add_admin(conn, added_person_id, added)
- })
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_user"))?;
-
- // Mod tables
- let form = ModAddForm {
- mod_person_id: local_user_view.person.id,
- other_person_id: added_admin.id,
- removed: Some(!data.added),
- };
-
- blocking(context.pool(), move |conn| ModAdd::create(conn, &form)).await??;
-
- let admins = blocking(context.pool(), PersonViewSafe::admins).await??;
-
- let res = AddAdminResponse { admins };
-
- context.chat_server().do_send(SendAllMessage {
- op: UserOperation::AddAdmin,
- response: res.clone(),
- websocket_id,
- });
-
- Ok(res)
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for BanPerson {
- type Response = BanPersonResponse;
-
- #[tracing::instrument(skip(context, websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- websocket_id: Option<ConnectionId>,
- ) -> Result<BanPersonResponse, LemmyError> {
- let data: &BanPerson = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- // Make sure user is an admin
- is_admin(&local_user_view)?;
-
- let ban = data.ban;
- let banned_person_id = data.person_id;
- let expires = data.expires.map(naive_from_unix);
-
- let ban_person = move |conn: &'_ _| Person::ban_person(conn, banned_person_id, ban, expires);
- let person = blocking(context.pool(), ban_person)
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_user"))?;
-
- // Remove their data if that's desired
- let remove_data = data.remove_data.unwrap_or(false);
- if remove_data {
- remove_user_data(person.id, context.pool()).await?;
- }
-
- // Mod tables
- let form = ModBanForm {
- mod_person_id: local_user_view.person.id,
- other_person_id: data.person_id,
- reason: data.reason.to_owned(),
- banned: Some(data.ban),
- expires,
- };
-
- blocking(context.pool(), move |conn| ModBan::create(conn, &form)).await??;
-
- let person_id = data.person_id;
- let person_view = blocking(context.pool(), move |conn| {
- PersonViewSafe::read(conn, person_id)
- })
- .await??;
-
- let site = SiteOrCommunity::Site(
- blocking(context.pool(), Site::read_local_site)
- .await??
- .into(),
- );
- // if the action affects a local user, federate to other instances
- if person.local {
- if ban {
- BlockUser::send(
- &site,
- &person.into(),
- &local_user_view.person.into(),
- remove_data,
- data.reason.clone(),
- expires,
- context,
- )
- .await?;
- } else {
- UndoBlockUser::send(
- &site,
- &person.into(),
- &local_user_view.person.into(),
- data.reason.clone(),
- context,
- )
- .await?;
- }
- }
-
- let res = BanPersonResponse {
- person_view,
- banned: data.ban,
- };
-
- context.chat_server().do_send(SendAllMessage {
- op: UserOperation::BanPerson,
- response: res.clone(),
- websocket_id,
- });
-
- Ok(res)
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for GetBannedPersons {
- type Response = BannedPersonsResponse;
-
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<Self::Response, LemmyError> {
- let data: &GetBannedPersons = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- // Make sure user is an admin
- is_admin(&local_user_view)?;
-
- let banned = blocking(context.pool(), PersonViewSafe::banned).await??;
-
- let res = Self::Response { banned };
-
- Ok(res)
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for BlockPerson {
- type Response = BlockPersonResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<BlockPersonResponse, LemmyError> {
- let data: &BlockPerson = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- let target_id = data.person_id;
- let person_id = local_user_view.person.id;
-
- // Don't let a person block themselves
- if target_id == person_id {
- return Err(LemmyError::from_message("cant_block_yourself"));
- }
-
- let person_block_form = PersonBlockForm {
- person_id,
- target_id,
- };
-
- if data.block {
- let block = move |conn: &'_ _| PersonBlock::block(conn, &person_block_form);
- blocking(context.pool(), block)
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "person_block_already_exists"))?;
- } else {
- let unblock = move |conn: &'_ _| PersonBlock::unblock(conn, &person_block_form);
- blocking(context.pool(), unblock)
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "person_block_already_exists"))?;
- }
-
- // TODO does any federated stuff need to be done here?
-
- let person_view = blocking(context.pool(), move |conn| {
- PersonViewSafe::read(conn, target_id)
- })
- .await??;
-
- let res = BlockPersonResponse {
- person_view,
- blocked: data.block,
- };
-
- Ok(res)
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for GetReplies {
- type Response = GetRepliesResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<GetRepliesResponse, LemmyError> {
- let data: &GetReplies = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- let sort: Option<SortType> = from_opt_str_to_opt_enum(&data.sort);
-
- let page = data.page;
- let limit = data.limit;
- let unread_only = data.unread_only;
- let person_id = local_user_view.person.id;
- let show_bot_accounts = local_user_view.local_user.show_bot_accounts;
-
- let replies = blocking(context.pool(), move |conn| {
- CommentQueryBuilder::create(conn)
- .sort(sort)
- .unread_only(unread_only)
- .recipient_id(person_id)
- .show_bot_accounts(show_bot_accounts)
- .my_person_id(person_id)
- .page(page)
- .limit(limit)
- .list()
- })
- .await??;
-
- Ok(GetRepliesResponse { replies })
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for GetPersonMentions {
- type Response = GetPersonMentionsResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<GetPersonMentionsResponse, LemmyError> {
- let data: &GetPersonMentions = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- let sort: Option<SortType> = from_opt_str_to_opt_enum(&data.sort);
-
- let page = data.page;
- let limit = data.limit;
- let unread_only = data.unread_only;
- let person_id = local_user_view.person.id;
- let mentions = blocking(context.pool(), move |conn| {
- PersonMentionQueryBuilder::create(conn)
- .recipient_id(person_id)
- .my_person_id(person_id)
- .sort(sort)
- .unread_only(unread_only)
- .page(page)
- .limit(limit)
- .list()
- })
- .await??;
-
- Ok(GetPersonMentionsResponse { mentions })
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for MarkPersonMentionAsRead {
- type Response = PersonMentionResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<PersonMentionResponse, LemmyError> {
- let data: &MarkPersonMentionAsRead = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- let person_mention_id = data.person_mention_id;
- let read_person_mention = blocking(context.pool(), move |conn| {
- PersonMention::read(conn, person_mention_id)
- })
- .await??;
-
- if local_user_view.person.id != read_person_mention.recipient_id {
- return Err(LemmyError::from_message("couldnt_update_comment"));
- }
-
- let person_mention_id = read_person_mention.id;
- let read = data.read;
- let update_mention =
- move |conn: &'_ _| PersonMention::update_read(conn, person_mention_id, read);
- blocking(context.pool(), update_mention)
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?;
-
- let person_mention_id = read_person_mention.id;
- let person_id = local_user_view.person.id;
- let person_mention_view = blocking(context.pool(), move |conn| {
- PersonMentionView::read(conn, person_mention_id, Some(person_id))
- })
- .await??;
-
- Ok(PersonMentionResponse {
- person_mention_view,
- })
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for MarkAllAsRead {
- type Response = GetRepliesResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<GetRepliesResponse, LemmyError> {
- let data: &MarkAllAsRead = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- let person_id = local_user_view.person.id;
- let replies = blocking(context.pool(), move |conn| {
- CommentQueryBuilder::create(conn)
- .my_person_id(person_id)
- .recipient_id(person_id)
- .unread_only(true)
- .page(1)
- .limit(999)
- .list()
- })
- .await??;
-
- // TODO: this should probably be a bulk operation
- // Not easy to do as a bulk operation,
- // because recipient_id isn't in the comment table
- for comment_view in &replies {
- let reply_id = comment_view.comment.id;
- let mark_as_read = move |conn: &'_ _| Comment::update_read(conn, reply_id, true);
- blocking(context.pool(), mark_as_read)
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?;
- }
-
- // Mark all user mentions as read
- let update_person_mentions =
- move |conn: &'_ _| PersonMention::mark_all_as_read(conn, person_id);
- blocking(context.pool(), update_person_mentions)
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?;
-
- // Mark all private_messages as read
- let update_pm = move |conn: &'_ _| PrivateMessage::mark_all_as_read(conn, person_id);
- blocking(context.pool(), update_pm)
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_private_message"))?;
-
- Ok(GetRepliesResponse { replies: vec![] })
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for PasswordReset {
- type Response = PasswordResetResponse;
-
- #[tracing::instrument(skip(self, context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<PasswordResetResponse, LemmyError> {
- let data: &PasswordReset = self;
-
- // Fetch that email
- let email = data.email.clone();
- let local_user_view = blocking(context.pool(), move |conn| {
- LocalUserView::find_by_email(conn, &email)
- })
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_that_username_or_email"))?;
-
- // Email the pure token to the user.
- send_password_reset_email(&local_user_view, context.pool(), &context.settings()).await?;
- Ok(PasswordResetResponse {})
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for PasswordChange {
- type Response = LoginResponse;
-
- #[tracing::instrument(skip(self, context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<LoginResponse, LemmyError> {
- let data: &PasswordChange = self;
-
- // Fetch the user_id from the token
- let token = data.token.clone();
- let local_user_id = blocking(context.pool(), move |conn| {
- PasswordResetRequest::read_from_token(conn, &token).map(|p| p.local_user_id)
- })
- .await??;
-
- password_length_check(&data.password)?;
-
- // Make sure passwords match
- if data.password != data.password_verify {
- return Err(LemmyError::from_message("passwords_dont_match"));
- }
-
- // Update the user with the new password
- let password = data.password.clone();
- let updated_local_user = blocking(context.pool(), move |conn| {
- LocalUser::update_password(conn, local_user_id, &password)
- })
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_user"))?;
-
- // Return the jwt
- Ok(LoginResponse {
- jwt: Some(
- Claims::jwt(
- updated_local_user.id.0,
- &context.secret().jwt_secret,
- &context.settings().hostname,
- )?
- .into(),
- ),
- verify_email_sent: false,
- registration_created: false,
- })
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for GetReportCount {
- type Response = GetReportCountResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<GetReportCountResponse, LemmyError> {
- let data: &GetReportCount = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- let person_id = local_user_view.person.id;
- let admin = local_user_view.person.admin;
- let community_id = data.community_id;
-
- let comment_reports = blocking(context.pool(), move |conn| {
- CommentReportView::get_report_count(conn, person_id, admin, community_id)
- })
- .await??;
-
- let post_reports = blocking(context.pool(), move |conn| {
- PostReportView::get_report_count(conn, person_id, admin, community_id)
- })
- .await??;
-
- let res = GetReportCountResponse {
- community_id,
- comment_reports,
- post_reports,
- };
-
- Ok(res)
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for GetUnreadCount {
- type Response = GetUnreadCountResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<Self::Response, LemmyError> {
- let data = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- let person_id = local_user_view.person.id;
-
- let replies = blocking(context.pool(), move |conn| {
- CommentView::get_unread_replies(conn, person_id)
- })
- .await??;
-
- let mentions = blocking(context.pool(), move |conn| {
- PersonMentionView::get_unread_mentions(conn, person_id)
- })
- .await??;
-
- let private_messages = blocking(context.pool(), move |conn| {
- PrivateMessageView::get_unread_messages(conn, person_id)
- })
- .await??;
-
- let res = Self::Response {
- replies,
- mentions,
- private_messages,
- };
-
- Ok(res)
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for VerifyEmail {
- type Response = VerifyEmailResponse;
-
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<usize>,
- ) -> Result<Self::Response, LemmyError> {
- let token = self.token.clone();
- let verification = blocking(context.pool(), move |conn| {
- EmailVerification::read_for_token(conn, &token)
- })
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "token_not_found"))?;
-
- let form = LocalUserForm {
- // necessary in case this is a new signup
- email_verified: Some(true),
- // necessary in case email of an existing user was changed
- email: Some(Some(verification.email)),
- ..LocalUserForm::default()
- };
- let local_user_id = verification.local_user_id;
- blocking(context.pool(), move |conn| {
- LocalUser::update(conn, local_user_id, &form)
- })
- .await??;
-
- let local_user_view = blocking(context.pool(), move |conn| {
- LocalUserView::read(conn, local_user_id)
- })
- .await??;
-
- send_email_verification_success(&local_user_view, &context.settings())?;
-
- blocking(context.pool(), move |conn| {
- EmailVerification::delete_old_tokens_for_local_user(conn, local_user_id)
- })
- .await??;
-
- Ok(VerifyEmailResponse {})
- }
-}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ get_local_user_view_from_jwt,
+ is_admin,
+ person::{AddAdmin, AddAdminResponse},
+};
+use lemmy_db_schema::{
+ source::{
+ moderator::{ModAdd, ModAddForm},
+ person::Person,
+ },
+ traits::Crud,
+};
+use lemmy_db_views_actor::person_view::PersonViewSafe;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::{messages::SendAllMessage, LemmyContext, UserOperation};
+
+#[async_trait::async_trait(?Send)]
+impl Perform for AddAdmin {
+ type Response = AddAdminResponse;
+
+ #[tracing::instrument(skip(context, websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ websocket_id: Option<ConnectionId>,
+ ) -> Result<AddAdminResponse, LemmyError> {
+ let data: &AddAdmin = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ // Make sure user is an admin
+ is_admin(&local_user_view)?;
+
+ let added = data.added;
+ let added_person_id = data.person_id;
+ let added_admin = blocking(context.pool(), move |conn| {
+ Person::add_admin(conn, added_person_id, added)
+ })
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_user"))?;
+
+ // Mod tables
+ let form = ModAddForm {
+ mod_person_id: local_user_view.person.id,
+ other_person_id: added_admin.id,
+ removed: Some(!data.added),
+ };
+
+ blocking(context.pool(), move |conn| ModAdd::create(conn, &form)).await??;
+
+ let admins = blocking(context.pool(), PersonViewSafe::admins).await??;
+
+ let res = AddAdminResponse { admins };
+
+ context.chat_server().do_send(SendAllMessage {
+ op: UserOperation::AddAdmin,
+ response: res.clone(),
+ websocket_id,
+ });
+
+ Ok(res)
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ get_local_user_view_from_jwt,
+ is_admin,
+ person::{BanPerson, BanPersonResponse},
+ remove_user_data,
+};
+use lemmy_apub::{
+ activities::block::SiteOrCommunity,
+ protocol::activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
+};
+use lemmy_db_schema::{
+ source::{
+ moderator::{ModBan, ModBanForm},
+ person::Person,
+ site::Site,
+ },
+ traits::Crud,
+};
+use lemmy_db_views_actor::person_view::PersonViewSafe;
+use lemmy_utils::{utils::naive_from_unix, ConnectionId, LemmyError};
+use lemmy_websocket::{messages::SendAllMessage, LemmyContext, UserOperation};
+
+#[async_trait::async_trait(?Send)]
+impl Perform for BanPerson {
+ type Response = BanPersonResponse;
+
+ #[tracing::instrument(skip(context, websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ websocket_id: Option<ConnectionId>,
+ ) -> Result<BanPersonResponse, LemmyError> {
+ let data: &BanPerson = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ // Make sure user is an admin
+ is_admin(&local_user_view)?;
+
+ let ban = data.ban;
+ let banned_person_id = data.person_id;
+ let expires = data.expires.map(naive_from_unix);
+
+ let ban_person = move |conn: &'_ _| Person::ban_person(conn, banned_person_id, ban, expires);
+ let person = blocking(context.pool(), ban_person)
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_user"))?;
+
+ // Remove their data if that's desired
+ let remove_data = data.remove_data.unwrap_or(false);
+ if remove_data {
+ remove_user_data(person.id, context.pool()).await?;
+ }
+
+ // Mod tables
+ let form = ModBanForm {
+ mod_person_id: local_user_view.person.id,
+ other_person_id: data.person_id,
+ reason: data.reason.to_owned(),
+ banned: Some(data.ban),
+ expires,
+ };
+
+ blocking(context.pool(), move |conn| ModBan::create(conn, &form)).await??;
+
+ let person_id = data.person_id;
+ let person_view = blocking(context.pool(), move |conn| {
+ PersonViewSafe::read(conn, person_id)
+ })
+ .await??;
+
+ let site = SiteOrCommunity::Site(
+ blocking(context.pool(), Site::read_local_site)
+ .await??
+ .into(),
+ );
+ // if the action affects a local user, federate to other instances
+ if person.local {
+ if ban {
+ BlockUser::send(
+ &site,
+ &person.into(),
+ &local_user_view.person.into(),
+ remove_data,
+ data.reason.clone(),
+ expires,
+ context,
+ )
+ .await?;
+ } else {
+ UndoBlockUser::send(
+ &site,
+ &person.into(),
+ &local_user_view.person.into(),
+ data.reason.clone(),
+ context,
+ )
+ .await?;
+ }
+ }
+
+ let res = BanPersonResponse {
+ person_view,
+ banned: data.ban,
+ };
+
+ context.chat_server().do_send(SendAllMessage {
+ op: UserOperation::BanPerson,
+ response: res.clone(),
+ websocket_id,
+ });
+
+ Ok(res)
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ get_local_user_view_from_jwt,
+ person::{BlockPerson, BlockPersonResponse},
+};
+use lemmy_db_schema::{
+ source::person_block::{PersonBlock, PersonBlockForm},
+ traits::Blockable,
+};
+use lemmy_db_views_actor::person_view::PersonViewSafe;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for BlockPerson {
+ type Response = BlockPersonResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<BlockPersonResponse, LemmyError> {
+ let data: &BlockPerson = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ let target_id = data.person_id;
+ let person_id = local_user_view.person.id;
+
+ // Don't let a person block themselves
+ if target_id == person_id {
+ return Err(LemmyError::from_message("cant_block_yourself"));
+ }
+
+ let person_block_form = PersonBlockForm {
+ person_id,
+ target_id,
+ };
+
+ if data.block {
+ let block = move |conn: &'_ _| PersonBlock::block(conn, &person_block_form);
+ blocking(context.pool(), block)
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "person_block_already_exists"))?;
+ } else {
+ let unblock = move |conn: &'_ _| PersonBlock::unblock(conn, &person_block_form);
+ blocking(context.pool(), unblock)
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "person_block_already_exists"))?;
+ }
+
+ let person_view = blocking(context.pool(), move |conn| {
+ PersonViewSafe::read(conn, target_id)
+ })
+ .await??;
+
+ let res = BlockPersonResponse {
+ person_view,
+ blocked: data.block,
+ };
+
+ Ok(res)
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use bcrypt::verify;
+use lemmy_api_common::{
+ blocking,
+ get_local_user_view_from_jwt,
+ password_length_check,
+ person::{ChangePassword, LoginResponse},
+};
+use lemmy_db_schema::source::local_user::LocalUser;
+use lemmy_utils::{claims::Claims, ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for ChangePassword {
+ type Response = LoginResponse;
+
+ #[tracing::instrument(skip(self, context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<LoginResponse, LemmyError> {
+ let data: &ChangePassword = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(data.auth.as_ref(), context.pool(), context.secret()).await?;
+
+ password_length_check(&data.new_password)?;
+
+ // Make sure passwords match
+ if data.new_password != data.new_password_verify {
+ return Err(LemmyError::from_message("passwords_dont_match"));
+ }
+
+ // Check the old password
+ let valid: bool = verify(
+ &data.old_password,
+ &local_user_view.local_user.password_encrypted,
+ )
+ .unwrap_or(false);
+ if !valid {
+ return Err(LemmyError::from_message("password_incorrect"));
+ }
+
+ let local_user_id = local_user_view.local_user.id;
+ let new_password = data.new_password.to_owned();
+ let updated_local_user = blocking(context.pool(), move |conn| {
+ LocalUser::update_password(conn, local_user_id, &new_password)
+ })
+ .await??;
+
+ // Return the jwt
+ Ok(LoginResponse {
+ jwt: Some(
+ Claims::jwt(
+ updated_local_user.id.0,
+ &context.secret().jwt_secret,
+ &context.settings().hostname,
+ )?
+ .into(),
+ ),
+ verify_email_sent: false,
+ registration_created: false,
+ })
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ password_length_check,
+ person::{LoginResponse, PasswordChangeAfterReset},
+};
+use lemmy_db_schema::source::{
+ local_user::LocalUser,
+ password_reset_request::PasswordResetRequest,
+};
+use lemmy_utils::{claims::Claims, ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for PasswordChangeAfterReset {
+ type Response = LoginResponse;
+
+ #[tracing::instrument(skip(self, context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<LoginResponse, LemmyError> {
+ let data: &PasswordChangeAfterReset = self;
+
+ // Fetch the user_id from the token
+ let token = data.token.clone();
+ let local_user_id = blocking(context.pool(), move |conn| {
+ PasswordResetRequest::read_from_token(conn, &token).map(|p| p.local_user_id)
+ })
+ .await??;
+
+ password_length_check(&data.password)?;
+
+ // Make sure passwords match
+ if data.password != data.password_verify {
+ return Err(LemmyError::from_message("passwords_dont_match"));
+ }
+
+ // Update the user with the new password
+ let password = data.password.clone();
+ let updated_local_user = blocking(context.pool(), move |conn| {
+ LocalUser::update_password(conn, local_user_id, &password)
+ })
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_user"))?;
+
+ // Return the jwt
+ Ok(LoginResponse {
+ jwt: Some(
+ Claims::jwt(
+ updated_local_user.id.0,
+ &context.secret().jwt_secret,
+ &context.settings().hostname,
+ )?
+ .into(),
+ ),
+ verify_email_sent: false,
+ registration_created: false,
+ })
+ }
+}
--- /dev/null
+use crate::{captcha_as_wav_base64, Perform};
+use actix_web::web::Data;
+use captcha::{gen, Difficulty};
+use chrono::Duration;
+use lemmy_api_common::person::{CaptchaResponse, GetCaptcha, GetCaptchaResponse};
+use lemmy_db_schema::naive_now;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::{messages::CaptchaItem, LemmyContext};
+
+#[async_trait::async_trait(?Send)]
+impl Perform for GetCaptcha {
+ type Response = GetCaptchaResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<Self::Response, LemmyError> {
+ let captcha_settings = context.settings().captcha;
+
+ if !captcha_settings.enabled {
+ return Ok(GetCaptchaResponse { ok: None });
+ }
+
+ let captcha = gen(match captcha_settings.difficulty.as_str() {
+ "easy" => Difficulty::Easy,
+ "hard" => Difficulty::Hard,
+ _ => Difficulty::Medium,
+ });
+
+ let answer = captcha.chars_as_string();
+
+ let png = captcha.as_base64().expect("failed to generate captcha");
+
+ let uuid = uuid::Uuid::new_v4().to_string();
+
+ let wav = captcha_as_wav_base64(&captcha);
+
+ let captcha_item = CaptchaItem {
+ answer,
+ uuid: uuid.to_owned(),
+ expires: naive_now() + Duration::minutes(10), // expires in 10 minutes
+ };
+
+ // Stores the captcha item on the queue
+ context.chat_server().do_send(captcha_item);
+
+ Ok(GetCaptchaResponse {
+ ok: Some(CaptchaResponse { png, wav, uuid }),
+ })
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ get_local_user_view_from_jwt,
+ is_admin,
+ person::{BannedPersonsResponse, GetBannedPersons},
+};
+use lemmy_db_views_actor::person_view::PersonViewSafe;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for GetBannedPersons {
+ type Response = BannedPersonsResponse;
+
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<Self::Response, LemmyError> {
+ let data: &GetBannedPersons = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ // Make sure user is an admin
+ is_admin(&local_user_view)?;
+
+ let banned = blocking(context.pool(), PersonViewSafe::banned).await??;
+
+ let res = Self::Response { banned };
+
+ Ok(res)
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use bcrypt::verify;
+use lemmy_api_common::{
+ blocking,
+ check_registration_application,
+ person::{Login, LoginResponse},
+};
+use lemmy_db_schema::source::site::Site;
+use lemmy_db_views::local_user_view::LocalUserView;
+use lemmy_utils::{claims::Claims, ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for Login {
+ type Response = LoginResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<LoginResponse, LemmyError> {
+ let data: &Login = self;
+
+ // Fetch that username / email
+ let username_or_email = data.username_or_email.clone();
+ let local_user_view = blocking(context.pool(), move |conn| {
+ LocalUserView::find_by_email_or_name(conn, &username_or_email)
+ })
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_that_username_or_email"))?;
+
+ // Verify the password
+ let valid: bool = verify(
+ &data.password,
+ &local_user_view.local_user.password_encrypted,
+ )
+ .unwrap_or(false);
+ if !valid {
+ return Err(LemmyError::from_message("password_incorrect"));
+ }
+
+ let site = blocking(context.pool(), Site::read_local_site).await??;
+ if site.require_email_verification && !local_user_view.local_user.email_verified {
+ return Err(LemmyError::from_message("email_not_verified"));
+ }
+
+ check_registration_application(&site, &local_user_view, context.pool()).await?;
+
+ // Return the jwt
+ Ok(LoginResponse {
+ jwt: Some(
+ Claims::jwt(
+ local_user_view.local_user.id.0,
+ &context.secret().jwt_secret,
+ &context.settings().hostname,
+ )?
+ .into(),
+ ),
+ verify_email_sent: false,
+ registration_created: false,
+ })
+ }
+}
--- /dev/null
+mod add_admin;
+mod ban_person;
+mod block;
+mod change_password;
+mod change_password_after_reset;
+mod get_captcha;
+mod list_banned;
+mod login;
+mod notifications;
+mod report_count;
+mod reset_password;
+mod save_settings;
+mod verify_email;
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ get_local_user_view_from_jwt,
+ person::{GetPersonMentions, GetPersonMentionsResponse},
+};
+use lemmy_db_schema::{from_opt_str_to_opt_enum, SortType};
+use lemmy_db_views_actor::person_mention_view::PersonMentionQueryBuilder;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for GetPersonMentions {
+ type Response = GetPersonMentionsResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<GetPersonMentionsResponse, LemmyError> {
+ let data: &GetPersonMentions = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ let sort: Option<SortType> = from_opt_str_to_opt_enum(&data.sort);
+
+ let page = data.page;
+ let limit = data.limit;
+ let unread_only = data.unread_only;
+ let person_id = local_user_view.person.id;
+ let mentions = blocking(context.pool(), move |conn| {
+ PersonMentionQueryBuilder::create(conn)
+ .recipient_id(person_id)
+ .my_person_id(person_id)
+ .sort(sort)
+ .unread_only(unread_only)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
+
+ Ok(GetPersonMentionsResponse { mentions })
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ get_local_user_view_from_jwt,
+ person::{GetReplies, GetRepliesResponse},
+};
+use lemmy_db_schema::{from_opt_str_to_opt_enum, SortType};
+use lemmy_db_views::comment_view::CommentQueryBuilder;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for GetReplies {
+ type Response = GetRepliesResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<GetRepliesResponse, LemmyError> {
+ let data: &GetReplies = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ let sort: Option<SortType> = from_opt_str_to_opt_enum(&data.sort);
+
+ let page = data.page;
+ let limit = data.limit;
+ let unread_only = data.unread_only;
+ let person_id = local_user_view.person.id;
+ let show_bot_accounts = local_user_view.local_user.show_bot_accounts;
+
+ let replies = blocking(context.pool(), move |conn| {
+ CommentQueryBuilder::create(conn)
+ .sort(sort)
+ .unread_only(unread_only)
+ .recipient_id(person_id)
+ .show_bot_accounts(show_bot_accounts)
+ .my_person_id(person_id)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
+
+ Ok(GetRepliesResponse { replies })
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ get_local_user_view_from_jwt,
+ person::{GetRepliesResponse, MarkAllAsRead},
+};
+use lemmy_db_schema::source::{
+ comment::Comment,
+ person_mention::PersonMention,
+ private_message::PrivateMessage,
+};
+use lemmy_db_views::comment_view::CommentQueryBuilder;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for MarkAllAsRead {
+ type Response = GetRepliesResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<GetRepliesResponse, LemmyError> {
+ let data: &MarkAllAsRead = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ let person_id = local_user_view.person.id;
+ let replies = blocking(context.pool(), move |conn| {
+ CommentQueryBuilder::create(conn)
+ .my_person_id(person_id)
+ .recipient_id(person_id)
+ .unread_only(true)
+ .page(1)
+ .limit(999)
+ .list()
+ })
+ .await??;
+
+ // TODO: this should probably be a bulk operation
+ // Not easy to do as a bulk operation,
+ // because recipient_id isn't in the comment table
+ for comment_view in &replies {
+ let reply_id = comment_view.comment.id;
+ let mark_as_read = move |conn: &'_ _| Comment::update_read(conn, reply_id, true);
+ blocking(context.pool(), mark_as_read)
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?;
+ }
+
+ // Mark all user mentions as read
+ let update_person_mentions =
+ move |conn: &'_ _| PersonMention::mark_all_as_read(conn, person_id);
+ blocking(context.pool(), update_person_mentions)
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?;
+
+ // Mark all private_messages as read
+ let update_pm = move |conn: &'_ _| PrivateMessage::mark_all_as_read(conn, person_id);
+ blocking(context.pool(), update_pm)
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_private_message"))?;
+
+ Ok(GetRepliesResponse { replies: vec![] })
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ get_local_user_view_from_jwt,
+ person::{MarkPersonMentionAsRead, PersonMentionResponse},
+};
+use lemmy_db_schema::{source::person_mention::PersonMention, traits::Crud};
+use lemmy_db_views_actor::person_mention_view::PersonMentionView;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for MarkPersonMentionAsRead {
+ type Response = PersonMentionResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<PersonMentionResponse, LemmyError> {
+ let data: &MarkPersonMentionAsRead = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ let person_mention_id = data.person_mention_id;
+ let read_person_mention = blocking(context.pool(), move |conn| {
+ PersonMention::read(conn, person_mention_id)
+ })
+ .await??;
+
+ if local_user_view.person.id != read_person_mention.recipient_id {
+ return Err(LemmyError::from_message("couldnt_update_comment"));
+ }
+
+ let person_mention_id = read_person_mention.id;
+ let read = data.read;
+ let update_mention =
+ move |conn: &'_ _| PersonMention::update_read(conn, person_mention_id, read);
+ blocking(context.pool(), update_mention)
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?;
+
+ let person_mention_id = read_person_mention.id;
+ let person_id = local_user_view.person.id;
+ let person_mention_view = blocking(context.pool(), move |conn| {
+ PersonMentionView::read(conn, person_mention_id, Some(person_id))
+ })
+ .await??;
+
+ Ok(PersonMentionResponse {
+ person_mention_view,
+ })
+ }
+}
--- /dev/null
+mod list_mentions;
+mod list_replies;
+mod mark_all_read;
+mod mark_mention_read;
+mod unread_count;
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ get_local_user_view_from_jwt,
+ person::{GetUnreadCount, GetUnreadCountResponse},
+};
+use lemmy_db_views::{comment_view::CommentView, private_message_view::PrivateMessageView};
+use lemmy_db_views_actor::person_mention_view::PersonMentionView;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for GetUnreadCount {
+ type Response = GetUnreadCountResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<Self::Response, LemmyError> {
+ let data = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ let person_id = local_user_view.person.id;
+
+ let replies = blocking(context.pool(), move |conn| {
+ CommentView::get_unread_replies(conn, person_id)
+ })
+ .await??;
+
+ let mentions = blocking(context.pool(), move |conn| {
+ PersonMentionView::get_unread_mentions(conn, person_id)
+ })
+ .await??;
+
+ let private_messages = blocking(context.pool(), move |conn| {
+ PrivateMessageView::get_unread_messages(conn, person_id)
+ })
+ .await??;
+
+ let res = Self::Response {
+ replies,
+ mentions,
+ private_messages,
+ };
+
+ Ok(res)
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ get_local_user_view_from_jwt,
+ person::{GetReportCount, GetReportCountResponse},
+};
+use lemmy_db_views::{comment_report_view::CommentReportView, post_report_view::PostReportView};
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for GetReportCount {
+ type Response = GetReportCountResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<GetReportCountResponse, LemmyError> {
+ let data: &GetReportCount = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ let person_id = local_user_view.person.id;
+ let admin = local_user_view.person.admin;
+ let community_id = data.community_id;
+
+ let comment_reports = blocking(context.pool(), move |conn| {
+ CommentReportView::get_report_count(conn, person_id, admin, community_id)
+ })
+ .await??;
+
+ let post_reports = blocking(context.pool(), move |conn| {
+ PostReportView::get_report_count(conn, person_id, admin, community_id)
+ })
+ .await??;
+
+ let res = GetReportCountResponse {
+ community_id,
+ comment_reports,
+ post_reports,
+ };
+
+ Ok(res)
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ person::{PasswordReset, PasswordResetResponse},
+ send_password_reset_email,
+};
+use lemmy_db_views::local_user_view::LocalUserView;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for PasswordReset {
+ type Response = PasswordResetResponse;
+
+ #[tracing::instrument(skip(self, context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<PasswordResetResponse, LemmyError> {
+ let data: &PasswordReset = self;
+
+ // Fetch that email
+ let email = data.email.clone();
+ let local_user_view = blocking(context.pool(), move |conn| {
+ LocalUserView::find_by_email(conn, &email)
+ })
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_that_username_or_email"))?;
+
+ // Email the pure token to the user.
+ send_password_reset_email(&local_user_view, context.pool(), &context.settings()).await?;
+ Ok(PasswordResetResponse {})
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ check_image_has_local_domain,
+ get_local_user_view_from_jwt,
+ person::{LoginResponse, SaveUserSettings},
+ send_verification_email,
+};
+use lemmy_db_schema::{
+ diesel_option_overwrite,
+ diesel_option_overwrite_to_url,
+ naive_now,
+ source::{
+ local_user::{LocalUser, LocalUserForm},
+ person::{Person, PersonForm},
+ site::Site,
+ },
+ traits::Crud,
+};
+use lemmy_utils::{
+ claims::Claims,
+ utils::{is_valid_display_name, is_valid_matrix_id},
+ ConnectionId,
+ LemmyError,
+};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for SaveUserSettings {
+ type Response = LoginResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<LoginResponse, LemmyError> {
+ let data: &SaveUserSettings = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ let avatar = diesel_option_overwrite_to_url(&data.avatar)?;
+ let banner = diesel_option_overwrite_to_url(&data.banner)?;
+ let bio = diesel_option_overwrite(&data.bio);
+ let display_name = diesel_option_overwrite(&data.display_name);
+ let matrix_user_id = diesel_option_overwrite(&data.matrix_user_id);
+ let bot_account = data.bot_account;
+ let email_deref = data.email.as_deref().map(|e| e.to_owned());
+ let email = diesel_option_overwrite(&email_deref);
+
+ check_image_has_local_domain(avatar.as_ref().unwrap_or(&None))?;
+ check_image_has_local_domain(banner.as_ref().unwrap_or(&None))?;
+
+ if let Some(Some(email)) = &email {
+ let previous_email = local_user_view.local_user.email.clone().unwrap_or_default();
+ // Only send the verification email if there was an email change
+ if previous_email.ne(email) {
+ send_verification_email(&local_user_view, email, context.pool(), &context.settings())
+ .await?;
+ }
+ }
+
+ // When the site requires email, make sure email is not Some(None). IE, an overwrite to a None value
+ if let Some(email) = &email {
+ let site_fut = blocking(context.pool(), Site::read_local_site);
+ if email.is_none() && site_fut.await??.require_email_verification {
+ return Err(LemmyError::from_message("email_required"));
+ }
+ }
+
+ if let Some(Some(bio)) = &bio {
+ if bio.chars().count() > 300 {
+ return Err(LemmyError::from_message("bio_length_overflow"));
+ }
+ }
+
+ if let Some(Some(display_name)) = &display_name {
+ if !is_valid_display_name(
+ display_name.trim(),
+ context.settings().actor_name_max_length,
+ ) {
+ return Err(LemmyError::from_message("invalid_username"));
+ }
+ }
+
+ if let Some(Some(matrix_user_id)) = &matrix_user_id {
+ if !is_valid_matrix_id(matrix_user_id) {
+ return Err(LemmyError::from_message("invalid_matrix_id"));
+ }
+ }
+
+ let local_user_id = local_user_view.local_user.id;
+ let person_id = local_user_view.person.id;
+ let default_listing_type = data.default_listing_type;
+ let default_sort_type = data.default_sort_type;
+ let password_encrypted = local_user_view.local_user.password_encrypted;
+ let public_key = local_user_view.person.public_key;
+
+ let person_form = PersonForm {
+ name: local_user_view.person.name,
+ avatar,
+ banner,
+ inbox_url: None,
+ display_name,
+ published: None,
+ updated: Some(naive_now()),
+ banned: None,
+ deleted: None,
+ actor_id: None,
+ bio,
+ local: None,
+ admin: None,
+ private_key: None,
+ public_key,
+ last_refreshed_at: None,
+ shared_inbox_url: None,
+ matrix_user_id,
+ bot_account,
+ ban_expires: None,
+ };
+
+ blocking(context.pool(), move |conn| {
+ Person::update(conn, person_id, &person_form)
+ })
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "user_already_exists"))?;
+
+ let local_user_form = LocalUserForm {
+ person_id: Some(person_id),
+ email,
+ password_encrypted: Some(password_encrypted),
+ show_nsfw: data.show_nsfw,
+ show_bot_accounts: data.show_bot_accounts,
+ show_scores: data.show_scores,
+ theme: data.theme.to_owned(),
+ default_sort_type,
+ default_listing_type,
+ lang: data.lang.to_owned(),
+ show_avatars: data.show_avatars,
+ show_read_posts: data.show_read_posts,
+ show_new_post_notifs: data.show_new_post_notifs,
+ send_notifications_to_email: data.send_notifications_to_email,
+ email_verified: None,
+ accepted_application: None,
+ };
+
+ let local_user_res = blocking(context.pool(), move |conn| {
+ LocalUser::update(conn, local_user_id, &local_user_form)
+ })
+ .await?;
+ let updated_local_user = match local_user_res {
+ Ok(u) => u,
+ Err(e) => {
+ let err_type = if e.to_string()
+ == "duplicate key value violates unique constraint \"local_user_email_key\""
+ {
+ "email_already_exists"
+ } else {
+ "user_already_exists"
+ };
+
+ return Err(LemmyError::from_error_message(e, err_type));
+ }
+ };
+
+ // Return the jwt
+ Ok(LoginResponse {
+ jwt: Some(
+ Claims::jwt(
+ updated_local_user.id.0,
+ &context.secret().jwt_secret,
+ &context.settings().hostname,
+ )?
+ .into(),
+ ),
+ verify_email_sent: false,
+ registration_created: false,
+ })
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ person::{VerifyEmail, VerifyEmailResponse},
+ send_email_verification_success,
+};
+use lemmy_db_schema::{
+ source::{
+ email_verification::EmailVerification,
+ local_user::{LocalUser, LocalUserForm},
+ },
+ traits::Crud,
+};
+use lemmy_db_views::local_user_view::LocalUserView;
+use lemmy_utils::LemmyError;
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for VerifyEmail {
+ type Response = VerifyEmailResponse;
+
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<usize>,
+ ) -> Result<Self::Response, LemmyError> {
+ let token = self.token.clone();
+ let verification = blocking(context.pool(), move |conn| {
+ EmailVerification::read_for_token(conn, &token)
+ })
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "token_not_found"))?;
+
+ let form = LocalUserForm {
+ // necessary in case this is a new signup
+ email_verified: Some(true),
+ // necessary in case email of an existing user was changed
+ email: Some(Some(verification.email)),
+ ..LocalUserForm::default()
+ };
+ let local_user_id = verification.local_user_id;
+ blocking(context.pool(), move |conn| {
+ LocalUser::update(conn, local_user_id, &form)
+ })
+ .await??;
+
+ let local_user_view = blocking(context.pool(), move |conn| {
+ LocalUserView::read(conn, local_user_id)
+ })
+ .await??;
+
+ send_email_verification_success(&local_user_view, &context.settings())?;
+
+ blocking(context.pool(), move |conn| {
+ EmailVerification::delete_old_tokens_for_local_user(conn, local_user_id)
+ })
+ .await??;
+
+ Ok(VerifyEmailResponse {})
+ }
+}
+++ /dev/null
-use crate::Perform;
-use actix_web::web::Data;
-use lemmy_api_common::{
- blocking,
- check_community_ban,
- check_community_deleted_or_removed,
- check_downvotes_enabled,
- get_local_user_view_from_jwt,
- is_mod_or_admin,
- mark_post_as_read,
- mark_post_as_unread,
- post::*,
-};
-use lemmy_apub::{
- fetcher::post_or_comment::PostOrComment,
- objects::post::ApubPost,
- protocol::activities::{
- create_or_update::post::CreateOrUpdatePost,
- voting::{
- undo_vote::UndoVote,
- vote::{Vote, VoteType},
- },
- CreateOrUpdateType,
- },
-};
-use lemmy_db_schema::{
- source::{moderator::*, post::*},
- traits::{Crud, Likeable, Saveable},
-};
-use lemmy_db_views::post_view::PostView;
-use lemmy_utils::{request::fetch_site_metadata, ConnectionId, LemmyError};
-use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperation};
-use std::convert::TryInto;
-
-#[async_trait::async_trait(?Send)]
-impl Perform for CreatePostLike {
- type Response = PostResponse;
-
- #[tracing::instrument(skip(context, websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- websocket_id: Option<ConnectionId>,
- ) -> Result<PostResponse, LemmyError> {
- let data: &CreatePostLike = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- // Don't do a downvote if site has downvotes disabled
- check_downvotes_enabled(data.score, context.pool()).await?;
-
- // Check for a community ban
- let post_id = data.post_id;
- let post: ApubPost = blocking(context.pool(), move |conn| Post::read(conn, post_id))
- .await??
- .into();
-
- check_community_ban(local_user_view.person.id, post.community_id, context.pool()).await?;
- check_community_deleted_or_removed(post.community_id, context.pool()).await?;
-
- let like_form = PostLikeForm {
- post_id: data.post_id,
- person_id: local_user_view.person.id,
- score: data.score,
- };
-
- // Remove any likes first
- let person_id = local_user_view.person.id;
- blocking(context.pool(), move |conn| {
- PostLike::remove(conn, person_id, post_id)
- })
- .await??;
-
- let community_id = post.community_id;
- let object = PostOrComment::Post(Box::new(post));
-
- // 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 {
- let like_form2 = like_form.clone();
- let like = move |conn: &'_ _| PostLike::like(conn, &like_form2);
- blocking(context.pool(), like)
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "couldnt_like_post"))?;
-
- Vote::send(
- &object,
- &local_user_view.person.clone().into(),
- community_id,
- like_form.score.try_into()?,
- context,
- )
- .await?;
- } else {
- // API doesn't distinguish between Undo/Like and Undo/Dislike
- UndoVote::send(
- &object,
- &local_user_view.person.clone().into(),
- community_id,
- VoteType::Like,
- context,
- )
- .await?;
- }
-
- // Mark the post as read
- mark_post_as_read(person_id, post_id, context.pool()).await?;
-
- send_post_ws_message(
- data.post_id,
- UserOperation::CreatePostLike,
- websocket_id,
- Some(local_user_view.person.id),
- context,
- )
- .await
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for MarkPostAsRead {
- type Response = PostResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<Self::Response, LemmyError> {
- let data = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- let post_id = data.post_id;
- let person_id = local_user_view.person.id;
-
- // Mark the post as read / unread
- if data.read {
- mark_post_as_read(person_id, post_id, context.pool()).await?;
- } else {
- mark_post_as_unread(person_id, post_id, context.pool()).await?;
- }
-
- // Fetch it
- let post_view = blocking(context.pool(), move |conn| {
- PostView::read(conn, post_id, Some(person_id))
- })
- .await??;
-
- let res = Self::Response { post_view };
-
- Ok(res)
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for LockPost {
- type Response = PostResponse;
-
- #[tracing::instrument(skip(context, websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- websocket_id: Option<ConnectionId>,
- ) -> Result<PostResponse, LemmyError> {
- let data: &LockPost = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- let post_id = data.post_id;
- let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
-
- check_community_ban(
- local_user_view.person.id,
- orig_post.community_id,
- context.pool(),
- )
- .await?;
- check_community_deleted_or_removed(orig_post.community_id, context.pool()).await?;
-
- // Verify that only the mods can lock
- is_mod_or_admin(
- context.pool(),
- local_user_view.person.id,
- orig_post.community_id,
- )
- .await?;
-
- // Update the post
- let post_id = data.post_id;
- let locked = data.locked;
- let updated_post: ApubPost = blocking(context.pool(), move |conn| {
- Post::update_locked(conn, post_id, locked)
- })
- .await??
- .into();
-
- // Mod tables
- let form = ModLockPostForm {
- mod_person_id: local_user_view.person.id,
- post_id: data.post_id,
- locked: Some(locked),
- };
- blocking(context.pool(), move |conn| ModLockPost::create(conn, &form)).await??;
-
- // apub updates
- CreateOrUpdatePost::send(
- updated_post,
- &local_user_view.person.clone().into(),
- CreateOrUpdateType::Update,
- context,
- )
- .await?;
-
- send_post_ws_message(
- data.post_id,
- UserOperation::LockPost,
- websocket_id,
- Some(local_user_view.person.id),
- context,
- )
- .await
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for StickyPost {
- type Response = PostResponse;
-
- #[tracing::instrument(skip(context, websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- websocket_id: Option<ConnectionId>,
- ) -> Result<PostResponse, LemmyError> {
- let data: &StickyPost = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- let post_id = data.post_id;
- let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
-
- check_community_ban(
- local_user_view.person.id,
- orig_post.community_id,
- context.pool(),
- )
- .await?;
- check_community_deleted_or_removed(orig_post.community_id, context.pool()).await?;
-
- // Verify that only the mods can sticky
- is_mod_or_admin(
- context.pool(),
- local_user_view.person.id,
- orig_post.community_id,
- )
- .await?;
-
- // Update the post
- let post_id = data.post_id;
- let stickied = data.stickied;
- let updated_post: ApubPost = blocking(context.pool(), move |conn| {
- Post::update_stickied(conn, post_id, stickied)
- })
- .await??
- .into();
-
- // Mod tables
- let form = ModStickyPostForm {
- mod_person_id: local_user_view.person.id,
- post_id: data.post_id,
- stickied: Some(stickied),
- };
- blocking(context.pool(), move |conn| {
- ModStickyPost::create(conn, &form)
- })
- .await??;
-
- // Apub updates
- // TODO stickied should pry work like locked for ease of use
- CreateOrUpdatePost::send(
- updated_post,
- &local_user_view.person.clone().into(),
- CreateOrUpdateType::Update,
- context,
- )
- .await?;
-
- send_post_ws_message(
- data.post_id,
- UserOperation::StickyPost,
- websocket_id,
- Some(local_user_view.person.id),
- context,
- )
- .await
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for SavePost {
- type Response = PostResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<PostResponse, LemmyError> {
- let data: &SavePost = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- let post_saved_form = PostSavedForm {
- post_id: data.post_id,
- person_id: local_user_view.person.id,
- };
-
- if data.save {
- let save = move |conn: &'_ _| PostSaved::save(conn, &post_saved_form);
- blocking(context.pool(), save)
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "couldnt_save_post"))?;
- } else {
- let unsave = move |conn: &'_ _| PostSaved::unsave(conn, &post_saved_form);
- blocking(context.pool(), unsave)
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "couldnt_save_post"))?;
- }
-
- let post_id = data.post_id;
- let person_id = local_user_view.person.id;
- let post_view = blocking(context.pool(), move |conn| {
- PostView::read(conn, post_id, Some(person_id))
- })
- .await??;
-
- // Mark the post as read
- mark_post_as_read(person_id, post_id, context.pool()).await?;
-
- Ok(PostResponse { post_view })
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for GetSiteMetadata {
- type Response = GetSiteMetadataResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<GetSiteMetadataResponse, LemmyError> {
- let data: &Self = self;
-
- let metadata = fetch_site_metadata(context.client(), &data.url).await?;
-
- Ok(GetSiteMetadataResponse { metadata })
- }
-}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::post::{GetSiteMetadata, GetSiteMetadataResponse};
+use lemmy_utils::{request::fetch_site_metadata, ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for GetSiteMetadata {
+ type Response = GetSiteMetadataResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<GetSiteMetadataResponse, LemmyError> {
+ let data: &Self = self;
+
+ let metadata = fetch_site_metadata(context.client(), &data.url).await?;
+
+ Ok(GetSiteMetadataResponse { metadata })
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ check_community_ban,
+ check_community_deleted_or_removed,
+ check_downvotes_enabled,
+ get_local_user_view_from_jwt,
+ mark_post_as_read,
+ post::{CreatePostLike, PostResponse},
+};
+use lemmy_apub::{
+ fetcher::post_or_comment::PostOrComment,
+ objects::post::ApubPost,
+ protocol::activities::voting::{
+ undo_vote::UndoVote,
+ vote::{Vote, VoteType},
+ },
+};
+use lemmy_db_schema::{
+ source::post::{Post, PostLike, PostLikeForm},
+ traits::{Crud, Likeable},
+};
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperation};
+
+#[async_trait::async_trait(?Send)]
+impl Perform for CreatePostLike {
+ type Response = PostResponse;
+
+ #[tracing::instrument(skip(context, websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ websocket_id: Option<ConnectionId>,
+ ) -> Result<PostResponse, LemmyError> {
+ let data: &CreatePostLike = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ // Don't do a downvote if site has downvotes disabled
+ check_downvotes_enabled(data.score, context.pool()).await?;
+
+ // Check for a community ban
+ let post_id = data.post_id;
+ let post: ApubPost = blocking(context.pool(), move |conn| Post::read(conn, post_id))
+ .await??
+ .into();
+
+ check_community_ban(local_user_view.person.id, post.community_id, context.pool()).await?;
+ check_community_deleted_or_removed(post.community_id, context.pool()).await?;
+
+ let like_form = PostLikeForm {
+ post_id: data.post_id,
+ person_id: local_user_view.person.id,
+ score: data.score,
+ };
+
+ // Remove any likes first
+ let person_id = local_user_view.person.id;
+ blocking(context.pool(), move |conn| {
+ PostLike::remove(conn, person_id, post_id)
+ })
+ .await??;
+
+ let community_id = post.community_id;
+ let object = PostOrComment::Post(Box::new(post));
+
+ // 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 {
+ let like_form2 = like_form.clone();
+ let like = move |conn: &'_ _| PostLike::like(conn, &like_form2);
+ blocking(context.pool(), like)
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_like_post"))?;
+
+ Vote::send(
+ &object,
+ &local_user_view.person.clone().into(),
+ community_id,
+ like_form.score.try_into()?,
+ context,
+ )
+ .await?;
+ } else {
+ // API doesn't distinguish between Undo/Like and Undo/Dislike
+ UndoVote::send(
+ &object,
+ &local_user_view.person.clone().into(),
+ community_id,
+ VoteType::Like,
+ context,
+ )
+ .await?;
+ }
+
+ // Mark the post as read
+ mark_post_as_read(person_id, post_id, context.pool()).await?;
+
+ send_post_ws_message(
+ data.post_id,
+ UserOperation::CreatePostLike,
+ websocket_id,
+ Some(local_user_view.person.id),
+ context,
+ )
+ .await
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ check_community_ban,
+ check_community_deleted_or_removed,
+ get_local_user_view_from_jwt,
+ is_mod_or_admin,
+ post::{LockPost, PostResponse},
+};
+use lemmy_apub::{
+ objects::post::ApubPost,
+ protocol::activities::{create_or_update::post::CreateOrUpdatePost, CreateOrUpdateType},
+};
+use lemmy_db_schema::{
+ source::{
+ moderator::{ModLockPost, ModLockPostForm},
+ post::Post,
+ },
+ traits::Crud,
+};
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperation};
+
+#[async_trait::async_trait(?Send)]
+impl Perform for LockPost {
+ type Response = PostResponse;
+
+ #[tracing::instrument(skip(context, websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ websocket_id: Option<ConnectionId>,
+ ) -> Result<PostResponse, LemmyError> {
+ let data: &LockPost = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ let post_id = data.post_id;
+ let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
+
+ check_community_ban(
+ local_user_view.person.id,
+ orig_post.community_id,
+ context.pool(),
+ )
+ .await?;
+ check_community_deleted_or_removed(orig_post.community_id, context.pool()).await?;
+
+ // Verify that only the mods can lock
+ is_mod_or_admin(
+ context.pool(),
+ local_user_view.person.id,
+ orig_post.community_id,
+ )
+ .await?;
+
+ // Update the post
+ let post_id = data.post_id;
+ let locked = data.locked;
+ let updated_post: ApubPost = blocking(context.pool(), move |conn| {
+ Post::update_locked(conn, post_id, locked)
+ })
+ .await??
+ .into();
+
+ // Mod tables
+ let form = ModLockPostForm {
+ mod_person_id: local_user_view.person.id,
+ post_id: data.post_id,
+ locked: Some(locked),
+ };
+ blocking(context.pool(), move |conn| ModLockPost::create(conn, &form)).await??;
+
+ // apub updates
+ CreateOrUpdatePost::send(
+ updated_post,
+ &local_user_view.person.clone().into(),
+ CreateOrUpdateType::Update,
+ context,
+ )
+ .await?;
+
+ send_post_ws_message(
+ data.post_id,
+ UserOperation::LockPost,
+ websocket_id,
+ Some(local_user_view.person.id),
+ context,
+ )
+ .await
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ get_local_user_view_from_jwt,
+ mark_post_as_read,
+ mark_post_as_unread,
+ post::{MarkPostAsRead, PostResponse},
+};
+use lemmy_db_views::post_view::PostView;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for MarkPostAsRead {
+ type Response = PostResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<Self::Response, LemmyError> {
+ let data = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ let post_id = data.post_id;
+ let person_id = local_user_view.person.id;
+
+ // Mark the post as read / unread
+ if data.read {
+ mark_post_as_read(person_id, post_id, context.pool()).await?;
+ } else {
+ mark_post_as_unread(person_id, post_id, context.pool()).await?;
+ }
+
+ // Fetch it
+ let post_view = blocking(context.pool(), move |conn| {
+ PostView::read(conn, post_id, Some(person_id))
+ })
+ .await??;
+
+ let res = Self::Response { post_view };
+
+ Ok(res)
+ }
+}
--- /dev/null
+mod get_link_metadata;
+mod like;
+mod lock;
+mod mark_read;
+mod save;
+mod sticky;
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ get_local_user_view_from_jwt,
+ mark_post_as_read,
+ post::{PostResponse, SavePost},
+};
+use lemmy_db_schema::{
+ source::post::{PostSaved, PostSavedForm},
+ traits::Saveable,
+};
+use lemmy_db_views::post_view::PostView;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for SavePost {
+ type Response = PostResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<PostResponse, LemmyError> {
+ let data: &SavePost = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ let post_saved_form = PostSavedForm {
+ post_id: data.post_id,
+ person_id: local_user_view.person.id,
+ };
+
+ if data.save {
+ let save = move |conn: &'_ _| PostSaved::save(conn, &post_saved_form);
+ blocking(context.pool(), save)
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_save_post"))?;
+ } else {
+ let unsave = move |conn: &'_ _| PostSaved::unsave(conn, &post_saved_form);
+ blocking(context.pool(), unsave)
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_save_post"))?;
+ }
+
+ let post_id = data.post_id;
+ let person_id = local_user_view.person.id;
+ let post_view = blocking(context.pool(), move |conn| {
+ PostView::read(conn, post_id, Some(person_id))
+ })
+ .await??;
+
+ // Mark the post as read
+ mark_post_as_read(person_id, post_id, context.pool()).await?;
+
+ Ok(PostResponse { post_view })
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ check_community_ban,
+ check_community_deleted_or_removed,
+ get_local_user_view_from_jwt,
+ is_mod_or_admin,
+ post::{PostResponse, StickyPost},
+};
+use lemmy_apub::{
+ objects::post::ApubPost,
+ protocol::activities::{create_or_update::post::CreateOrUpdatePost, CreateOrUpdateType},
+};
+use lemmy_db_schema::{
+ source::{
+ moderator::{ModStickyPost, ModStickyPostForm},
+ post::Post,
+ },
+ traits::Crud,
+};
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperation};
+
+#[async_trait::async_trait(?Send)]
+impl Perform for StickyPost {
+ type Response = PostResponse;
+
+ #[tracing::instrument(skip(context, websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ websocket_id: Option<ConnectionId>,
+ ) -> Result<PostResponse, LemmyError> {
+ let data: &StickyPost = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ let post_id = data.post_id;
+ let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
+
+ check_community_ban(
+ local_user_view.person.id,
+ orig_post.community_id,
+ context.pool(),
+ )
+ .await?;
+ check_community_deleted_or_removed(orig_post.community_id, context.pool()).await?;
+
+ // Verify that only the mods can sticky
+ is_mod_or_admin(
+ context.pool(),
+ local_user_view.person.id,
+ orig_post.community_id,
+ )
+ .await?;
+
+ // Update the post
+ let post_id = data.post_id;
+ let stickied = data.stickied;
+ let updated_post: ApubPost = blocking(context.pool(), move |conn| {
+ Post::update_stickied(conn, post_id, stickied)
+ })
+ .await??
+ .into();
+
+ // Mod tables
+ let form = ModStickyPostForm {
+ mod_person_id: local_user_view.person.id,
+ post_id: data.post_id,
+ stickied: Some(stickied),
+ };
+ blocking(context.pool(), move |conn| {
+ ModStickyPost::create(conn, &form)
+ })
+ .await??;
+
+ // Apub updates
+ // TODO stickied should pry work like locked for ease of use
+ CreateOrUpdatePost::send(
+ updated_post,
+ &local_user_view.person.clone().into(),
+ CreateOrUpdateType::Update,
+ context,
+ )
+ .await?;
+
+ send_post_ws_message(
+ data.post_id,
+ UserOperation::StickyPost,
+ websocket_id,
+ Some(local_user_view.person.id),
+ context,
+ )
+ .await
+ }
+}
+++ /dev/null
-use crate::Perform;
-use actix_web::web::Data;
-use lemmy_api_common::{
- blocking,
- check_community_ban,
- get_local_user_view_from_jwt,
- is_mod_or_admin,
- post::{
- CreatePostReport,
- ListPostReports,
- ListPostReportsResponse,
- PostReportResponse,
- ResolvePostReport,
- },
-};
-use lemmy_apub::protocol::activities::community::report::Report;
-use lemmy_apub_lib::object_id::ObjectId;
-use lemmy_db_schema::{
- source::post_report::{PostReport, PostReportForm},
- traits::Reportable,
-};
-use lemmy_db_views::{
- post_report_view::{PostReportQueryBuilder, PostReportView},
- post_view::PostView,
-};
-use lemmy_utils::{ConnectionId, LemmyError};
-use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
-
-/// Creates a post report and notifies the moderators of the community
-#[async_trait::async_trait(?Send)]
-impl Perform for CreatePostReport {
- type Response = PostReportResponse;
-
- #[tracing::instrument(skip(context, websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- websocket_id: Option<ConnectionId>,
- ) -> Result<PostReportResponse, LemmyError> {
- let data: &CreatePostReport = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- // check size of report and check for whitespace
- let reason = data.reason.trim();
- if reason.is_empty() {
- return Err(LemmyError::from_message("report_reason_required"));
- }
- if reason.chars().count() > 1000 {
- return Err(LemmyError::from_message("report_too_long"));
- }
-
- let person_id = local_user_view.person.id;
- let post_id = data.post_id;
- let post_view = blocking(context.pool(), move |conn| {
- PostView::read(conn, post_id, None)
- })
- .await??;
-
- check_community_ban(person_id, post_view.community.id, context.pool()).await?;
-
- let report_form = PostReportForm {
- creator_id: person_id,
- post_id,
- original_post_name: post_view.post.name,
- original_post_url: post_view.post.url,
- original_post_body: post_view.post.body,
- reason: data.reason.to_owned(),
- };
-
- let report = blocking(context.pool(), move |conn| {
- PostReport::report(conn, &report_form)
- })
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "couldnt_create_report"))?;
-
- let post_report_view = blocking(context.pool(), move |conn| {
- PostReportView::read(conn, report.id, person_id)
- })
- .await??;
-
- let res = PostReportResponse { post_report_view };
-
- context.chat_server().do_send(SendModRoomMessage {
- op: UserOperation::CreatePostReport,
- response: res.clone(),
- community_id: post_view.community.id,
- websocket_id,
- });
-
- Report::send(
- ObjectId::new(post_view.post.ap_id),
- &local_user_view.person.into(),
- ObjectId::new(post_view.community.actor_id),
- reason.to_string(),
- context,
- )
- .await?;
-
- Ok(res)
- }
-}
-
-/// Resolves or unresolves a post report and notifies the moderators of the community
-#[async_trait::async_trait(?Send)]
-impl Perform for ResolvePostReport {
- type Response = PostReportResponse;
-
- #[tracing::instrument(skip(context, websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- websocket_id: Option<ConnectionId>,
- ) -> Result<PostReportResponse, LemmyError> {
- let data: &ResolvePostReport = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- let report_id = data.report_id;
- let person_id = local_user_view.person.id;
- let report = blocking(context.pool(), move |conn| {
- PostReportView::read(conn, report_id, person_id)
- })
- .await??;
-
- let person_id = local_user_view.person.id;
- is_mod_or_admin(context.pool(), person_id, report.community.id).await?;
-
- let resolved = data.resolved;
- let resolve_fun = move |conn: &'_ _| {
- if resolved {
- PostReport::resolve(conn, report_id, person_id)
- } else {
- PostReport::unresolve(conn, report_id, person_id)
- }
- };
-
- blocking(context.pool(), resolve_fun)
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "couldnt_resolve_report"))?;
-
- let post_report_view = blocking(context.pool(), move |conn| {
- PostReportView::read(conn, report_id, person_id)
- })
- .await??;
-
- let res = PostReportResponse { post_report_view };
-
- context.chat_server().do_send(SendModRoomMessage {
- op: UserOperation::ResolvePostReport,
- response: res.clone(),
- community_id: report.community.id,
- websocket_id,
- });
-
- Ok(res)
- }
-}
-
-/// Lists post reports for a community if an id is supplied
-/// or returns all post reports for communities a user moderates
-#[async_trait::async_trait(?Send)]
-impl Perform for ListPostReports {
- type Response = ListPostReportsResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<ListPostReportsResponse, LemmyError> {
- let data: &ListPostReports = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- let person_id = local_user_view.person.id;
- let admin = local_user_view.person.admin;
- let community_id = data.community_id;
- let unresolved_only = data.unresolved_only;
-
- let page = data.page;
- let limit = data.limit;
- let post_reports = blocking(context.pool(), move |conn| {
- PostReportQueryBuilder::create(conn, person_id, admin)
- .community_id(community_id)
- .unresolved_only(unresolved_only)
- .page(page)
- .limit(limit)
- .list()
- })
- .await??;
-
- let res = ListPostReportsResponse { post_reports };
-
- Ok(res)
- }
-}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ check_community_ban,
+ get_local_user_view_from_jwt,
+ post::{CreatePostReport, PostReportResponse},
+};
+use lemmy_apub::protocol::activities::community::report::Report;
+use lemmy_apub_lib::object_id::ObjectId;
+use lemmy_db_schema::{
+ source::post_report::{PostReport, PostReportForm},
+ traits::Reportable,
+};
+use lemmy_db_views::{post_report_view::PostReportView, post_view::PostView};
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
+
+/// Creates a post report and notifies the moderators of the community
+#[async_trait::async_trait(?Send)]
+impl Perform for CreatePostReport {
+ type Response = PostReportResponse;
+
+ #[tracing::instrument(skip(context, websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ websocket_id: Option<ConnectionId>,
+ ) -> Result<PostReportResponse, LemmyError> {
+ let data: &CreatePostReport = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ // check size of report and check for whitespace
+ let reason = data.reason.trim();
+ if reason.is_empty() {
+ return Err(LemmyError::from_message("report_reason_required"));
+ }
+ if reason.chars().count() > 1000 {
+ return Err(LemmyError::from_message("report_too_long"));
+ }
+
+ let person_id = local_user_view.person.id;
+ let post_id = data.post_id;
+ let post_view = blocking(context.pool(), move |conn| {
+ PostView::read(conn, post_id, None)
+ })
+ .await??;
+
+ check_community_ban(person_id, post_view.community.id, context.pool()).await?;
+
+ let report_form = PostReportForm {
+ creator_id: person_id,
+ post_id,
+ original_post_name: post_view.post.name,
+ original_post_url: post_view.post.url,
+ original_post_body: post_view.post.body,
+ reason: data.reason.to_owned(),
+ };
+
+ let report = blocking(context.pool(), move |conn| {
+ PostReport::report(conn, &report_form)
+ })
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_create_report"))?;
+
+ let post_report_view = blocking(context.pool(), move |conn| {
+ PostReportView::read(conn, report.id, person_id)
+ })
+ .await??;
+
+ let res = PostReportResponse { post_report_view };
+
+ context.chat_server().do_send(SendModRoomMessage {
+ op: UserOperation::CreatePostReport,
+ response: res.clone(),
+ community_id: post_view.community.id,
+ websocket_id,
+ });
+
+ Report::send(
+ ObjectId::new(post_view.post.ap_id),
+ &local_user_view.person.into(),
+ ObjectId::new(post_view.community.actor_id),
+ reason.to_string(),
+ context,
+ )
+ .await?;
+
+ Ok(res)
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ get_local_user_view_from_jwt,
+ post::{ListPostReports, ListPostReportsResponse},
+};
+use lemmy_db_views::post_report_view::PostReportQueryBuilder;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+/// Lists post reports for a community if an id is supplied
+/// or returns all post reports for communities a user moderates
+#[async_trait::async_trait(?Send)]
+impl Perform for ListPostReports {
+ type Response = ListPostReportsResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<ListPostReportsResponse, LemmyError> {
+ let data: &ListPostReports = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ let person_id = local_user_view.person.id;
+ let admin = local_user_view.person.admin;
+ let community_id = data.community_id;
+ let unresolved_only = data.unresolved_only;
+
+ let page = data.page;
+ let limit = data.limit;
+ let post_reports = blocking(context.pool(), move |conn| {
+ PostReportQueryBuilder::create(conn, person_id, admin)
+ .community_id(community_id)
+ .unresolved_only(unresolved_only)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
+
+ let res = ListPostReportsResponse { post_reports };
+
+ Ok(res)
+ }
+}
--- /dev/null
+mod create;
+mod list;
+mod resolve;
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ get_local_user_view_from_jwt,
+ is_mod_or_admin,
+ post::{PostReportResponse, ResolvePostReport},
+};
+use lemmy_db_schema::{source::post_report::PostReport, traits::Reportable};
+use lemmy_db_views::post_report_view::PostReportView;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
+
+/// Resolves or unresolves a post report and notifies the moderators of the community
+#[async_trait::async_trait(?Send)]
+impl Perform for ResolvePostReport {
+ type Response = PostReportResponse;
+
+ #[tracing::instrument(skip(context, websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ websocket_id: Option<ConnectionId>,
+ ) -> Result<PostReportResponse, LemmyError> {
+ let data: &ResolvePostReport = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ let report_id = data.report_id;
+ let person_id = local_user_view.person.id;
+ let report = blocking(context.pool(), move |conn| {
+ PostReportView::read(conn, report_id, person_id)
+ })
+ .await??;
+
+ let person_id = local_user_view.person.id;
+ is_mod_or_admin(context.pool(), person_id, report.community.id).await?;
+
+ let resolved = data.resolved;
+ let resolve_fun = move |conn: &'_ _| {
+ if resolved {
+ PostReport::resolve(conn, report_id, person_id)
+ } else {
+ PostReport::unresolve(conn, report_id, person_id)
+ }
+ };
+
+ blocking(context.pool(), resolve_fun)
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_resolve_report"))?;
+
+ let post_report_view = blocking(context.pool(), move |conn| {
+ PostReportView::read(conn, report_id, person_id)
+ })
+ .await??;
+
+ let res = PostReportResponse { post_report_view };
+
+ context.chat_server().do_send(SendModRoomMessage {
+ op: UserOperation::ResolvePostReport,
+ response: res.clone(),
+ community_id: report.community.id,
+ websocket_id,
+ });
+
+ Ok(res)
+ }
+}
--- /dev/null
+mod mark_read;
+++ /dev/null
-use crate::Perform;
-use actix_web::web::Data;
-use diesel::NotFound;
-use lemmy_api_common::{
- blocking,
- build_federated_instances,
- check_private_instance,
- get_local_user_view_from_jwt,
- get_local_user_view_from_jwt_opt,
- is_admin,
- send_application_approved_email,
- site::*,
-};
-use lemmy_apub::{
- fetcher::{
- resolve_actor_identifier,
- search::{search_by_apub_id, SearchableObjects},
- },
- objects::community::ApubCommunity,
-};
-use lemmy_db_schema::{
- diesel_option_overwrite,
- from_opt_str_to_opt_enum,
- newtypes::PersonId,
- source::{
- community::Community,
- local_user::{LocalUser, LocalUserForm},
- moderator::*,
- person::Person,
- registration_application::{RegistrationApplication, RegistrationApplicationForm},
- site::Site,
- },
- traits::{Crud, DeleteableOrRemoveable},
- DbPool,
- ListingType,
- SearchType,
- SortType,
-};
-use lemmy_db_views::{
- comment_view::{CommentQueryBuilder, CommentView},
- local_user_view::LocalUserView,
- post_view::{PostQueryBuilder, PostView},
- registration_application_view::{
- RegistrationApplicationQueryBuilder,
- RegistrationApplicationView,
- },
- site_view::SiteView,
-};
-use lemmy_db_views_actor::{
- community_view::{CommunityQueryBuilder, CommunityView},
- person_view::{PersonQueryBuilder, PersonViewSafe},
-};
-use lemmy_db_views_moderator::{
- mod_add_community_view::ModAddCommunityView,
- mod_add_view::ModAddView,
- mod_ban_from_community_view::ModBanFromCommunityView,
- mod_ban_view::ModBanView,
- mod_hide_community_view::ModHideCommunityView,
- mod_lock_post_view::ModLockPostView,
- mod_remove_comment_view::ModRemoveCommentView,
- mod_remove_community_view::ModRemoveCommunityView,
- mod_remove_post_view::ModRemovePostView,
- mod_sticky_post_view::ModStickyPostView,
- mod_transfer_community_view::ModTransferCommunityView,
-};
-use lemmy_utils::{settings::structs::Settings, version, ConnectionId, LemmyError};
-use lemmy_websocket::LemmyContext;
-
-#[async_trait::async_trait(?Send)]
-impl Perform for GetModlog {
- type Response = GetModlogResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<GetModlogResponse, LemmyError> {
- let data: &GetModlog = self;
-
- let local_user_view =
- get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
- .await?;
-
- check_private_instance(&local_user_view, context.pool()).await?;
-
- let community_id = data.community_id;
- let mod_person_id = data.mod_person_id;
- let page = data.page;
- let limit = data.limit;
- let removed_posts = blocking(context.pool(), move |conn| {
- ModRemovePostView::list(conn, community_id, mod_person_id, page, limit)
- })
- .await??;
-
- let locked_posts = blocking(context.pool(), move |conn| {
- ModLockPostView::list(conn, community_id, mod_person_id, page, limit)
- })
- .await??;
-
- let stickied_posts = blocking(context.pool(), move |conn| {
- ModStickyPostView::list(conn, community_id, mod_person_id, page, limit)
- })
- .await??;
-
- let removed_comments = blocking(context.pool(), move |conn| {
- ModRemoveCommentView::list(conn, community_id, mod_person_id, page, limit)
- })
- .await??;
-
- let banned_from_community = blocking(context.pool(), move |conn| {
- ModBanFromCommunityView::list(conn, community_id, mod_person_id, page, limit)
- })
- .await??;
-
- let added_to_community = blocking(context.pool(), move |conn| {
- ModAddCommunityView::list(conn, community_id, mod_person_id, page, limit)
- })
- .await??;
-
- let transferred_to_community = blocking(context.pool(), move |conn| {
- ModTransferCommunityView::list(conn, community_id, mod_person_id, page, limit)
- })
- .await??;
-
- let hidden_communities = blocking(context.pool(), move |conn| {
- ModHideCommunityView::list(conn, community_id, mod_person_id, page, limit)
- })
- .await??;
-
- // These arrays are only for the full modlog, when a community isn't given
- let (removed_communities, banned, added) = if data.community_id.is_none() {
- blocking(context.pool(), move |conn| {
- Ok((
- ModRemoveCommunityView::list(conn, mod_person_id, page, limit)?,
- ModBanView::list(conn, mod_person_id, page, limit)?,
- ModAddView::list(conn, mod_person_id, page, limit)?,
- )) as Result<_, LemmyError>
- })
- .await??
- } else {
- (Vec::new(), Vec::new(), Vec::new())
- };
-
- // Return the jwt
- Ok(GetModlogResponse {
- removed_posts,
- locked_posts,
- stickied_posts,
- removed_comments,
- removed_communities,
- banned_from_community,
- banned,
- added_to_community,
- added,
- transferred_to_community,
- hidden_communities,
- })
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for Search {
- type Response = SearchResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<SearchResponse, LemmyError> {
- let data: &Search = self;
-
- let local_user_view =
- get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
- .await?;
-
- check_private_instance(&local_user_view, context.pool()).await?;
-
- let show_nsfw = local_user_view.as_ref().map(|t| t.local_user.show_nsfw);
- let show_bot_accounts = local_user_view
- .as_ref()
- .map(|t| t.local_user.show_bot_accounts);
- let show_read_posts = local_user_view
- .as_ref()
- .map(|t| t.local_user.show_read_posts);
-
- let person_id = local_user_view.map(|u| u.person.id);
-
- let mut posts = Vec::new();
- let mut comments = Vec::new();
- let mut communities = Vec::new();
- let mut users = Vec::new();
-
- // TODO no clean / non-nsfw searching rn
-
- let q = data.q.to_owned();
- let page = data.page;
- let limit = data.limit;
- let sort: Option<SortType> = from_opt_str_to_opt_enum(&data.sort);
- let listing_type: Option<ListingType> = from_opt_str_to_opt_enum(&data.listing_type);
- let search_type: SearchType = from_opt_str_to_opt_enum(&data.type_).unwrap_or(SearchType::All);
- let community_id = data.community_id;
- let community_actor_id = if let Some(name) = &data.community_name {
- resolve_actor_identifier::<ApubCommunity, Community>(name, context)
- .await
- .ok()
- .map(|c| c.actor_id)
- } else {
- None
- };
- let creator_id = data.creator_id;
- match search_type {
- SearchType::Posts => {
- posts = blocking(context.pool(), move |conn| {
- PostQueryBuilder::create(conn)
- .sort(sort)
- .show_nsfw(show_nsfw)
- .show_bot_accounts(show_bot_accounts)
- .show_read_posts(show_read_posts)
- .listing_type(listing_type)
- .community_id(community_id)
- .community_actor_id(community_actor_id)
- .creator_id(creator_id)
- .my_person_id(person_id)
- .search_term(q)
- .page(page)
- .limit(limit)
- .list()
- })
- .await??;
- }
- SearchType::Comments => {
- comments = blocking(context.pool(), move |conn| {
- CommentQueryBuilder::create(conn)
- .sort(sort)
- .listing_type(listing_type)
- .search_term(q)
- .show_bot_accounts(show_bot_accounts)
- .community_id(community_id)
- .community_actor_id(community_actor_id)
- .creator_id(creator_id)
- .my_person_id(person_id)
- .page(page)
- .limit(limit)
- .list()
- })
- .await??;
- }
- SearchType::Communities => {
- communities = blocking(context.pool(), move |conn| {
- CommunityQueryBuilder::create(conn)
- .sort(sort)
- .listing_type(listing_type)
- .search_term(q)
- .my_person_id(person_id)
- .page(page)
- .limit(limit)
- .list()
- })
- .await??;
- }
- SearchType::Users => {
- users = blocking(context.pool(), move |conn| {
- PersonQueryBuilder::create(conn)
- .sort(sort)
- .search_term(q)
- .page(page)
- .limit(limit)
- .list()
- })
- .await??;
- }
- SearchType::All => {
- // If the community or creator is included, dont search communities or users
- let community_or_creator_included =
- data.community_id.is_some() || data.community_name.is_some() || data.creator_id.is_some();
- let community_actor_id_2 = community_actor_id.to_owned();
-
- posts = blocking(context.pool(), move |conn| {
- PostQueryBuilder::create(conn)
- .sort(sort)
- .show_nsfw(show_nsfw)
- .show_bot_accounts(show_bot_accounts)
- .show_read_posts(show_read_posts)
- .listing_type(listing_type)
- .community_id(community_id)
- .community_actor_id(community_actor_id_2)
- .creator_id(creator_id)
- .my_person_id(person_id)
- .search_term(q)
- .page(page)
- .limit(limit)
- .list()
- })
- .await??;
-
- let q = data.q.to_owned();
- let community_actor_id = community_actor_id.to_owned();
-
- comments = blocking(context.pool(), move |conn| {
- CommentQueryBuilder::create(conn)
- .sort(sort)
- .listing_type(listing_type)
- .search_term(q)
- .show_bot_accounts(show_bot_accounts)
- .community_id(community_id)
- .community_actor_id(community_actor_id)
- .creator_id(creator_id)
- .my_person_id(person_id)
- .page(page)
- .limit(limit)
- .list()
- })
- .await??;
-
- let q = data.q.to_owned();
-
- communities = if community_or_creator_included {
- vec![]
- } else {
- blocking(context.pool(), move |conn| {
- CommunityQueryBuilder::create(conn)
- .sort(sort)
- .listing_type(listing_type)
- .search_term(q)
- .my_person_id(person_id)
- .page(page)
- .limit(limit)
- .list()
- })
- .await??
- };
-
- let q = data.q.to_owned();
-
- users = if community_or_creator_included {
- vec![]
- } else {
- blocking(context.pool(), move |conn| {
- PersonQueryBuilder::create(conn)
- .sort(sort)
- .search_term(q)
- .page(page)
- .limit(limit)
- .list()
- })
- .await??
- };
- }
- SearchType::Url => {
- posts = blocking(context.pool(), move |conn| {
- PostQueryBuilder::create(conn)
- .sort(sort)
- .show_nsfw(show_nsfw)
- .show_bot_accounts(show_bot_accounts)
- .show_read_posts(show_read_posts)
- .listing_type(listing_type)
- .my_person_id(person_id)
- .community_id(community_id)
- .community_actor_id(community_actor_id)
- .creator_id(creator_id)
- .url_search(q)
- .page(page)
- .limit(limit)
- .list()
- })
- .await??;
- }
- };
-
- // Blank out deleted or removed info for non logged in users
- if person_id.is_none() {
- for cv in communities
- .iter_mut()
- .filter(|cv| cv.community.deleted || cv.community.removed)
- {
- cv.community = cv.to_owned().community.blank_out_deleted_or_removed_info();
- }
-
- for pv in posts
- .iter_mut()
- .filter(|p| p.post.deleted || p.post.removed)
- {
- pv.post = pv.to_owned().post.blank_out_deleted_or_removed_info();
- }
-
- for cv in comments
- .iter_mut()
- .filter(|cv| cv.comment.deleted || cv.comment.removed)
- {
- cv.comment = cv.to_owned().comment.blank_out_deleted_or_removed_info();
- }
- }
-
- // Return the jwt
- Ok(SearchResponse {
- type_: search_type.to_string(),
- comments,
- posts,
- communities,
- users,
- })
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for ResolveObject {
- type Response = ResolveObjectResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<ResolveObjectResponse, LemmyError> {
- let local_user_view =
- get_local_user_view_from_jwt_opt(self.auth.as_ref(), context.pool(), context.secret())
- .await?;
- check_private_instance(&local_user_view, context.pool()).await?;
-
- let res = search_by_apub_id(&self.q, context)
- .await
- .map_err(|e| e.with_message("couldnt_find_object"))?;
- convert_response(res, local_user_view.map(|l| l.person.id), context.pool())
- .await
- .map_err(|e| e.with_message("couldnt_find_object"))
- }
-}
-
-async fn convert_response(
- object: SearchableObjects,
- user_id: Option<PersonId>,
- pool: &DbPool,
-) -> Result<ResolveObjectResponse, LemmyError> {
- let removed_or_deleted;
- let mut res = ResolveObjectResponse {
- comment: None,
- post: None,
- community: None,
- person: None,
- };
- use SearchableObjects::*;
- match object {
- Person(p) => {
- removed_or_deleted = p.deleted;
- res.person = Some(blocking(pool, move |conn| PersonViewSafe::read(conn, p.id)).await??)
- }
- Community(c) => {
- removed_or_deleted = c.deleted || c.removed;
- res.community =
- Some(blocking(pool, move |conn| CommunityView::read(conn, c.id, user_id)).await??)
- }
- Post(p) => {
- removed_or_deleted = p.deleted || p.removed;
- res.post = Some(blocking(pool, move |conn| PostView::read(conn, p.id, user_id)).await??)
- }
- Comment(c) => {
- removed_or_deleted = c.deleted || c.removed;
- res.comment = Some(blocking(pool, move |conn| CommentView::read(conn, c.id, user_id)).await??)
- }
- };
- // if the object was deleted from database, dont return it
- if removed_or_deleted {
- return Err(NotFound {}.into());
- }
- Ok(res)
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for LeaveAdmin {
- type Response = GetSiteResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<GetSiteResponse, LemmyError> {
- let data: &LeaveAdmin = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- is_admin(&local_user_view)?;
-
- // Make sure there isn't just one admin (so if one leaves, there will still be one left)
- let admins = blocking(context.pool(), PersonViewSafe::admins).await??;
- if admins.len() == 1 {
- return Err(LemmyError::from_message("cannot_leave_admin"));
- }
-
- let person_id = local_user_view.person.id;
- blocking(context.pool(), move |conn| {
- Person::leave_admin(conn, person_id)
- })
- .await??;
-
- // Mod tables
- let form = ModAddForm {
- mod_person_id: person_id,
- other_person_id: person_id,
- removed: Some(true),
- };
-
- blocking(context.pool(), move |conn| ModAdd::create(conn, &form)).await??;
-
- // Reread site and admins
- let site_view = blocking(context.pool(), SiteView::read_local).await??;
- let admins = blocking(context.pool(), PersonViewSafe::admins).await??;
-
- let federated_instances =
- build_federated_instances(context.pool(), &context.settings()).await?;
-
- Ok(GetSiteResponse {
- site_view: Some(site_view),
- admins,
- online: 0,
- version: version::VERSION.to_string(),
- my_user: None,
- federated_instances,
- })
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for GetSiteConfig {
- type Response = GetSiteConfigResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<GetSiteConfigResponse, LemmyError> {
- let data: &GetSiteConfig = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- // Only let admins read this
- is_admin(&local_user_view)?;
-
- let config_hjson = Settings::read_config_file()?;
-
- Ok(GetSiteConfigResponse { config_hjson })
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for SaveSiteConfig {
- type Response = GetSiteConfigResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<GetSiteConfigResponse, LemmyError> {
- let data: &SaveSiteConfig = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- // Only let admins read this
- is_admin(&local_user_view)?;
-
- // Make sure docker doesn't have :ro at the end of the volume, so its not a read-only filesystem
- let config_hjson = Settings::save_config_file(&data.config_hjson)
- .map_err(|e| e.with_message("couldnt_update_site"))?;
-
- Ok(GetSiteConfigResponse { config_hjson })
- }
-}
-
-/// Lists registration applications, filterable by undenied only.
-#[async_trait::async_trait(?Send)]
-impl Perform for ListRegistrationApplications {
- type Response = ListRegistrationApplicationsResponse;
-
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<Self::Response, LemmyError> {
- let data = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- // Make sure user is an admin
- is_admin(&local_user_view)?;
-
- let unread_only = data.unread_only;
- let verified_email_only = blocking(context.pool(), Site::read_local_site)
- .await??
- .require_email_verification;
-
- let page = data.page;
- let limit = data.limit;
- let registration_applications = blocking(context.pool(), move |conn| {
- RegistrationApplicationQueryBuilder::create(conn)
- .unread_only(unread_only)
- .verified_email_only(verified_email_only)
- .page(page)
- .limit(limit)
- .list()
- })
- .await??;
-
- let res = Self::Response {
- registration_applications,
- };
-
- Ok(res)
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for ApproveRegistrationApplication {
- type Response = RegistrationApplicationResponse;
-
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<Self::Response, LemmyError> {
- let data = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- let app_id = data.id;
-
- // Only let admins do this
- is_admin(&local_user_view)?;
-
- // Update the registration with reason, admin_id
- let deny_reason = diesel_option_overwrite(&data.deny_reason);
- let app_form = RegistrationApplicationForm {
- admin_id: Some(local_user_view.person.id),
- deny_reason,
- ..RegistrationApplicationForm::default()
- };
-
- let registration_application = blocking(context.pool(), move |conn| {
- RegistrationApplication::update(conn, app_id, &app_form)
- })
- .await??;
-
- // Update the local_user row
- let local_user_form = LocalUserForm {
- accepted_application: Some(data.approve),
- ..LocalUserForm::default()
- };
-
- let approved_user_id = registration_application.local_user_id;
- blocking(context.pool(), move |conn| {
- LocalUser::update(conn, approved_user_id, &local_user_form)
- })
- .await??;
-
- if data.approve {
- let approved_local_user_view = blocking(context.pool(), move |conn| {
- LocalUserView::read(conn, approved_user_id)
- })
- .await??;
-
- if approved_local_user_view.local_user.email.is_some() {
- send_application_approved_email(&approved_local_user_view, &context.settings())?;
- }
- }
-
- // Read the view
- let registration_application = blocking(context.pool(), move |conn| {
- RegistrationApplicationView::read(conn, app_id)
- })
- .await??;
-
- Ok(Self::Response {
- registration_application,
- })
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for GetUnreadRegistrationApplicationCount {
- type Response = GetUnreadRegistrationApplicationCountResponse;
-
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<Self::Response, LemmyError> {
- let data = self;
- let local_user_view =
- get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
-
- // Only let admins do this
- is_admin(&local_user_view)?;
-
- let verified_email_only = blocking(context.pool(), Site::read_local_site)
- .await??
- .require_email_verification;
-
- let registration_applications = blocking(context.pool(), move |conn| {
- RegistrationApplicationView::get_unread_count(conn, verified_email_only)
- })
- .await??;
-
- Ok(Self::Response {
- registration_applications,
- })
- }
-}
--- /dev/null
+mod read;
+mod update;
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ get_local_user_view_from_jwt,
+ is_admin,
+ site::{GetSiteConfig, GetSiteConfigResponse},
+};
+use lemmy_utils::{settings::structs::Settings, ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for GetSiteConfig {
+ type Response = GetSiteConfigResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<GetSiteConfigResponse, LemmyError> {
+ let data: &GetSiteConfig = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ // Only let admins read this
+ is_admin(&local_user_view)?;
+
+ let config_hjson = Settings::read_config_file()?;
+
+ Ok(GetSiteConfigResponse { config_hjson })
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ get_local_user_view_from_jwt,
+ is_admin,
+ site::{GetSiteConfigResponse, SaveSiteConfig},
+};
+use lemmy_utils::{settings::structs::Settings, ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for SaveSiteConfig {
+ type Response = GetSiteConfigResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<GetSiteConfigResponse, LemmyError> {
+ let data: &SaveSiteConfig = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ // Only let admins read this
+ is_admin(&local_user_view)?;
+
+ // Make sure docker doesn't have :ro at the end of the volume, so its not a read-only filesystem
+ let config_hjson = Settings::save_config_file(&data.config_hjson)
+ .map_err(|e| e.with_message("couldnt_update_site"))?;
+
+ Ok(GetSiteConfigResponse { config_hjson })
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ build_federated_instances,
+ get_local_user_view_from_jwt,
+ is_admin,
+ site::{GetSiteResponse, LeaveAdmin},
+};
+use lemmy_db_schema::{
+ source::{
+ moderator::{ModAdd, ModAddForm},
+ person::Person,
+ },
+ traits::Crud,
+};
+use lemmy_db_views::site_view::SiteView;
+use lemmy_db_views_actor::person_view::PersonViewSafe;
+use lemmy_utils::{version, ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for LeaveAdmin {
+ type Response = GetSiteResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<GetSiteResponse, LemmyError> {
+ let data: &LeaveAdmin = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ is_admin(&local_user_view)?;
+
+ // Make sure there isn't just one admin (so if one leaves, there will still be one left)
+ let admins = blocking(context.pool(), PersonViewSafe::admins).await??;
+ if admins.len() == 1 {
+ return Err(LemmyError::from_message("cannot_leave_admin"));
+ }
+
+ let person_id = local_user_view.person.id;
+ blocking(context.pool(), move |conn| {
+ Person::leave_admin(conn, person_id)
+ })
+ .await??;
+
+ // Mod tables
+ let form = ModAddForm {
+ mod_person_id: person_id,
+ other_person_id: person_id,
+ removed: Some(true),
+ };
+
+ blocking(context.pool(), move |conn| ModAdd::create(conn, &form)).await??;
+
+ // Reread site and admins
+ let site_view = blocking(context.pool(), SiteView::read_local).await??;
+ let admins = blocking(context.pool(), PersonViewSafe::admins).await??;
+
+ let federated_instances =
+ build_federated_instances(context.pool(), &context.settings()).await?;
+
+ Ok(GetSiteResponse {
+ site_view: Some(site_view),
+ admins,
+ online: 0,
+ version: version::VERSION.to_string(),
+ my_user: None,
+ federated_instances,
+ })
+ }
+}
--- /dev/null
+mod config;
+mod leave_admin;
+mod mod_log;
+mod registration_applications;
+mod resolve_object;
+mod search;
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ check_private_instance,
+ get_local_user_view_from_jwt_opt,
+ site::{GetModlog, GetModlogResponse},
+};
+use lemmy_db_views_moderator::{
+ mod_add_community_view::ModAddCommunityView,
+ mod_add_view::ModAddView,
+ mod_ban_from_community_view::ModBanFromCommunityView,
+ mod_ban_view::ModBanView,
+ mod_hide_community_view::ModHideCommunityView,
+ mod_lock_post_view::ModLockPostView,
+ mod_remove_comment_view::ModRemoveCommentView,
+ mod_remove_community_view::ModRemoveCommunityView,
+ mod_remove_post_view::ModRemovePostView,
+ mod_sticky_post_view::ModStickyPostView,
+ mod_transfer_community_view::ModTransferCommunityView,
+};
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for GetModlog {
+ type Response = GetModlogResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<GetModlogResponse, LemmyError> {
+ let data: &GetModlog = self;
+
+ let local_user_view =
+ get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
+ .await?;
+
+ check_private_instance(&local_user_view, context.pool()).await?;
+
+ let community_id = data.community_id;
+ let mod_person_id = data.mod_person_id;
+ let page = data.page;
+ let limit = data.limit;
+ let removed_posts = blocking(context.pool(), move |conn| {
+ ModRemovePostView::list(conn, community_id, mod_person_id, page, limit)
+ })
+ .await??;
+
+ let locked_posts = blocking(context.pool(), move |conn| {
+ ModLockPostView::list(conn, community_id, mod_person_id, page, limit)
+ })
+ .await??;
+
+ let stickied_posts = blocking(context.pool(), move |conn| {
+ ModStickyPostView::list(conn, community_id, mod_person_id, page, limit)
+ })
+ .await??;
+
+ let removed_comments = blocking(context.pool(), move |conn| {
+ ModRemoveCommentView::list(conn, community_id, mod_person_id, page, limit)
+ })
+ .await??;
+
+ let banned_from_community = blocking(context.pool(), move |conn| {
+ ModBanFromCommunityView::list(conn, community_id, mod_person_id, page, limit)
+ })
+ .await??;
+
+ let added_to_community = blocking(context.pool(), move |conn| {
+ ModAddCommunityView::list(conn, community_id, mod_person_id, page, limit)
+ })
+ .await??;
+
+ let transferred_to_community = blocking(context.pool(), move |conn| {
+ ModTransferCommunityView::list(conn, community_id, mod_person_id, page, limit)
+ })
+ .await??;
+
+ let hidden_communities = blocking(context.pool(), move |conn| {
+ ModHideCommunityView::list(conn, community_id, mod_person_id, page, limit)
+ })
+ .await??;
+
+ // These arrays are only for the full modlog, when a community isn't given
+ let (removed_communities, banned, added) = if data.community_id.is_none() {
+ blocking(context.pool(), move |conn| {
+ Ok((
+ ModRemoveCommunityView::list(conn, mod_person_id, page, limit)?,
+ ModBanView::list(conn, mod_person_id, page, limit)?,
+ ModAddView::list(conn, mod_person_id, page, limit)?,
+ )) as Result<_, LemmyError>
+ })
+ .await??
+ } else {
+ (Vec::new(), Vec::new(), Vec::new())
+ };
+
+ // Return the jwt
+ Ok(GetModlogResponse {
+ removed_posts,
+ locked_posts,
+ stickied_posts,
+ removed_comments,
+ removed_communities,
+ banned_from_community,
+ banned,
+ added_to_community,
+ added,
+ transferred_to_community,
+ hidden_communities,
+ })
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ get_local_user_view_from_jwt,
+ is_admin,
+ send_application_approved_email,
+ site::*,
+};
+use lemmy_db_schema::{
+ diesel_option_overwrite,
+ source::{
+ local_user::{LocalUser, LocalUserForm},
+ registration_application::{RegistrationApplication, RegistrationApplicationForm},
+ },
+ traits::Crud,
+};
+use lemmy_db_views::{
+ local_user_view::LocalUserView,
+ registration_application_view::RegistrationApplicationView,
+};
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for ApproveRegistrationApplication {
+ type Response = RegistrationApplicationResponse;
+
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<Self::Response, LemmyError> {
+ let data = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ let app_id = data.id;
+
+ // Only let admins do this
+ is_admin(&local_user_view)?;
+
+ // Update the registration with reason, admin_id
+ let deny_reason = diesel_option_overwrite(&data.deny_reason);
+ let app_form = RegistrationApplicationForm {
+ admin_id: Some(local_user_view.person.id),
+ deny_reason,
+ ..RegistrationApplicationForm::default()
+ };
+
+ let registration_application = blocking(context.pool(), move |conn| {
+ RegistrationApplication::update(conn, app_id, &app_form)
+ })
+ .await??;
+
+ // Update the local_user row
+ let local_user_form = LocalUserForm {
+ accepted_application: Some(data.approve),
+ ..LocalUserForm::default()
+ };
+
+ let approved_user_id = registration_application.local_user_id;
+ blocking(context.pool(), move |conn| {
+ LocalUser::update(conn, approved_user_id, &local_user_form)
+ })
+ .await??;
+
+ if data.approve {
+ let approved_local_user_view = blocking(context.pool(), move |conn| {
+ LocalUserView::read(conn, approved_user_id)
+ })
+ .await??;
+
+ if approved_local_user_view.local_user.email.is_some() {
+ send_application_approved_email(&approved_local_user_view, &context.settings())?;
+ }
+ }
+
+ // Read the view
+ let registration_application = blocking(context.pool(), move |conn| {
+ RegistrationApplicationView::read(conn, app_id)
+ })
+ .await??;
+
+ Ok(Self::Response {
+ registration_application,
+ })
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ get_local_user_view_from_jwt,
+ is_admin,
+ site::{ListRegistrationApplications, ListRegistrationApplicationsResponse},
+};
+use lemmy_db_schema::source::site::Site;
+use lemmy_db_views::registration_application_view::RegistrationApplicationQueryBuilder;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+/// Lists registration applications, filterable by undenied only.
+#[async_trait::async_trait(?Send)]
+impl Perform for ListRegistrationApplications {
+ type Response = ListRegistrationApplicationsResponse;
+
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<Self::Response, LemmyError> {
+ let data = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ // Make sure user is an admin
+ is_admin(&local_user_view)?;
+
+ let unread_only = data.unread_only;
+ let verified_email_only = blocking(context.pool(), Site::read_local_site)
+ .await??
+ .require_email_verification;
+
+ let page = data.page;
+ let limit = data.limit;
+ let registration_applications = blocking(context.pool(), move |conn| {
+ RegistrationApplicationQueryBuilder::create(conn)
+ .unread_only(unread_only)
+ .verified_email_only(verified_email_only)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
+
+ let res = Self::Response {
+ registration_applications,
+ };
+
+ Ok(res)
+ }
+}
--- /dev/null
+mod approve;
+mod list;
+mod unread_count;
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ get_local_user_view_from_jwt,
+ is_admin,
+ site::{GetUnreadRegistrationApplicationCount, GetUnreadRegistrationApplicationCountResponse},
+};
+use lemmy_db_schema::source::site::Site;
+use lemmy_db_views::registration_application_view::RegistrationApplicationView;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for GetUnreadRegistrationApplicationCount {
+ type Response = GetUnreadRegistrationApplicationCountResponse;
+
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<Self::Response, LemmyError> {
+ let data = self;
+ let local_user_view =
+ get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+
+ // Only let admins do this
+ is_admin(&local_user_view)?;
+
+ let verified_email_only = blocking(context.pool(), Site::read_local_site)
+ .await??
+ .require_email_verification;
+
+ let registration_applications = blocking(context.pool(), move |conn| {
+ RegistrationApplicationView::get_unread_count(conn, verified_email_only)
+ })
+ .await??;
+
+ Ok(Self::Response {
+ registration_applications,
+ })
+ }
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use diesel::NotFound;
+use lemmy_api_common::{
+ blocking,
+ check_private_instance,
+ get_local_user_view_from_jwt_opt,
+ site::{ResolveObject, ResolveObjectResponse},
+};
+use lemmy_apub::fetcher::search::{search_by_apub_id, SearchableObjects};
+use lemmy_db_schema::{newtypes::PersonId, DbPool};
+use lemmy_db_views::{comment_view::CommentView, post_view::PostView};
+use lemmy_db_views_actor::{community_view::CommunityView, person_view::PersonViewSafe};
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for ResolveObject {
+ type Response = ResolveObjectResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<ResolveObjectResponse, LemmyError> {
+ let local_user_view =
+ get_local_user_view_from_jwt_opt(self.auth.as_ref(), context.pool(), context.secret())
+ .await?;
+ check_private_instance(&local_user_view, context.pool()).await?;
+
+ let res = search_by_apub_id(&self.q, context)
+ .await
+ .map_err(|e| e.with_message("couldnt_find_object"))?;
+ convert_response(res, local_user_view.map(|l| l.person.id), context.pool())
+ .await
+ .map_err(|e| e.with_message("couldnt_find_object"))
+ }
+}
+
+async fn convert_response(
+ object: SearchableObjects,
+ user_id: Option<PersonId>,
+ pool: &DbPool,
+) -> Result<ResolveObjectResponse, LemmyError> {
+ let removed_or_deleted;
+ let mut res = ResolveObjectResponse {
+ comment: None,
+ post: None,
+ community: None,
+ person: None,
+ };
+ use SearchableObjects::*;
+ match object {
+ Person(p) => {
+ removed_or_deleted = p.deleted;
+ res.person = Some(blocking(pool, move |conn| PersonViewSafe::read(conn, p.id)).await??)
+ }
+ Community(c) => {
+ removed_or_deleted = c.deleted || c.removed;
+ res.community =
+ Some(blocking(pool, move |conn| CommunityView::read(conn, c.id, user_id)).await??)
+ }
+ Post(p) => {
+ removed_or_deleted = p.deleted || p.removed;
+ res.post = Some(blocking(pool, move |conn| PostView::read(conn, p.id, user_id)).await??)
+ }
+ Comment(c) => {
+ removed_or_deleted = c.deleted || c.removed;
+ res.comment = Some(blocking(pool, move |conn| CommentView::read(conn, c.id, user_id)).await??)
+ }
+ };
+ // if the object was deleted from database, dont return it
+ if removed_or_deleted {
+ return Err(NotFound {}.into());
+ }
+ Ok(res)
+}
--- /dev/null
+use crate::Perform;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ check_private_instance,
+ get_local_user_view_from_jwt_opt,
+ site::{Search, SearchResponse},
+};
+use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity};
+use lemmy_db_schema::{
+ from_opt_str_to_opt_enum,
+ source::community::Community,
+ traits::DeleteableOrRemoveable,
+ ListingType,
+ SearchType,
+ SortType,
+};
+use lemmy_db_views::{comment_view::CommentQueryBuilder, post_view::PostQueryBuilder};
+use lemmy_db_views_actor::{
+ community_view::CommunityQueryBuilder,
+ person_view::PersonQueryBuilder,
+};
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl Perform for Search {
+ type Response = SearchResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<SearchResponse, LemmyError> {
+ let data: &Search = self;
+
+ let local_user_view =
+ get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
+ .await?;
+
+ check_private_instance(&local_user_view, context.pool()).await?;
+
+ let show_nsfw = local_user_view.as_ref().map(|t| t.local_user.show_nsfw);
+ let show_bot_accounts = local_user_view
+ .as_ref()
+ .map(|t| t.local_user.show_bot_accounts);
+ let show_read_posts = local_user_view
+ .as_ref()
+ .map(|t| t.local_user.show_read_posts);
+
+ let person_id = local_user_view.map(|u| u.person.id);
+
+ let mut posts = Vec::new();
+ let mut comments = Vec::new();
+ let mut communities = Vec::new();
+ let mut users = Vec::new();
+
+ // TODO no clean / non-nsfw searching rn
+
+ let q = data.q.to_owned();
+ let page = data.page;
+ let limit = data.limit;
+ let sort: Option<SortType> = from_opt_str_to_opt_enum(&data.sort);
+ let listing_type: Option<ListingType> = from_opt_str_to_opt_enum(&data.listing_type);
+ let search_type: SearchType = from_opt_str_to_opt_enum(&data.type_).unwrap_or(SearchType::All);
+ let community_id = data.community_id;
+ let community_actor_id = if let Some(name) = &data.community_name {
+ resolve_actor_identifier::<ApubCommunity, Community>(name, context)
+ .await
+ .ok()
+ .map(|c| c.actor_id)
+ } else {
+ None
+ };
+ let creator_id = data.creator_id;
+ match search_type {
+ SearchType::Posts => {
+ posts = blocking(context.pool(), move |conn| {
+ PostQueryBuilder::create(conn)
+ .sort(sort)
+ .show_nsfw(show_nsfw)
+ .show_bot_accounts(show_bot_accounts)
+ .show_read_posts(show_read_posts)
+ .listing_type(listing_type)
+ .community_id(community_id)
+ .community_actor_id(community_actor_id)
+ .creator_id(creator_id)
+ .my_person_id(person_id)
+ .search_term(q)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
+ }
+ SearchType::Comments => {
+ comments = blocking(context.pool(), move |conn| {
+ CommentQueryBuilder::create(conn)
+ .sort(sort)
+ .listing_type(listing_type)
+ .search_term(q)
+ .show_bot_accounts(show_bot_accounts)
+ .community_id(community_id)
+ .community_actor_id(community_actor_id)
+ .creator_id(creator_id)
+ .my_person_id(person_id)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
+ }
+ SearchType::Communities => {
+ communities = blocking(context.pool(), move |conn| {
+ CommunityQueryBuilder::create(conn)
+ .sort(sort)
+ .listing_type(listing_type)
+ .search_term(q)
+ .my_person_id(person_id)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
+ }
+ SearchType::Users => {
+ users = blocking(context.pool(), move |conn| {
+ PersonQueryBuilder::create(conn)
+ .sort(sort)
+ .search_term(q)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
+ }
+ SearchType::All => {
+ // If the community or creator is included, dont search communities or users
+ let community_or_creator_included =
+ data.community_id.is_some() || data.community_name.is_some() || data.creator_id.is_some();
+ let community_actor_id_2 = community_actor_id.to_owned();
+
+ posts = blocking(context.pool(), move |conn| {
+ PostQueryBuilder::create(conn)
+ .sort(sort)
+ .show_nsfw(show_nsfw)
+ .show_bot_accounts(show_bot_accounts)
+ .show_read_posts(show_read_posts)
+ .listing_type(listing_type)
+ .community_id(community_id)
+ .community_actor_id(community_actor_id_2)
+ .creator_id(creator_id)
+ .my_person_id(person_id)
+ .search_term(q)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
+
+ let q = data.q.to_owned();
+ let community_actor_id = community_actor_id.to_owned();
+
+ comments = blocking(context.pool(), move |conn| {
+ CommentQueryBuilder::create(conn)
+ .sort(sort)
+ .listing_type(listing_type)
+ .search_term(q)
+ .show_bot_accounts(show_bot_accounts)
+ .community_id(community_id)
+ .community_actor_id(community_actor_id)
+ .creator_id(creator_id)
+ .my_person_id(person_id)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
+
+ let q = data.q.to_owned();
+
+ communities = if community_or_creator_included {
+ vec![]
+ } else {
+ blocking(context.pool(), move |conn| {
+ CommunityQueryBuilder::create(conn)
+ .sort(sort)
+ .listing_type(listing_type)
+ .search_term(q)
+ .my_person_id(person_id)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??
+ };
+
+ let q = data.q.to_owned();
+
+ users = if community_or_creator_included {
+ vec![]
+ } else {
+ blocking(context.pool(), move |conn| {
+ PersonQueryBuilder::create(conn)
+ .sort(sort)
+ .search_term(q)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??
+ };
+ }
+ SearchType::Url => {
+ posts = blocking(context.pool(), move |conn| {
+ PostQueryBuilder::create(conn)
+ .sort(sort)
+ .show_nsfw(show_nsfw)
+ .show_bot_accounts(show_bot_accounts)
+ .show_read_posts(show_read_posts)
+ .listing_type(listing_type)
+ .my_person_id(person_id)
+ .community_id(community_id)
+ .community_actor_id(community_actor_id)
+ .creator_id(creator_id)
+ .url_search(q)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
+ }
+ };
+
+ // Blank out deleted or removed info for non logged in users
+ if person_id.is_none() {
+ for cv in communities
+ .iter_mut()
+ .filter(|cv| cv.community.deleted || cv.community.removed)
+ {
+ cv.community = cv.to_owned().community.blank_out_deleted_or_removed_info();
+ }
+
+ for pv in posts
+ .iter_mut()
+ .filter(|p| p.post.deleted || p.post.removed)
+ {
+ pv.post = pv.to_owned().post.blank_out_deleted_or_removed_info();
+ }
+
+ for cv in comments
+ .iter_mut()
+ .filter(|cv| cv.comment.deleted || cv.comment.removed)
+ {
+ cv.comment = cv.to_owned().comment.blank_out_deleted_or_removed_info();
+ }
+ }
+
+ // Return the jwt
+ Ok(SearchResponse {
+ type_: search_type.to_string(),
+ comments,
+ posts,
+ communities,
+ users,
+ })
+ }
+}
pub struct PasswordResetResponse {}
#[derive(Debug, Serialize, Deserialize)]
-pub struct PasswordChange {
+pub struct PasswordChangeAfterReset {
pub token: Sensitive<String>,
pub password: Sensitive<String>,
pub password_verify: Sensitive<String>,
--- /dev/null
+use crate::PerformCrud;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ check_private_instance,
+ comment::*,
+ get_local_user_view_from_jwt_opt,
+};
+use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity};
+use lemmy_db_schema::{
+ from_opt_str_to_opt_enum,
+ source::community::Community,
+ traits::DeleteableOrRemoveable,
+ ListingType,
+ SortType,
+};
+use lemmy_db_views::comment_view::CommentQueryBuilder;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl PerformCrud for GetComments {
+ type Response = GetCommentsResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<GetCommentsResponse, LemmyError> {
+ let data: &GetComments = self;
+ let local_user_view =
+ get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
+ .await?;
+
+ check_private_instance(&local_user_view, context.pool()).await?;
+
+ let show_bot_accounts = local_user_view
+ .as_ref()
+ .map(|t| t.local_user.show_bot_accounts);
+ let person_id = local_user_view.map(|u| u.person.id);
+
+ let sort: Option<SortType> = from_opt_str_to_opt_enum(&data.sort);
+ let listing_type: Option<ListingType> = from_opt_str_to_opt_enum(&data.type_);
+
+ let community_id = data.community_id;
+ let community_actor_id = if let Some(name) = &data.community_name {
+ resolve_actor_identifier::<ApubCommunity, Community>(name, context)
+ .await
+ .ok()
+ .map(|c| c.actor_id)
+ } else {
+ None
+ };
+ let saved_only = data.saved_only;
+ let page = data.page;
+ let limit = data.limit;
+ let mut comments = blocking(context.pool(), move |conn| {
+ CommentQueryBuilder::create(conn)
+ .listing_type(listing_type)
+ .sort(sort)
+ .saved_only(saved_only)
+ .community_id(community_id)
+ .community_actor_id(community_actor_id)
+ .my_person_id(person_id)
+ .show_bot_accounts(show_bot_accounts)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_get_comments"))?;
+
+ // Blank out deleted or removed info
+ for cv in comments
+ .iter_mut()
+ .filter(|cv| cv.comment.deleted || cv.comment.removed)
+ {
+ cv.comment = cv.to_owned().comment.blank_out_deleted_or_removed_info();
+ }
+
+ Ok(GetCommentsResponse { comments })
+ }
+}
mod create;
mod delete;
+mod list;
mod read;
mod update;
comment::*,
get_local_user_view_from_jwt_opt,
};
-use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity};
-use lemmy_db_schema::{
- from_opt_str_to_opt_enum,
- source::community::Community,
- traits::DeleteableOrRemoveable,
- ListingType,
- SortType,
-};
-use lemmy_db_views::comment_view::{CommentQueryBuilder, CommentView};
+use lemmy_db_views::comment_view::CommentView;
use lemmy_utils::{ConnectionId, LemmyError};
use lemmy_websocket::LemmyContext;
})
}
}
-
-#[async_trait::async_trait(?Send)]
-impl PerformCrud for GetComments {
- type Response = GetCommentsResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<GetCommentsResponse, LemmyError> {
- let data: &GetComments = self;
- let local_user_view =
- get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
- .await?;
-
- check_private_instance(&local_user_view, context.pool()).await?;
-
- let show_bot_accounts = local_user_view
- .as_ref()
- .map(|t| t.local_user.show_bot_accounts);
- let person_id = local_user_view.map(|u| u.person.id);
-
- let sort: Option<SortType> = from_opt_str_to_opt_enum(&data.sort);
- let listing_type: Option<ListingType> = from_opt_str_to_opt_enum(&data.type_);
-
- let community_id = data.community_id;
- let community_actor_id = if let Some(name) = &data.community_name {
- resolve_actor_identifier::<ApubCommunity, Community>(name, context)
- .await
- .ok()
- .map(|c| c.actor_id)
- } else {
- None
- };
- let saved_only = data.saved_only;
- let page = data.page;
- let limit = data.limit;
- let mut comments = blocking(context.pool(), move |conn| {
- CommentQueryBuilder::create(conn)
- .listing_type(listing_type)
- .sort(sort)
- .saved_only(saved_only)
- .community_id(community_id)
- .community_actor_id(community_actor_id)
- .my_person_id(person_id)
- .show_bot_accounts(show_bot_accounts)
- .page(page)
- .limit(limit)
- .list()
- })
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "couldnt_get_comments"))?;
-
- // Blank out deleted or removed info
- for cv in comments
- .iter_mut()
- .filter(|cv| cv.comment.deleted || cv.comment.removed)
- {
- cv.comment = cv.to_owned().comment.blank_out_deleted_or_removed_info();
- }
-
- Ok(GetCommentsResponse { comments })
- }
-}
--- /dev/null
+use crate::PerformCrud;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ check_private_instance,
+ community::*,
+ get_local_user_view_from_jwt_opt,
+};
+use lemmy_db_schema::{
+ from_opt_str_to_opt_enum,
+ traits::DeleteableOrRemoveable,
+ ListingType,
+ SortType,
+};
+use lemmy_db_views_actor::community_view::CommunityQueryBuilder;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl PerformCrud for ListCommunities {
+ type Response = ListCommunitiesResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<ListCommunitiesResponse, LemmyError> {
+ let data: &ListCommunities = self;
+ let local_user_view =
+ get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
+ .await?;
+
+ check_private_instance(&local_user_view, context.pool()).await?;
+
+ let person_id = local_user_view.to_owned().map(|l| l.person.id);
+
+ // Don't show NSFW by default
+ let show_nsfw = match &local_user_view {
+ Some(uv) => uv.local_user.show_nsfw,
+ None => false,
+ };
+
+ let sort: Option<SortType> = from_opt_str_to_opt_enum(&data.sort);
+ let listing_type: Option<ListingType> = from_opt_str_to_opt_enum(&data.type_);
+
+ let page = data.page;
+ let limit = data.limit;
+ let mut communities = blocking(context.pool(), move |conn| {
+ CommunityQueryBuilder::create(conn)
+ .listing_type(listing_type)
+ .sort(sort)
+ .show_nsfw(show_nsfw)
+ .my_person_id(person_id)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await??;
+
+ // Blank out deleted or removed info for non-logged in users
+ if person_id.is_none() {
+ for cv in communities
+ .iter_mut()
+ .filter(|cv| cv.community.deleted || cv.community.removed)
+ {
+ cv.community = cv.to_owned().community.blank_out_deleted_or_removed_info();
+ }
+ }
+
+ // Return the jwt
+ Ok(ListCommunitiesResponse { communities })
+ }
+}
mod create;
mod delete;
+mod list;
mod read;
mod update;
objects::{community::ApubCommunity, instance::instance_actor_id_from_url},
};
use lemmy_db_schema::{
- from_opt_str_to_opt_enum,
source::{community::Community, site::Site},
traits::DeleteableOrRemoveable,
- ListingType,
- SortType,
};
use lemmy_db_views_actor::{
community_moderator_view::CommunityModeratorView,
- community_view::{CommunityQueryBuilder, CommunityView},
+ community_view::CommunityView,
};
use lemmy_utils::{ConnectionId, LemmyError};
use lemmy_websocket::{messages::GetCommunityUsersOnline, LemmyContext};
Ok(res)
}
}
-
-#[async_trait::async_trait(?Send)]
-impl PerformCrud for ListCommunities {
- type Response = ListCommunitiesResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<ListCommunitiesResponse, LemmyError> {
- let data: &ListCommunities = self;
- let local_user_view =
- get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
- .await?;
-
- check_private_instance(&local_user_view, context.pool()).await?;
-
- let person_id = local_user_view.to_owned().map(|l| l.person.id);
-
- // Don't show NSFW by default
- let show_nsfw = match &local_user_view {
- Some(uv) => uv.local_user.show_nsfw,
- None => false,
- };
-
- let sort: Option<SortType> = from_opt_str_to_opt_enum(&data.sort);
- let listing_type: Option<ListingType> = from_opt_str_to_opt_enum(&data.type_);
-
- let page = data.page;
- let limit = data.limit;
- let mut communities = blocking(context.pool(), move |conn| {
- CommunityQueryBuilder::create(conn)
- .listing_type(listing_type)
- .sort(sort)
- .show_nsfw(show_nsfw)
- .my_person_id(person_id)
- .page(page)
- .limit(limit)
- .list()
- })
- .await??;
-
- // Blank out deleted or removed info for non-logged in users
- if person_id.is_none() {
- for cv in communities
- .iter_mut()
- .filter(|cv| cv.community.deleted || cv.community.removed)
- {
- cv.community = cv.to_owned().community.blank_out_deleted_or_removed_info();
- }
- }
-
- // Return the jwt
- Ok(ListCommunitiesResponse { communities })
- }
-}
--- /dev/null
+use crate::PerformCrud;
+use actix_web::web::Data;
+use lemmy_api_common::{
+ blocking,
+ check_private_instance,
+ get_local_user_view_from_jwt_opt,
+ post::*,
+};
+use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity};
+use lemmy_db_schema::{
+ from_opt_str_to_opt_enum,
+ source::community::Community,
+ traits::DeleteableOrRemoveable,
+ ListingType,
+ SortType,
+};
+use lemmy_db_views::post_view::PostQueryBuilder;
+use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_websocket::LemmyContext;
+
+#[async_trait::async_trait(?Send)]
+impl PerformCrud for GetPosts {
+ type Response = GetPostsResponse;
+
+ #[tracing::instrument(skip(context, _websocket_id))]
+ async fn perform(
+ &self,
+ context: &Data<LemmyContext>,
+ _websocket_id: Option<ConnectionId>,
+ ) -> Result<GetPostsResponse, LemmyError> {
+ let data: &GetPosts = self;
+ let local_user_view =
+ get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
+ .await?;
+
+ check_private_instance(&local_user_view, context.pool()).await?;
+
+ let person_id = local_user_view.to_owned().map(|l| l.person.id);
+
+ let show_nsfw = local_user_view.as_ref().map(|t| t.local_user.show_nsfw);
+ let show_bot_accounts = local_user_view
+ .as_ref()
+ .map(|t| t.local_user.show_bot_accounts);
+ let show_read_posts = local_user_view
+ .as_ref()
+ .map(|t| t.local_user.show_read_posts);
+
+ let sort: Option<SortType> = from_opt_str_to_opt_enum(&data.sort);
+ let listing_type: Option<ListingType> = from_opt_str_to_opt_enum(&data.type_);
+
+ let page = data.page;
+ let limit = data.limit;
+ let community_id = data.community_id;
+ let community_actor_id = if let Some(name) = &data.community_name {
+ resolve_actor_identifier::<ApubCommunity, Community>(name, context)
+ .await
+ .ok()
+ .map(|c| c.actor_id)
+ } else {
+ None
+ };
+ let saved_only = data.saved_only;
+
+ let mut posts = blocking(context.pool(), move |conn| {
+ PostQueryBuilder::create(conn)
+ .listing_type(listing_type)
+ .sort(sort)
+ .show_nsfw(show_nsfw)
+ .show_bot_accounts(show_bot_accounts)
+ .show_read_posts(show_read_posts)
+ .community_id(community_id)
+ .community_actor_id(community_actor_id)
+ .saved_only(saved_only)
+ .my_person_id(person_id)
+ .page(page)
+ .limit(limit)
+ .list()
+ })
+ .await?
+ .map_err(|e| LemmyError::from_error_message(e, "couldnt_get_posts"))?;
+
+ // Blank out deleted or removed info for non-logged in users
+ if person_id.is_none() {
+ for pv in posts
+ .iter_mut()
+ .filter(|p| p.post.deleted || p.post.removed)
+ {
+ pv.post = pv.to_owned().post.blank_out_deleted_or_removed_info();
+ }
+
+ for pv in posts
+ .iter_mut()
+ .filter(|p| p.community.deleted || p.community.removed)
+ {
+ pv.community = pv.to_owned().community.blank_out_deleted_or_removed_info();
+ }
+ }
+
+ Ok(GetPostsResponse { posts })
+ }
+}
mod create;
mod delete;
+mod list;
mod read;
mod update;
mark_post_as_read,
post::*,
};
-use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity};
-use lemmy_db_schema::{
- from_opt_str_to_opt_enum,
- source::community::Community,
- traits::DeleteableOrRemoveable,
- ListingType,
- SortType,
-};
-use lemmy_db_views::{
- comment_view::CommentQueryBuilder,
- post_view::{PostQueryBuilder, PostView},
-};
+use lemmy_db_schema::traits::DeleteableOrRemoveable;
+use lemmy_db_views::{comment_view::CommentQueryBuilder, post_view::PostView};
use lemmy_db_views_actor::{
community_moderator_view::CommunityModeratorView,
community_view::CommunityView,
})
}
}
-
-#[async_trait::async_trait(?Send)]
-impl PerformCrud for GetPosts {
- type Response = GetPostsResponse;
-
- #[tracing::instrument(skip(context, _websocket_id))]
- async fn perform(
- &self,
- context: &Data<LemmyContext>,
- _websocket_id: Option<ConnectionId>,
- ) -> Result<GetPostsResponse, LemmyError> {
- let data: &GetPosts = self;
- let local_user_view =
- get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
- .await?;
-
- check_private_instance(&local_user_view, context.pool()).await?;
-
- let person_id = local_user_view.to_owned().map(|l| l.person.id);
-
- let show_nsfw = local_user_view.as_ref().map(|t| t.local_user.show_nsfw);
- let show_bot_accounts = local_user_view
- .as_ref()
- .map(|t| t.local_user.show_bot_accounts);
- let show_read_posts = local_user_view
- .as_ref()
- .map(|t| t.local_user.show_read_posts);
-
- let sort: Option<SortType> = from_opt_str_to_opt_enum(&data.sort);
- let listing_type: Option<ListingType> = from_opt_str_to_opt_enum(&data.type_);
-
- let page = data.page;
- let limit = data.limit;
- let community_id = data.community_id;
- let community_actor_id = if let Some(name) = &data.community_name {
- resolve_actor_identifier::<ApubCommunity, Community>(name, context)
- .await
- .ok()
- .map(|c| c.actor_id)
- } else {
- None
- };
- let saved_only = data.saved_only;
-
- let mut posts = blocking(context.pool(), move |conn| {
- PostQueryBuilder::create(conn)
- .listing_type(listing_type)
- .sort(sort)
- .show_nsfw(show_nsfw)
- .show_bot_accounts(show_bot_accounts)
- .show_read_posts(show_read_posts)
- .community_id(community_id)
- .community_actor_id(community_actor_id)
- .saved_only(saved_only)
- .my_person_id(person_id)
- .page(page)
- .limit(limit)
- .list()
- })
- .await?
- .map_err(|e| LemmyError::from_error_message(e, "couldnt_get_posts"))?;
-
- // Blank out deleted or removed info for non-logged in users
- if person_id.is_none() {
- for pv in posts
- .iter_mut()
- .filter(|p| p.post.deleted || p.post.removed)
- {
- pv.post = pv.to_owned().post.blank_out_deleted_or_removed_info();
- }
-
- for pv in posts
- .iter_mut()
- .filter(|p| p.community.deleted || p.community.removed)
- {
- pv.community = pv.to_owned().community.blank_out_deleted_or_removed_info();
- }
- }
-
- Ok(GetPostsResponse { posts })
- }
-}
)
.route(
"/password_change",
- web::post().to(route_post::<PasswordChange>),
+ web::post().to(route_post::<PasswordChangeAfterReset>),
)
// mark_all_as_read feels off being in this section as well
.route(