2 use crate::is_valid_community_name;
4 #[derive(Serialize, Deserialize)]
5 pub struct GetCommunity {
11 #[derive(Serialize, Deserialize)]
12 pub struct GetCommunityResponse {
13 pub community: CommunityView,
14 moderators: Vec<CommunityModeratorView>,
15 admins: Vec<UserView>,
19 #[derive(Serialize, Deserialize)]
20 pub struct CreateCommunity {
23 description: Option<String>,
29 #[derive(Serialize, Deserialize, Clone)]
30 pub struct CommunityResponse {
31 pub community: CommunityView,
34 #[derive(Serialize, Deserialize)]
35 pub struct ListCommunities {
42 #[derive(Serialize, Deserialize)]
43 pub struct ListCommunitiesResponse {
44 communities: Vec<CommunityView>,
47 #[derive(Serialize, Deserialize, Clone)]
48 pub struct BanFromCommunity {
49 pub community_id: i32,
52 reason: Option<String>,
57 #[derive(Serialize, Deserialize, Clone)]
58 pub struct BanFromCommunityResponse {
63 #[derive(Serialize, Deserialize)]
64 pub struct AddModToCommunity {
65 pub community_id: i32,
71 #[derive(Serialize, Deserialize, Clone)]
72 pub struct AddModToCommunityResponse {
73 moderators: Vec<CommunityModeratorView>,
76 #[derive(Serialize, Deserialize)]
77 pub struct EditCommunity {
81 description: Option<String>,
83 removed: Option<bool>,
84 deleted: Option<bool>,
86 reason: Option<String>,
91 #[derive(Serialize, Deserialize)]
92 pub struct FollowCommunity {
98 #[derive(Serialize, Deserialize)]
99 pub struct GetFollowedCommunities {
103 #[derive(Serialize, Deserialize)]
104 pub struct GetFollowedCommunitiesResponse {
105 communities: Vec<CommunityFollowerView>,
108 #[derive(Serialize, Deserialize)]
109 pub struct TransferCommunity {
115 impl Perform for Oper<GetCommunity> {
116 type Response = GetCommunityResponse;
120 pool: Pool<ConnectionManager<PgConnection>>,
121 websocket_info: Option<WebsocketInfo>,
122 ) -> Result<GetCommunityResponse, Error> {
123 let data: &GetCommunity = &self.data;
125 let user_id: Option<i32> = match &data.auth {
126 Some(auth) => match Claims::decode(&auth) {
128 let user_id = claims.claims.id;
136 let conn = pool.get()?;
138 let community_id = match data.id {
141 match Community::read_from_name(
143 data.name.to_owned().unwrap_or_else(|| "main".to_string()),
145 Ok(community) => community.id,
146 Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
151 let community_view = match CommunityView::read(&conn, community_id, user_id) {
152 Ok(community) => community,
153 Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
156 let moderators = match CommunityModeratorView::for_community(&conn, community_id) {
157 Ok(moderators) => moderators,
158 Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
161 let site_creator_id = Site::read(&conn, 1)?.creator_id;
162 let mut admins = UserView::admins(&conn)?;
163 let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
164 let creator_user = admins.remove(creator_index);
165 admins.insert(0, creator_user);
167 let online = if let Some(ws) = websocket_info {
168 if let Some(id) = ws.id {
170 .do_send(JoinCommunityRoom { community_id, id });
176 // ws.chatserver.send(GetCommunityUsersOnline {community_id}).await.unwrap()
178 // Runtime::new().unwrap().block_on(fut)
183 let res = GetCommunityResponse {
184 community: community_view,
195 impl Perform for Oper<CreateCommunity> {
196 type Response = CommunityResponse;
200 pool: Pool<ConnectionManager<PgConnection>>,
201 _websocket_info: Option<WebsocketInfo>,
202 ) -> Result<CommunityResponse, Error> {
203 let data: &CreateCommunity = &self.data;
205 let claims = match Claims::decode(&data.auth) {
206 Ok(claims) => claims.claims,
207 Err(_e) => return Err(APIError::err("not_logged_in").into()),
210 if let Err(slurs) = slur_check(&data.name) {
211 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
214 if let Err(slurs) = slur_check(&data.title) {
215 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
218 if let Some(description) = &data.description {
219 if let Err(slurs) = slur_check(description) {
220 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
224 if !is_valid_community_name(&data.name) {
225 return Err(APIError::err("invalid_community_name").into());
228 let user_id = claims.id;
230 let conn = pool.get()?;
232 // Check for a site ban
233 if UserView::read(&conn, user_id)?.banned {
234 return Err(APIError::err("site_ban").into());
237 // When you create a community, make sure the user becomes a moderator and a follower
238 let community_form = CommunityForm {
239 name: data.name.to_owned(),
240 title: data.title.to_owned(),
241 description: data.description.to_owned(),
242 category_id: data.category_id,
250 let inserted_community = match Community::create(&conn, &community_form) {
251 Ok(community) => community,
252 Err(_e) => return Err(APIError::err("community_already_exists").into()),
255 let community_moderator_form = CommunityModeratorForm {
256 community_id: inserted_community.id,
260 let _inserted_community_moderator =
261 match CommunityModerator::join(&conn, &community_moderator_form) {
263 Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
266 let community_follower_form = CommunityFollowerForm {
267 community_id: inserted_community.id,
271 let _inserted_community_follower =
272 match CommunityFollower::follow(&conn, &community_follower_form) {
274 Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
277 let community_view = CommunityView::read(&conn, inserted_community.id, Some(user_id))?;
279 Ok(CommunityResponse {
280 community: community_view,
285 impl Perform for Oper<EditCommunity> {
286 type Response = CommunityResponse;
290 pool: Pool<ConnectionManager<PgConnection>>,
291 websocket_info: Option<WebsocketInfo>,
292 ) -> Result<CommunityResponse, Error> {
293 let data: &EditCommunity = &self.data;
295 if let Err(slurs) = slur_check(&data.name) {
296 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
299 if let Err(slurs) = slur_check(&data.title) {
300 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
303 if let Some(description) = &data.description {
304 if let Err(slurs) = slur_check(description) {
305 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
309 let claims = match Claims::decode(&data.auth) {
310 Ok(claims) => claims.claims,
311 Err(_e) => return Err(APIError::err("not_logged_in").into()),
314 if !is_valid_community_name(&data.name) {
315 return Err(APIError::err("invalid_community_name").into());
318 let user_id = claims.id;
320 let conn = pool.get()?;
322 // Check for a site ban
323 if UserView::read(&conn, user_id)?.banned {
324 return Err(APIError::err("site_ban").into());
328 let mut editors: Vec<i32> = Vec::new();
330 &mut CommunityModeratorView::for_community(&conn, data.edit_id)?
335 editors.append(&mut UserView::admins(&conn)?.into_iter().map(|a| a.id).collect());
336 if !editors.contains(&user_id) {
337 return Err(APIError::err("no_community_edit_allowed").into());
340 let community_form = CommunityForm {
341 name: data.name.to_owned(),
342 title: data.title.to_owned(),
343 description: data.description.to_owned(),
344 category_id: data.category_id.to_owned(),
346 removed: data.removed.to_owned(),
347 deleted: data.deleted.to_owned(),
349 updated: Some(naive_now()),
352 let _updated_community = match Community::update(&conn, data.edit_id, &community_form) {
353 Ok(community) => community,
354 Err(_e) => return Err(APIError::err("couldnt_update_community").into()),
358 if let Some(removed) = data.removed.to_owned() {
359 let expires = match data.expires {
360 Some(time) => Some(naive_from_unix(time)),
363 let form = ModRemoveCommunityForm {
364 mod_user_id: user_id,
365 community_id: data.edit_id,
366 removed: Some(removed),
367 reason: data.reason.to_owned(),
370 ModRemoveCommunity::create(&conn, &form)?;
373 let community_view = CommunityView::read(&conn, data.edit_id, Some(user_id))?;
375 let res = CommunityResponse {
376 community: community_view,
379 if let Some(ws) = websocket_info {
380 // Strip out the user id and subscribed when sending to others
381 let mut res_sent = res.clone();
382 res_sent.community.user_id = None;
383 res_sent.community.subscribed = None;
385 ws.chatserver.do_send(SendCommunityRoomMessage {
386 op: UserOperation::EditCommunity,
388 community_id: data.edit_id,
397 impl Perform for Oper<ListCommunities> {
398 type Response = ListCommunitiesResponse;
402 pool: Pool<ConnectionManager<PgConnection>>,
403 _websocket_info: Option<WebsocketInfo>,
404 ) -> Result<ListCommunitiesResponse, Error> {
405 let data: &ListCommunities = &self.data;
407 let user_claims: Option<Claims> = match &data.auth {
408 Some(auth) => match Claims::decode(&auth) {
409 Ok(claims) => Some(claims.claims),
415 let user_id = match &user_claims {
416 Some(claims) => Some(claims.id),
420 let show_nsfw = match &user_claims {
421 Some(claims) => claims.show_nsfw,
425 let sort = SortType::from_str(&data.sort)?;
427 let conn = pool.get()?;
429 let communities = CommunityQueryBuilder::create(&conn)
432 .show_nsfw(show_nsfw)
438 Ok(ListCommunitiesResponse { communities })
442 impl Perform for Oper<FollowCommunity> {
443 type Response = CommunityResponse;
447 pool: Pool<ConnectionManager<PgConnection>>,
448 _websocket_info: Option<WebsocketInfo>,
449 ) -> Result<CommunityResponse, Error> {
450 let data: &FollowCommunity = &self.data;
452 let claims = match Claims::decode(&data.auth) {
453 Ok(claims) => claims.claims,
454 Err(_e) => return Err(APIError::err("not_logged_in").into()),
457 let user_id = claims.id;
459 let community_follower_form = CommunityFollowerForm {
460 community_id: data.community_id,
464 let conn = pool.get()?;
467 match CommunityFollower::follow(&conn, &community_follower_form) {
469 Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
472 match CommunityFollower::ignore(&conn, &community_follower_form) {
474 Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
478 let community_view = CommunityView::read(&conn, data.community_id, Some(user_id))?;
480 Ok(CommunityResponse {
481 community: community_view,
486 impl Perform for Oper<GetFollowedCommunities> {
487 type Response = GetFollowedCommunitiesResponse;
491 pool: Pool<ConnectionManager<PgConnection>>,
492 _websocket_info: Option<WebsocketInfo>,
493 ) -> Result<GetFollowedCommunitiesResponse, Error> {
494 let data: &GetFollowedCommunities = &self.data;
496 let claims = match Claims::decode(&data.auth) {
497 Ok(claims) => claims.claims,
498 Err(_e) => return Err(APIError::err("not_logged_in").into()),
501 let user_id = claims.id;
503 let conn = pool.get()?;
505 let communities: Vec<CommunityFollowerView> =
506 match CommunityFollowerView::for_user(&conn, user_id) {
507 Ok(communities) => communities,
508 Err(_e) => return Err(APIError::err("system_err_login").into()),
512 Ok(GetFollowedCommunitiesResponse { communities })
516 impl Perform for Oper<BanFromCommunity> {
517 type Response = BanFromCommunityResponse;
521 pool: Pool<ConnectionManager<PgConnection>>,
522 websocket_info: Option<WebsocketInfo>,
523 ) -> Result<BanFromCommunityResponse, Error> {
524 let data: &BanFromCommunity = &self.data;
526 let claims = match Claims::decode(&data.auth) {
527 Ok(claims) => claims.claims,
528 Err(_e) => return Err(APIError::err("not_logged_in").into()),
531 let user_id = claims.id;
533 let community_user_ban_form = CommunityUserBanForm {
534 community_id: data.community_id,
535 user_id: data.user_id,
538 let conn = pool.get()?;
541 match CommunityUserBan::ban(&conn, &community_user_ban_form) {
543 Err(_e) => return Err(APIError::err("community_user_already_banned").into()),
546 match CommunityUserBan::unban(&conn, &community_user_ban_form) {
548 Err(_e) => return Err(APIError::err("community_user_already_banned").into()),
553 let expires = match data.expires {
554 Some(time) => Some(naive_from_unix(time)),
558 let form = ModBanFromCommunityForm {
559 mod_user_id: user_id,
560 other_user_id: data.user_id,
561 community_id: data.community_id,
562 reason: data.reason.to_owned(),
563 banned: Some(data.ban),
566 ModBanFromCommunity::create(&conn, &form)?;
568 let user_view = UserView::read(&conn, data.user_id)?;
570 let res = BanFromCommunityResponse {
575 if let Some(ws) = websocket_info {
576 ws.chatserver.do_send(SendCommunityRoomMessage {
577 op: UserOperation::BanFromCommunity,
578 response: res.clone(),
579 community_id: data.community_id,
588 impl Perform for Oper<AddModToCommunity> {
589 type Response = AddModToCommunityResponse;
593 pool: Pool<ConnectionManager<PgConnection>>,
594 websocket_info: Option<WebsocketInfo>,
595 ) -> Result<AddModToCommunityResponse, Error> {
596 let data: &AddModToCommunity = &self.data;
598 let claims = match Claims::decode(&data.auth) {
599 Ok(claims) => claims.claims,
600 Err(_e) => return Err(APIError::err("not_logged_in").into()),
603 let user_id = claims.id;
605 let community_moderator_form = CommunityModeratorForm {
606 community_id: data.community_id,
607 user_id: data.user_id,
610 let conn = pool.get()?;
613 match CommunityModerator::join(&conn, &community_moderator_form) {
615 Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
618 match CommunityModerator::leave(&conn, &community_moderator_form) {
620 Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
625 let form = ModAddCommunityForm {
626 mod_user_id: user_id,
627 other_user_id: data.user_id,
628 community_id: data.community_id,
629 removed: Some(!data.added),
631 ModAddCommunity::create(&conn, &form)?;
633 let moderators = CommunityModeratorView::for_community(&conn, data.community_id)?;
635 let res = AddModToCommunityResponse { moderators };
637 if let Some(ws) = websocket_info {
638 ws.chatserver.do_send(SendCommunityRoomMessage {
639 op: UserOperation::AddModToCommunity,
640 response: res.clone(),
641 community_id: data.community_id,
650 impl Perform for Oper<TransferCommunity> {
651 type Response = GetCommunityResponse;
655 pool: Pool<ConnectionManager<PgConnection>>,
656 _websocket_info: Option<WebsocketInfo>,
657 ) -> Result<GetCommunityResponse, Error> {
658 let data: &TransferCommunity = &self.data;
660 let claims = match Claims::decode(&data.auth) {
661 Ok(claims) => claims.claims,
662 Err(_e) => return Err(APIError::err("not_logged_in").into()),
665 let user_id = claims.id;
667 let conn = pool.get()?;
669 let read_community = Community::read(&conn, data.community_id)?;
671 let site_creator_id = Site::read(&conn, 1)?.creator_id;
672 let mut admins = UserView::admins(&conn)?;
673 let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
674 let creator_user = admins.remove(creator_index);
675 admins.insert(0, creator_user);
677 // Make sure user is the creator, or an admin
678 if user_id != read_community.creator_id && !admins.iter().map(|a| a.id).any(|x| x == user_id) {
679 return Err(APIError::err("not_an_admin").into());
682 let community_form = CommunityForm {
683 name: read_community.name,
684 title: read_community.title,
685 description: read_community.description,
686 category_id: read_community.category_id,
687 creator_id: data.user_id,
690 nsfw: read_community.nsfw,
691 updated: Some(naive_now()),
694 let _updated_community = match Community::update(&conn, data.community_id, &community_form) {
695 Ok(community) => community,
696 Err(_e) => return Err(APIError::err("couldnt_update_community").into()),
699 // You also have to re-do the community_moderator table, reordering it.
700 let mut community_mods = CommunityModeratorView::for_community(&conn, data.community_id)?;
701 let creator_index = community_mods
703 .position(|r| r.user_id == data.user_id)
705 let creator_user = community_mods.remove(creator_index);
706 community_mods.insert(0, creator_user);
708 CommunityModerator::delete_for_community(&conn, data.community_id)?;
710 for cmod in &community_mods {
711 let community_moderator_form = CommunityModeratorForm {
712 community_id: cmod.community_id,
713 user_id: cmod.user_id,
716 let _inserted_community_moderator =
717 match CommunityModerator::join(&conn, &community_moderator_form) {
719 Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
724 let form = ModAddCommunityForm {
725 mod_user_id: user_id,
726 other_user_id: data.user_id,
727 community_id: data.community_id,
728 removed: Some(false),
730 ModAddCommunity::create(&conn, &form)?;
732 let community_view = match CommunityView::read(&conn, data.community_id, Some(user_id)) {
733 Ok(community) => community,
734 Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
737 let moderators = match CommunityModeratorView::for_community(&conn, data.community_id) {
738 Ok(moderators) => moderators,
739 Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
743 Ok(GetCommunityResponse {
744 community: community_view,