2 use crate::apub::puller::{get_all_communities, get_remote_community};
3 use crate::apub::{gen_keypair_str, make_apub_endpoint, EndpointType};
4 use crate::settings::Settings;
5 use diesel::PgConnection;
8 #[derive(Serialize, Deserialize)]
9 pub struct GetCommunity {
11 pub name: Option<String>,
15 #[derive(Serialize, Deserialize)]
16 pub struct GetCommunityResponse {
17 pub community: CommunityView,
18 pub moderators: Vec<CommunityModeratorView>,
19 pub admins: Vec<UserView>,
23 #[derive(Serialize, Deserialize)]
24 pub struct CreateCommunity {
27 description: Option<String>,
33 #[derive(Serialize, Deserialize, Clone)]
34 pub struct CommunityResponse {
35 pub community: CommunityView,
38 #[derive(Serialize, Deserialize, Debug)]
39 pub struct ListCommunities {
41 pub page: Option<i64>,
42 pub limit: Option<i64>,
43 pub auth: Option<String>,
44 pub local_only: Option<bool>,
47 #[derive(Serialize, Deserialize, Debug)]
48 pub struct ListCommunitiesResponse {
49 pub communities: Vec<CommunityView>,
52 #[derive(Serialize, Deserialize, Clone)]
53 pub struct BanFromCommunity {
54 pub community_id: i32,
57 reason: Option<String>,
62 #[derive(Serialize, Deserialize)]
63 pub struct BanFromCommunityResponse {
68 #[derive(Serialize, Deserialize)]
69 pub struct AddModToCommunity {
70 pub community_id: i32,
76 #[derive(Serialize, Deserialize)]
77 pub struct AddModToCommunityResponse {
78 moderators: Vec<CommunityModeratorView>,
81 #[derive(Serialize, Deserialize)]
82 pub struct EditCommunity {
86 description: Option<String>,
88 removed: Option<bool>,
89 deleted: Option<bool>,
91 reason: Option<String>,
96 #[derive(Serialize, Deserialize)]
97 pub struct FollowCommunity {
103 #[derive(Serialize, Deserialize)]
104 pub struct GetFollowedCommunities {
108 #[derive(Serialize, Deserialize)]
109 pub struct GetFollowedCommunitiesResponse {
110 communities: Vec<CommunityFollowerView>,
113 #[derive(Serialize, Deserialize)]
114 pub struct TransferCommunity {
120 impl Perform<GetCommunityResponse> for Oper<GetCommunity> {
121 fn perform(&self, conn: &PgConnection) -> Result<GetCommunityResponse, Error> {
122 let data: &GetCommunity = &self.data;
124 if data.name.is_some()
125 && Settings::get().federation.enabled
126 && data.name.as_ref().unwrap().contains('@')
128 return get_remote_community(data.name.as_ref().unwrap());
131 let user_id: Option<i32> = match &data.auth {
132 Some(auth) => match Claims::decode(&auth) {
134 let user_id = claims.claims.id;
142 let community_id = match data.id {
145 match Community::read_from_name(
147 data.name.to_owned().unwrap_or_else(|| "main".to_string()),
149 Ok(community) => community.id,
150 Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
155 let community_view = match CommunityView::read(&conn, community_id, user_id) {
156 Ok(community) => community,
157 Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
160 let moderators = match CommunityModeratorView::for_community(&conn, community_id) {
161 Ok(moderators) => moderators,
162 Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
165 let site_creator_id = Site::read(&conn, 1)?.creator_id;
166 let mut admins = UserView::admins(&conn)?;
167 let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
168 let creator_user = admins.remove(creator_index);
169 admins.insert(0, creator_user);
172 Ok(GetCommunityResponse {
173 community: community_view,
181 impl Perform<CommunityResponse> for Oper<CreateCommunity> {
182 fn perform(&self, conn: &PgConnection) -> Result<CommunityResponse, Error> {
183 let data: &CreateCommunity = &self.data;
185 let claims = match Claims::decode(&data.auth) {
186 Ok(claims) => claims.claims,
187 Err(_e) => return Err(APIError::err("not_logged_in").into()),
190 if let Err(slurs) = slur_check(&data.name) {
191 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
194 if let Err(slurs) = slur_check(&data.title) {
195 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
198 if let Some(description) = &data.description {
199 if let Err(slurs) = slur_check(description) {
200 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
204 let user_id = claims.id;
206 // Check for a site ban
207 if UserView::read(&conn, user_id)?.banned {
208 return Err(APIError::err("site_ban").into());
211 // When you create a community, make sure the user becomes a moderator and a follower
212 let (community_public_key, community_private_key) = gen_keypair_str();
214 let community_form = CommunityForm {
215 name: data.name.to_owned(),
216 title: data.title.to_owned(),
217 description: data.description.to_owned(),
218 category_id: data.category_id,
224 actor_id: make_apub_endpoint(EndpointType::Community, &data.name).to_string(),
226 private_key: Some(community_private_key),
227 public_key: Some(community_public_key),
228 last_refreshed_at: None,
231 let inserted_community = match Community::create(&conn, &community_form) {
232 Ok(community) => community,
233 Err(_e) => return Err(APIError::err("community_already_exists").into()),
236 let community_moderator_form = CommunityModeratorForm {
237 community_id: inserted_community.id,
241 let _inserted_community_moderator =
242 match CommunityModerator::join(&conn, &community_moderator_form) {
244 Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
247 let community_follower_form = CommunityFollowerForm {
248 community_id: inserted_community.id,
252 let _inserted_community_follower =
253 match CommunityFollower::follow(&conn, &community_follower_form) {
255 Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
258 let community_view = CommunityView::read(&conn, inserted_community.id, Some(user_id))?;
260 Ok(CommunityResponse {
261 community: community_view,
266 impl Perform<CommunityResponse> for Oper<EditCommunity> {
267 fn perform(&self, conn: &PgConnection) -> Result<CommunityResponse, Error> {
268 let data: &EditCommunity = &self.data;
270 if let Err(slurs) = slur_check(&data.name) {
271 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
274 if let Err(slurs) = slur_check(&data.title) {
275 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
278 if let Some(description) = &data.description {
279 if let Err(slurs) = slur_check(description) {
280 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
284 let claims = match Claims::decode(&data.auth) {
285 Ok(claims) => claims.claims,
286 Err(_e) => return Err(APIError::err("not_logged_in").into()),
289 let user_id = claims.id;
291 // Check for a site ban
292 if UserView::read(&conn, user_id)?.banned {
293 return Err(APIError::err("site_ban").into());
297 let mut editors: Vec<i32> = Vec::new();
299 &mut CommunityModeratorView::for_community(&conn, data.edit_id)?
304 editors.append(&mut UserView::admins(&conn)?.into_iter().map(|a| a.id).collect());
305 if !editors.contains(&user_id) {
306 return Err(APIError::err("no_community_edit_allowed").into());
309 let read_community = Community::read(&conn, data.edit_id)?;
311 let community_form = CommunityForm {
312 name: data.name.to_owned(),
313 title: data.title.to_owned(),
314 description: data.description.to_owned(),
315 category_id: data.category_id.to_owned(),
317 removed: data.removed.to_owned(),
318 deleted: data.deleted.to_owned(),
320 updated: Some(naive_now()),
321 actor_id: read_community.actor_id,
322 local: read_community.local,
323 private_key: read_community.private_key,
324 public_key: read_community.public_key,
325 last_refreshed_at: None,
328 let _updated_community = match Community::update(&conn, data.edit_id, &community_form) {
329 Ok(community) => community,
330 Err(_e) => return Err(APIError::err("couldnt_update_community").into()),
334 if let Some(removed) = data.removed.to_owned() {
335 let expires = match data.expires {
336 Some(time) => Some(naive_from_unix(time)),
339 let form = ModRemoveCommunityForm {
340 mod_user_id: user_id,
341 community_id: data.edit_id,
342 removed: Some(removed),
343 reason: data.reason.to_owned(),
346 ModRemoveCommunity::create(&conn, &form)?;
349 let community_view = CommunityView::read(&conn, data.edit_id, Some(user_id))?;
351 Ok(CommunityResponse {
352 community: community_view,
357 impl Perform<ListCommunitiesResponse> for Oper<ListCommunities> {
358 fn perform(&self, conn: &PgConnection) -> Result<ListCommunitiesResponse, Error> {
359 let data: &ListCommunities = &self.data;
361 let local_only = data.local_only.unwrap_or(false);
362 if Settings::get().federation.enabled && !local_only {
363 return Ok(ListCommunitiesResponse {
364 communities: get_all_communities()?,
368 let user_claims: Option<Claims> = match &data.auth {
369 Some(auth) => match Claims::decode(&auth) {
370 Ok(claims) => Some(claims.claims),
376 let user_id = match &user_claims {
377 Some(claims) => Some(claims.id),
381 let show_nsfw = match &user_claims {
382 Some(claims) => claims.show_nsfw,
386 let sort = SortType::from_str(&data.sort)?;
388 let communities = CommunityQueryBuilder::create(&conn)
391 .show_nsfw(show_nsfw)
397 Ok(ListCommunitiesResponse { communities })
401 impl Perform<CommunityResponse> for Oper<FollowCommunity> {
402 fn perform(&self, conn: &PgConnection) -> Result<CommunityResponse, Error> {
403 let data: &FollowCommunity = &self.data;
405 let claims = match Claims::decode(&data.auth) {
406 Ok(claims) => claims.claims,
407 Err(_e) => return Err(APIError::err("not_logged_in").into()),
410 let user_id = claims.id;
412 let community_follower_form = CommunityFollowerForm {
413 community_id: data.community_id,
418 match CommunityFollower::follow(&conn, &community_follower_form) {
420 Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
423 match CommunityFollower::ignore(&conn, &community_follower_form) {
425 Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
429 let community_view = CommunityView::read(&conn, data.community_id, Some(user_id))?;
431 Ok(CommunityResponse {
432 community: community_view,
437 impl Perform<GetFollowedCommunitiesResponse> for Oper<GetFollowedCommunities> {
438 fn perform(&self, conn: &PgConnection) -> Result<GetFollowedCommunitiesResponse, Error> {
439 let data: &GetFollowedCommunities = &self.data;
441 let claims = match Claims::decode(&data.auth) {
442 Ok(claims) => claims.claims,
443 Err(_e) => return Err(APIError::err("not_logged_in").into()),
446 let user_id = claims.id;
448 let communities: Vec<CommunityFollowerView> =
449 match CommunityFollowerView::for_user(&conn, user_id) {
450 Ok(communities) => communities,
451 Err(_e) => return Err(APIError::err("system_err_login").into()),
455 Ok(GetFollowedCommunitiesResponse { communities })
459 impl Perform<BanFromCommunityResponse> for Oper<BanFromCommunity> {
460 fn perform(&self, conn: &PgConnection) -> Result<BanFromCommunityResponse, Error> {
461 let data: &BanFromCommunity = &self.data;
463 let claims = match Claims::decode(&data.auth) {
464 Ok(claims) => claims.claims,
465 Err(_e) => return Err(APIError::err("not_logged_in").into()),
468 let user_id = claims.id;
470 let community_user_ban_form = CommunityUserBanForm {
471 community_id: data.community_id,
472 user_id: data.user_id,
476 match CommunityUserBan::ban(&conn, &community_user_ban_form) {
478 Err(_e) => return Err(APIError::err("community_user_already_banned").into()),
481 match CommunityUserBan::unban(&conn, &community_user_ban_form) {
483 Err(_e) => return Err(APIError::err("community_user_already_banned").into()),
488 let expires = match data.expires {
489 Some(time) => Some(naive_from_unix(time)),
493 let form = ModBanFromCommunityForm {
494 mod_user_id: user_id,
495 other_user_id: data.user_id,
496 community_id: data.community_id,
497 reason: data.reason.to_owned(),
498 banned: Some(data.ban),
501 ModBanFromCommunity::create(&conn, &form)?;
503 let user_view = UserView::read(&conn, data.user_id)?;
505 Ok(BanFromCommunityResponse {
512 impl Perform<AddModToCommunityResponse> for Oper<AddModToCommunity> {
513 fn perform(&self, conn: &PgConnection) -> Result<AddModToCommunityResponse, Error> {
514 let data: &AddModToCommunity = &self.data;
516 let claims = match Claims::decode(&data.auth) {
517 Ok(claims) => claims.claims,
518 Err(_e) => return Err(APIError::err("not_logged_in").into()),
521 let user_id = claims.id;
523 let community_moderator_form = CommunityModeratorForm {
524 community_id: data.community_id,
525 user_id: data.user_id,
529 match CommunityModerator::join(&conn, &community_moderator_form) {
531 Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
534 match CommunityModerator::leave(&conn, &community_moderator_form) {
536 Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
541 let form = ModAddCommunityForm {
542 mod_user_id: user_id,
543 other_user_id: data.user_id,
544 community_id: data.community_id,
545 removed: Some(!data.added),
547 ModAddCommunity::create(&conn, &form)?;
549 let moderators = CommunityModeratorView::for_community(&conn, data.community_id)?;
551 Ok(AddModToCommunityResponse { moderators })
555 impl Perform<GetCommunityResponse> for Oper<TransferCommunity> {
556 fn perform(&self, conn: &PgConnection) -> Result<GetCommunityResponse, Error> {
557 let data: &TransferCommunity = &self.data;
559 let claims = match Claims::decode(&data.auth) {
560 Ok(claims) => claims.claims,
561 Err(_e) => return Err(APIError::err("not_logged_in").into()),
564 let user_id = claims.id;
566 let read_community = Community::read(&conn, data.community_id)?;
568 let site_creator_id = Site::read(&conn, 1)?.creator_id;
569 let mut admins = UserView::admins(&conn)?;
570 let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
571 let creator_user = admins.remove(creator_index);
572 admins.insert(0, creator_user);
574 // Make sure user is the creator, or an admin
575 if user_id != read_community.creator_id && !admins.iter().map(|a| a.id).any(|x| x == user_id) {
576 return Err(APIError::err("not_an_admin").into());
579 let community_form = CommunityForm {
580 name: read_community.name,
581 title: read_community.title,
582 description: read_community.description,
583 category_id: read_community.category_id,
584 creator_id: data.user_id,
587 nsfw: read_community.nsfw,
588 updated: Some(naive_now()),
589 actor_id: read_community.actor_id,
590 local: read_community.local,
591 private_key: read_community.private_key,
592 public_key: read_community.public_key,
593 last_refreshed_at: None,
596 let _updated_community = match Community::update(&conn, data.community_id, &community_form) {
597 Ok(community) => community,
598 Err(_e) => return Err(APIError::err("couldnt_update_community").into()),
601 // You also have to re-do the community_moderator table, reordering it.
602 let mut community_mods = CommunityModeratorView::for_community(&conn, data.community_id)?;
603 let creator_index = community_mods
605 .position(|r| r.user_id == data.user_id)
607 let creator_user = community_mods.remove(creator_index);
608 community_mods.insert(0, creator_user);
610 CommunityModerator::delete_for_community(&conn, data.community_id)?;
612 for cmod in &community_mods {
613 let community_moderator_form = CommunityModeratorForm {
614 community_id: cmod.community_id,
615 user_id: cmod.user_id,
618 let _inserted_community_moderator =
619 match CommunityModerator::join(&conn, &community_moderator_form) {
621 Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
626 let form = ModAddCommunityForm {
627 mod_user_id: user_id,
628 other_user_id: data.user_id,
629 community_id: data.community_id,
630 removed: Some(false),
632 ModAddCommunity::create(&conn, &form)?;
634 let community_view = match CommunityView::read(&conn, data.community_id, Some(user_id)) {
635 Ok(community) => community,
636 Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
639 let moderators = match CommunityModeratorView::for_community(&conn, data.community_id) {
640 Ok(moderators) => moderators,
641 Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
645 Ok(GetCommunityResponse {
646 community: community_view,