2 use crate::apub::activities::follow_community;
3 use crate::apub::{format_community_name, gen_keypair_str, make_apub_endpoint, EndpointType};
4 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>,
46 #[derive(Serialize, Deserialize, Debug)]
47 pub struct ListCommunitiesResponse {
48 pub communities: Vec<CommunityView>,
51 #[derive(Serialize, Deserialize, Clone)]
52 pub struct BanFromCommunity {
53 pub community_id: i32,
56 reason: Option<String>,
61 #[derive(Serialize, Deserialize)]
62 pub struct BanFromCommunityResponse {
67 #[derive(Serialize, Deserialize)]
68 pub struct AddModToCommunity {
69 pub community_id: i32,
75 #[derive(Serialize, Deserialize)]
76 pub struct AddModToCommunityResponse {
77 moderators: Vec<CommunityModeratorView>,
80 #[derive(Serialize, Deserialize)]
81 pub struct EditCommunity {
85 description: Option<String>,
87 removed: Option<bool>,
88 deleted: Option<bool>,
90 reason: Option<String>,
95 #[derive(Serialize, Deserialize)]
96 pub struct FollowCommunity {
102 #[derive(Serialize, Deserialize)]
103 pub struct GetFollowedCommunities {
107 #[derive(Serialize, Deserialize)]
108 pub struct GetFollowedCommunitiesResponse {
109 communities: Vec<CommunityFollowerView>,
112 #[derive(Serialize, Deserialize)]
113 pub struct TransferCommunity {
119 impl Perform<GetCommunityResponse> for Oper<GetCommunity> {
120 fn perform(&self, conn: &PgConnection) -> Result<GetCommunityResponse, Error> {
121 let data: &GetCommunity = &self.data;
123 let user_id: Option<i32> = match &data.auth {
124 Some(auth) => match Claims::decode(&auth) {
126 let user_id = claims.claims.id;
134 let community = match data.id {
135 Some(id) => Community::read(&conn, id)?,
137 match Community::read_from_name(
139 data.name.to_owned().unwrap_or_else(|| "main".to_string()),
141 Ok(community) => community,
142 Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
147 let mut community_view = match CommunityView::read(&conn, community.id, user_id) {
148 Ok(community) => community,
149 Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
152 let moderators = match CommunityModeratorView::for_community(&conn, community.id) {
153 Ok(moderators) => moderators,
154 Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
157 let site_creator_id = Site::read(&conn, 1)?.creator_id;
158 let mut admins = UserView::admins(&conn)?;
159 let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
160 let creator_user = admins.remove(creator_index);
161 admins.insert(0, creator_user);
163 if !community.local {
164 let domain = Url::parse(&community.actor_id)?;
165 community_view.name =
166 format_community_name(&community_view.name.to_string(), domain.host_str().unwrap());
170 Ok(GetCommunityResponse {
171 community: community_view,
179 impl Perform<CommunityResponse> for Oper<CreateCommunity> {
180 fn perform(&self, conn: &PgConnection) -> Result<CommunityResponse, Error> {
181 let data: &CreateCommunity = &self.data;
183 let claims = match Claims::decode(&data.auth) {
184 Ok(claims) => claims.claims,
185 Err(_e) => return Err(APIError::err("not_logged_in").into()),
188 if let Err(slurs) = slur_check(&data.name) {
189 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
192 if let Err(slurs) = slur_check(&data.title) {
193 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
196 if let Some(description) = &data.description {
197 if let Err(slurs) = slur_check(description) {
198 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
202 let user_id = claims.id;
204 // Check for a site ban
205 if UserView::read(&conn, user_id)?.banned {
206 return Err(APIError::err("site_ban").into());
209 // When you create a community, make sure the user becomes a moderator and a follower
210 let (community_public_key, community_private_key) = gen_keypair_str();
212 let community_form = CommunityForm {
213 name: data.name.to_owned(),
214 title: data.title.to_owned(),
215 description: data.description.to_owned(),
216 category_id: data.category_id,
222 actor_id: make_apub_endpoint(EndpointType::Community, &data.name).to_string(),
224 private_key: Some(community_private_key),
225 public_key: Some(community_public_key),
226 last_refreshed_at: None,
230 let inserted_community = match Community::create(&conn, &community_form) {
231 Ok(community) => community,
232 Err(_e) => return Err(APIError::err("community_already_exists").into()),
235 let community_moderator_form = CommunityModeratorForm {
236 community_id: inserted_community.id,
240 let _inserted_community_moderator =
241 match CommunityModerator::join(&conn, &community_moderator_form) {
243 Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
246 let community_follower_form = CommunityFollowerForm {
247 community_id: inserted_community.id,
251 let _inserted_community_follower =
252 match CommunityFollower::follow(&conn, &community_follower_form) {
254 Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
257 let community_view = CommunityView::read(&conn, inserted_community.id, Some(user_id))?;
259 Ok(CommunityResponse {
260 community: community_view,
265 impl Perform<CommunityResponse> for Oper<EditCommunity> {
266 fn perform(&self, conn: &PgConnection) -> Result<CommunityResponse, Error> {
267 let data: &EditCommunity = &self.data;
269 if let Err(slurs) = slur_check(&data.name) {
270 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
273 if let Err(slurs) = slur_check(&data.title) {
274 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
277 if let Some(description) = &data.description {
278 if let Err(slurs) = slur_check(description) {
279 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
283 let claims = match Claims::decode(&data.auth) {
284 Ok(claims) => claims.claims,
285 Err(_e) => return Err(APIError::err("not_logged_in").into()),
288 let user_id = claims.id;
290 // Check for a site ban
291 if UserView::read(&conn, user_id)?.banned {
292 return Err(APIError::err("site_ban").into());
296 let mut editors: Vec<i32> = Vec::new();
298 &mut CommunityModeratorView::for_community(&conn, data.edit_id)?
303 editors.append(&mut UserView::admins(&conn)?.into_iter().map(|a| a.id).collect());
304 if !editors.contains(&user_id) {
305 return Err(APIError::err("no_community_edit_allowed").into());
308 let read_community = Community::read(&conn, data.edit_id)?;
310 let community_form = CommunityForm {
311 name: data.name.to_owned(),
312 title: data.title.to_owned(),
313 description: data.description.to_owned(),
314 category_id: data.category_id.to_owned(),
316 removed: data.removed.to_owned(),
317 deleted: data.deleted.to_owned(),
319 updated: Some(naive_now()),
320 actor_id: read_community.actor_id,
321 local: read_community.local,
322 private_key: read_community.private_key,
323 public_key: read_community.public_key,
324 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 user_claims: Option<Claims> = match &data.auth {
362 Some(auth) => match Claims::decode(&auth) {
363 Ok(claims) => Some(claims.claims),
369 let user_id = match &user_claims {
370 Some(claims) => Some(claims.id),
374 let show_nsfw = match &user_claims {
375 Some(claims) => claims.show_nsfw,
379 let sort = SortType::from_str(&data.sort)?;
381 let communities = CommunityQueryBuilder::create(&conn)
384 .show_nsfw(show_nsfw)
390 Ok(ListCommunitiesResponse { communities })
394 impl Perform<CommunityResponse> for Oper<FollowCommunity> {
395 fn perform(&self, conn: &PgConnection) -> Result<CommunityResponse, Error> {
396 let data: &FollowCommunity = &self.data;
398 let claims = match Claims::decode(&data.auth) {
399 Ok(claims) => claims.claims,
400 Err(_e) => return Err(APIError::err("not_logged_in").into()),
403 let user_id = claims.id;
405 let community = Community::read(conn, data.community_id)?;
407 let community_follower_form = CommunityFollowerForm {
408 community_id: data.community_id,
413 match CommunityFollower::follow(&conn, &community_follower_form) {
415 Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
418 match CommunityFollower::ignore(&conn, &community_follower_form) {
420 Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
424 // TODO: still have to implement unfollow
425 let user = User_::read(conn, user_id)?;
426 follow_community(&community, &user, conn)?;
427 // TODO: this needs to return a "pending" state, until Accept is received from the remote server
430 let community_view = CommunityView::read(&conn, data.community_id, Some(user_id))?;
432 Ok(CommunityResponse {
433 community: community_view,
438 impl Perform<GetFollowedCommunitiesResponse> for Oper<GetFollowedCommunities> {
439 fn perform(&self, conn: &PgConnection) -> Result<GetFollowedCommunitiesResponse, Error> {
440 let data: &GetFollowedCommunities = &self.data;
442 let claims = match Claims::decode(&data.auth) {
443 Ok(claims) => claims.claims,
444 Err(_e) => return Err(APIError::err("not_logged_in").into()),
447 let user_id = claims.id;
449 let communities: Vec<CommunityFollowerView> =
450 match CommunityFollowerView::for_user(&conn, user_id) {
451 Ok(communities) => communities,
452 Err(_e) => return Err(APIError::err("system_err_login").into()),
456 Ok(GetFollowedCommunitiesResponse { communities })
460 impl Perform<BanFromCommunityResponse> for Oper<BanFromCommunity> {
461 fn perform(&self, conn: &PgConnection) -> Result<BanFromCommunityResponse, Error> {
462 let data: &BanFromCommunity = &self.data;
464 let claims = match Claims::decode(&data.auth) {
465 Ok(claims) => claims.claims,
466 Err(_e) => return Err(APIError::err("not_logged_in").into()),
469 let user_id = claims.id;
471 let community_user_ban_form = CommunityUserBanForm {
472 community_id: data.community_id,
473 user_id: data.user_id,
477 match CommunityUserBan::ban(&conn, &community_user_ban_form) {
479 Err(_e) => return Err(APIError::err("community_user_already_banned").into()),
482 match CommunityUserBan::unban(&conn, &community_user_ban_form) {
484 Err(_e) => return Err(APIError::err("community_user_already_banned").into()),
489 let expires = match data.expires {
490 Some(time) => Some(naive_from_unix(time)),
494 let form = ModBanFromCommunityForm {
495 mod_user_id: user_id,
496 other_user_id: data.user_id,
497 community_id: data.community_id,
498 reason: data.reason.to_owned(),
499 banned: Some(data.ban),
502 ModBanFromCommunity::create(&conn, &form)?;
504 let user_view = UserView::read(&conn, data.user_id)?;
506 Ok(BanFromCommunityResponse {
513 impl Perform<AddModToCommunityResponse> for Oper<AddModToCommunity> {
514 fn perform(&self, conn: &PgConnection) -> Result<AddModToCommunityResponse, Error> {
515 let data: &AddModToCommunity = &self.data;
517 let claims = match Claims::decode(&data.auth) {
518 Ok(claims) => claims.claims,
519 Err(_e) => return Err(APIError::err("not_logged_in").into()),
522 let user_id = claims.id;
524 let community_moderator_form = CommunityModeratorForm {
525 community_id: data.community_id,
526 user_id: data.user_id,
530 match CommunityModerator::join(&conn, &community_moderator_form) {
532 Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
535 match CommunityModerator::leave(&conn, &community_moderator_form) {
537 Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
542 let form = ModAddCommunityForm {
543 mod_user_id: user_id,
544 other_user_id: data.user_id,
545 community_id: data.community_id,
546 removed: Some(!data.added),
548 ModAddCommunity::create(&conn, &form)?;
550 let moderators = CommunityModeratorView::for_community(&conn, data.community_id)?;
552 Ok(AddModToCommunityResponse { moderators })
556 impl Perform<GetCommunityResponse> for Oper<TransferCommunity> {
557 fn perform(&self, conn: &PgConnection) -> Result<GetCommunityResponse, Error> {
558 let data: &TransferCommunity = &self.data;
560 let claims = match Claims::decode(&data.auth) {
561 Ok(claims) => claims.claims,
562 Err(_e) => return Err(APIError::err("not_logged_in").into()),
565 let user_id = claims.id;
567 let read_community = Community::read(&conn, data.community_id)?;
569 let site_creator_id = Site::read(&conn, 1)?.creator_id;
570 let mut admins = UserView::admins(&conn)?;
571 let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
572 let creator_user = admins.remove(creator_index);
573 admins.insert(0, creator_user);
575 // Make sure user is the creator, or an admin
576 if user_id != read_community.creator_id && !admins.iter().map(|a| a.id).any(|x| x == user_id) {
577 return Err(APIError::err("not_an_admin").into());
580 let community_form = CommunityForm {
581 name: read_community.name,
582 title: read_community.title,
583 description: read_community.description,
584 category_id: read_community.category_id,
585 creator_id: data.user_id,
588 nsfw: read_community.nsfw,
589 updated: Some(naive_now()),
590 actor_id: read_community.actor_id,
591 local: read_community.local,
592 private_key: read_community.private_key,
593 public_key: read_community.public_key,
594 last_refreshed_at: None,
598 let _updated_community = match Community::update(&conn, data.community_id, &community_form) {
599 Ok(community) => community,
600 Err(_e) => return Err(APIError::err("couldnt_update_community").into()),
603 // You also have to re-do the community_moderator table, reordering it.
604 let mut community_mods = CommunityModeratorView::for_community(&conn, data.community_id)?;
605 let creator_index = community_mods
607 .position(|r| r.user_id == data.user_id)
609 let creator_user = community_mods.remove(creator_index);
610 community_mods.insert(0, creator_user);
612 CommunityModerator::delete_for_community(&conn, data.community_id)?;
614 for cmod in &community_mods {
615 let community_moderator_form = CommunityModeratorForm {
616 community_id: cmod.community_id,
617 user_id: cmod.user_id,
620 let _inserted_community_moderator =
621 match CommunityModerator::join(&conn, &community_moderator_form) {
623 Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
628 let form = ModAddCommunityForm {
629 mod_user_id: user_id,
630 other_user_id: data.user_id,
631 community_id: data.community_id,
632 removed: Some(false),
634 ModAddCommunity::create(&conn, &form)?;
636 let community_view = match CommunityView::read(&conn, data.community_id, Some(user_id)) {
637 Ok(community) => community,
638 Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
641 let moderators = match CommunityModeratorView::for_community(&conn, data.community_id) {
642 Ok(moderators) => moderators,
643 Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
647 Ok(GetCommunityResponse {
648 community: community_view,