]> Untitled Git - lemmy.git/blob - crates/api/src/community/transfer.rs
05282f457d4939edb78a8baac433e185d674b1f1
[lemmy.git] / crates / api / src / community / transfer.rs
1 use crate::Perform;
2 use actix_web::web::Data;
3 use anyhow::Context;
4 use lemmy_api_common::{
5   blocking,
6   community::{GetCommunityResponse, TransferCommunity},
7   get_local_user_view_from_jwt,
8 };
9 use lemmy_db_schema::{
10   source::{
11     community::{CommunityModerator, CommunityModeratorForm},
12     moderator::{ModTransferCommunity, ModTransferCommunityForm},
13   },
14   traits::{Crud, Joinable},
15 };
16 use lemmy_db_views_actor::{
17   community_moderator_view::CommunityModeratorView,
18   community_view::CommunityView,
19   person_view::PersonViewSafe,
20 };
21 use lemmy_utils::{location_info, ConnectionId, LemmyError};
22 use lemmy_websocket::LemmyContext;
23
24 // TODO: we dont do anything for federation here, it should be updated the next time the community
25 //       gets fetched. i hope we can get rid of the community creator role soon.
26 #[async_trait::async_trait(?Send)]
27 impl Perform for TransferCommunity {
28   type Response = GetCommunityResponse;
29
30   #[tracing::instrument(skip(context, _websocket_id))]
31   async fn perform(
32     &self,
33     context: &Data<LemmyContext>,
34     _websocket_id: Option<ConnectionId>,
35   ) -> Result<GetCommunityResponse, LemmyError> {
36     let data: &TransferCommunity = self;
37     let local_user_view =
38       get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
39
40     let admins = blocking(context.pool(), PersonViewSafe::admins).await??;
41
42     // Fetch the community mods
43     let community_id = data.community_id;
44     let mut community_mods = blocking(context.pool(), move |conn| {
45       CommunityModeratorView::for_community(conn, community_id)
46     })
47     .await??;
48
49     // Make sure transferrer is either the top community mod, or an admin
50     if local_user_view.person.id != community_mods[0].moderator.id
51       && !admins
52         .iter()
53         .map(|a| a.person.id)
54         .any(|x| x == local_user_view.person.id)
55     {
56       return Err(LemmyError::from_message("not_an_admin"));
57     }
58
59     // You have to re-do the community_moderator table, reordering it.
60     // Add the transferee to the top
61     let creator_index = community_mods
62       .iter()
63       .position(|r| r.moderator.id == data.person_id)
64       .context(location_info!())?;
65     let creator_person = community_mods.remove(creator_index);
66     community_mods.insert(0, creator_person);
67
68     // Delete all the mods
69     let community_id = data.community_id;
70     blocking(context.pool(), move |conn| {
71       CommunityModerator::delete_for_community(conn, community_id)
72     })
73     .await??;
74
75     // TODO: this should probably be a bulk operation
76     // Re-add the mods, in the new order
77     for cmod in &community_mods {
78       let community_moderator_form = CommunityModeratorForm {
79         community_id: cmod.community.id,
80         person_id: cmod.moderator.id,
81       };
82
83       let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
84       blocking(context.pool(), join)
85         .await?
86         .map_err(|e| LemmyError::from_error_message(e, "community_moderator_already_exists"))?;
87     }
88
89     // Mod tables
90     let form = ModTransferCommunityForm {
91       mod_person_id: local_user_view.person.id,
92       other_person_id: data.person_id,
93       community_id: data.community_id,
94       removed: Some(false),
95     };
96     blocking(context.pool(), move |conn| {
97       ModTransferCommunity::create(conn, &form)
98     })
99     .await??;
100
101     let community_id = data.community_id;
102     let person_id = local_user_view.person.id;
103     let community_view = blocking(context.pool(), move |conn| {
104       CommunityView::read(conn, community_id, Some(person_id))
105     })
106     .await?
107     .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?;
108
109     let community_id = data.community_id;
110     let moderators = blocking(context.pool(), move |conn| {
111       CommunityModeratorView::for_community(conn, community_id)
112     })
113     .await?
114     .map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?;
115
116     // Return the jwt
117     Ok(GetCommunityResponse {
118       community_view,
119       site: None,
120       moderators,
121       online: 0,
122     })
123   }
124 }