3 #[derive(Serialize, Deserialize)]
4 pub struct GetCommunity {
10 #[derive(Serialize, Deserialize)]
11 pub struct GetCommunityResponse {
12 pub community: CommunityView,
13 moderators: Vec<CommunityModeratorView>,
14 admins: Vec<UserView>,
18 #[derive(Serialize, Deserialize)]
19 pub struct CreateCommunity {
22 description: Option<String>,
28 #[derive(Serialize, Deserialize, Clone)]
29 pub struct CommunityResponse {
30 pub community: CommunityView,
33 #[derive(Serialize, Deserialize)]
34 pub struct ListCommunities {
41 #[derive(Serialize, Deserialize)]
42 pub struct ListCommunitiesResponse {
43 communities: Vec<CommunityView>,
46 #[derive(Serialize, Deserialize, Clone)]
47 pub struct BanFromCommunity {
48 pub community_id: i32,
51 reason: Option<String>,
56 #[derive(Serialize, Deserialize, Clone)]
57 pub struct BanFromCommunityResponse {
62 #[derive(Serialize, Deserialize)]
63 pub struct AddModToCommunity {
64 pub community_id: i32,
70 #[derive(Serialize, Deserialize, Clone)]
71 pub struct AddModToCommunityResponse {
72 moderators: Vec<CommunityModeratorView>,
75 #[derive(Serialize, Deserialize)]
76 pub struct EditCommunity {
80 description: Option<String>,
82 removed: Option<bool>,
83 deleted: Option<bool>,
85 reason: Option<String>,
90 #[derive(Serialize, Deserialize)]
91 pub struct FollowCommunity {
97 #[derive(Serialize, Deserialize)]
98 pub struct GetFollowedCommunities {
102 #[derive(Serialize, Deserialize)]
103 pub struct GetFollowedCommunitiesResponse {
104 communities: Vec<CommunityFollowerView>,
107 #[derive(Serialize, Deserialize)]
108 pub struct TransferCommunity {
114 impl Perform for Oper<GetCommunity> {
115 type Response = GetCommunityResponse;
119 pool: Pool<ConnectionManager<PgConnection>>,
120 websocket_info: Option<WebsocketInfo>,
121 ) -> Result<GetCommunityResponse, Error> {
122 let data: &GetCommunity = &self.data;
124 let user_id: Option<i32> = match &data.auth {
125 Some(auth) => match Claims::decode(&auth) {
127 let user_id = claims.claims.id;
135 let conn = pool.get()?;
137 let community_id = match data.id {
140 match Community::read_from_name(
142 data.name.to_owned().unwrap_or_else(|| "main".to_string()),
144 Ok(community) => community.id,
145 Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
150 let community_view = match CommunityView::read(&conn, community_id, user_id) {
151 Ok(community) => community,
152 Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
155 let moderators = match CommunityModeratorView::for_community(&conn, community_id) {
156 Ok(moderators) => moderators,
157 Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
160 let site_creator_id = Site::read(&conn, 1)?.creator_id;
161 let mut admins = UserView::admins(&conn)?;
162 let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
163 let creator_user = admins.remove(creator_index);
164 admins.insert(0, creator_user);
166 let online = if let Some(ws) = websocket_info {
167 if let Some(id) = ws.id {
169 .do_send(JoinCommunityRoom { community_id, id });
175 // ws.chatserver.send(GetCommunityUsersOnline {community_id}).await.unwrap()
177 // Runtime::new().unwrap().block_on(fut)
182 let res = GetCommunityResponse {
183 community: community_view,
194 impl Perform for Oper<CreateCommunity> {
195 type Response = CommunityResponse;
199 pool: Pool<ConnectionManager<PgConnection>>,
200 _websocket_info: Option<WebsocketInfo>,
201 ) -> Result<CommunityResponse, Error> {
202 let data: &CreateCommunity = &self.data;
204 let claims = match Claims::decode(&data.auth) {
205 Ok(claims) => claims.claims,
206 Err(_e) => return Err(APIError::err("not_logged_in").into()),
209 if let Err(slurs) = slur_check(&data.name) {
210 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
213 if let Err(slurs) = slur_check(&data.title) {
214 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
217 if let Some(description) = &data.description {
218 if let Err(slurs) = slur_check(description) {
219 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
223 let user_id = claims.id;
225 let conn = pool.get()?;
227 // Check for a site ban
228 if UserView::read(&conn, user_id)?.banned {
229 return Err(APIError::err("site_ban").into());
232 // When you create a community, make sure the user becomes a moderator and a follower
233 let community_form = CommunityForm {
234 name: data.name.to_owned(),
235 title: data.title.to_owned(),
236 description: data.description.to_owned(),
237 category_id: data.category_id,
245 let inserted_community = match Community::create(&conn, &community_form) {
246 Ok(community) => community,
247 Err(_e) => return Err(APIError::err("community_already_exists").into()),
250 let community_moderator_form = CommunityModeratorForm {
251 community_id: inserted_community.id,
255 let _inserted_community_moderator =
256 match CommunityModerator::join(&conn, &community_moderator_form) {
258 Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
261 let community_follower_form = CommunityFollowerForm {
262 community_id: inserted_community.id,
266 let _inserted_community_follower =
267 match CommunityFollower::follow(&conn, &community_follower_form) {
269 Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
272 let community_view = CommunityView::read(&conn, inserted_community.id, Some(user_id))?;
274 Ok(CommunityResponse {
275 community: community_view,
280 impl Perform for Oper<EditCommunity> {
281 type Response = CommunityResponse;
285 pool: Pool<ConnectionManager<PgConnection>>,
286 websocket_info: Option<WebsocketInfo>,
287 ) -> Result<CommunityResponse, Error> {
288 let data: &EditCommunity = &self.data;
290 if let Err(slurs) = slur_check(&data.name) {
291 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
294 if let Err(slurs) = slur_check(&data.title) {
295 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
298 if let Some(description) = &data.description {
299 if let Err(slurs) = slur_check(description) {
300 return Err(APIError::err(&slurs_vec_to_str(slurs)).into());
304 let claims = match Claims::decode(&data.auth) {
305 Ok(claims) => claims.claims,
306 Err(_e) => return Err(APIError::err("not_logged_in").into()),
309 let user_id = claims.id;
311 let conn = pool.get()?;
313 // Check for a site ban
314 if UserView::read(&conn, user_id)?.banned {
315 return Err(APIError::err("site_ban").into());
319 let mut editors: Vec<i32> = Vec::new();
321 &mut CommunityModeratorView::for_community(&conn, data.edit_id)?
326 editors.append(&mut UserView::admins(&conn)?.into_iter().map(|a| a.id).collect());
327 if !editors.contains(&user_id) {
328 return Err(APIError::err("no_community_edit_allowed").into());
331 let community_form = CommunityForm {
332 name: data.name.to_owned(),
333 title: data.title.to_owned(),
334 description: data.description.to_owned(),
335 category_id: data.category_id.to_owned(),
337 removed: data.removed.to_owned(),
338 deleted: data.deleted.to_owned(),
340 updated: Some(naive_now()),
343 let _updated_community = match Community::update(&conn, data.edit_id, &community_form) {
344 Ok(community) => community,
345 Err(_e) => return Err(APIError::err("couldnt_update_community").into()),
349 if let Some(removed) = data.removed.to_owned() {
350 let expires = match data.expires {
351 Some(time) => Some(naive_from_unix(time)),
354 let form = ModRemoveCommunityForm {
355 mod_user_id: user_id,
356 community_id: data.edit_id,
357 removed: Some(removed),
358 reason: data.reason.to_owned(),
361 ModRemoveCommunity::create(&conn, &form)?;
364 let community_view = CommunityView::read(&conn, data.edit_id, Some(user_id))?;
366 let res = CommunityResponse {
367 community: community_view,
370 if let Some(ws) = websocket_info {
371 // Strip out the user id and subscribed when sending to others
372 let mut res_sent = res.clone();
373 res_sent.community.user_id = None;
374 res_sent.community.subscribed = None;
376 ws.chatserver.do_send(SendCommunityRoomMessage {
377 op: UserOperation::EditCommunity,
379 community_id: data.edit_id,
388 impl Perform for Oper<ListCommunities> {
389 type Response = ListCommunitiesResponse;
393 pool: Pool<ConnectionManager<PgConnection>>,
394 _websocket_info: Option<WebsocketInfo>,
395 ) -> Result<ListCommunitiesResponse, Error> {
396 let data: &ListCommunities = &self.data;
398 let user_claims: Option<Claims> = match &data.auth {
399 Some(auth) => match Claims::decode(&auth) {
400 Ok(claims) => Some(claims.claims),
406 let user_id = match &user_claims {
407 Some(claims) => Some(claims.id),
411 let show_nsfw = match &user_claims {
412 Some(claims) => claims.show_nsfw,
416 let sort = SortType::from_str(&data.sort)?;
418 let conn = pool.get()?;
420 let communities = CommunityQueryBuilder::create(&conn)
423 .show_nsfw(show_nsfw)
429 Ok(ListCommunitiesResponse { communities })
433 impl Perform for Oper<FollowCommunity> {
434 type Response = CommunityResponse;
438 pool: Pool<ConnectionManager<PgConnection>>,
439 _websocket_info: Option<WebsocketInfo>,
440 ) -> Result<CommunityResponse, Error> {
441 let data: &FollowCommunity = &self.data;
443 let claims = match Claims::decode(&data.auth) {
444 Ok(claims) => claims.claims,
445 Err(_e) => return Err(APIError::err("not_logged_in").into()),
448 let user_id = claims.id;
450 let community_follower_form = CommunityFollowerForm {
451 community_id: data.community_id,
455 let conn = pool.get()?;
458 match CommunityFollower::follow(&conn, &community_follower_form) {
460 Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
463 match CommunityFollower::ignore(&conn, &community_follower_form) {
465 Err(_e) => return Err(APIError::err("community_follower_already_exists").into()),
469 let community_view = CommunityView::read(&conn, data.community_id, Some(user_id))?;
471 Ok(CommunityResponse {
472 community: community_view,
477 impl Perform for Oper<GetFollowedCommunities> {
478 type Response = GetFollowedCommunitiesResponse;
482 pool: Pool<ConnectionManager<PgConnection>>,
483 _websocket_info: Option<WebsocketInfo>,
484 ) -> Result<GetFollowedCommunitiesResponse, Error> {
485 let data: &GetFollowedCommunities = &self.data;
487 let claims = match Claims::decode(&data.auth) {
488 Ok(claims) => claims.claims,
489 Err(_e) => return Err(APIError::err("not_logged_in").into()),
492 let user_id = claims.id;
494 let conn = pool.get()?;
496 let communities: Vec<CommunityFollowerView> =
497 match CommunityFollowerView::for_user(&conn, user_id) {
498 Ok(communities) => communities,
499 Err(_e) => return Err(APIError::err("system_err_login").into()),
503 Ok(GetFollowedCommunitiesResponse { communities })
507 impl Perform for Oper<BanFromCommunity> {
508 type Response = BanFromCommunityResponse;
512 pool: Pool<ConnectionManager<PgConnection>>,
513 websocket_info: Option<WebsocketInfo>,
514 ) -> Result<BanFromCommunityResponse, Error> {
515 let data: &BanFromCommunity = &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_user_ban_form = CommunityUserBanForm {
525 community_id: data.community_id,
526 user_id: data.user_id,
529 let conn = pool.get()?;
532 match CommunityUserBan::ban(&conn, &community_user_ban_form) {
534 Err(_e) => return Err(APIError::err("community_user_already_banned").into()),
537 match CommunityUserBan::unban(&conn, &community_user_ban_form) {
539 Err(_e) => return Err(APIError::err("community_user_already_banned").into()),
544 let expires = match data.expires {
545 Some(time) => Some(naive_from_unix(time)),
549 let form = ModBanFromCommunityForm {
550 mod_user_id: user_id,
551 other_user_id: data.user_id,
552 community_id: data.community_id,
553 reason: data.reason.to_owned(),
554 banned: Some(data.ban),
557 ModBanFromCommunity::create(&conn, &form)?;
559 let user_view = UserView::read(&conn, data.user_id)?;
561 let res = BanFromCommunityResponse {
566 if let Some(ws) = websocket_info {
567 ws.chatserver.do_send(SendCommunityRoomMessage {
568 op: UserOperation::BanFromCommunity,
569 response: res.clone(),
570 community_id: data.community_id,
579 impl Perform for Oper<AddModToCommunity> {
580 type Response = AddModToCommunityResponse;
584 pool: Pool<ConnectionManager<PgConnection>>,
585 websocket_info: Option<WebsocketInfo>,
586 ) -> Result<AddModToCommunityResponse, Error> {
587 let data: &AddModToCommunity = &self.data;
589 let claims = match Claims::decode(&data.auth) {
590 Ok(claims) => claims.claims,
591 Err(_e) => return Err(APIError::err("not_logged_in").into()),
594 let user_id = claims.id;
596 let community_moderator_form = CommunityModeratorForm {
597 community_id: data.community_id,
598 user_id: data.user_id,
601 let conn = pool.get()?;
604 match CommunityModerator::join(&conn, &community_moderator_form) {
606 Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
609 match CommunityModerator::leave(&conn, &community_moderator_form) {
611 Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
616 let form = ModAddCommunityForm {
617 mod_user_id: user_id,
618 other_user_id: data.user_id,
619 community_id: data.community_id,
620 removed: Some(!data.added),
622 ModAddCommunity::create(&conn, &form)?;
624 let moderators = CommunityModeratorView::for_community(&conn, data.community_id)?;
626 let res = AddModToCommunityResponse { moderators };
628 if let Some(ws) = websocket_info {
629 ws.chatserver.do_send(SendCommunityRoomMessage {
630 op: UserOperation::AddModToCommunity,
631 response: res.clone(),
632 community_id: data.community_id,
641 impl Perform for Oper<TransferCommunity> {
642 type Response = GetCommunityResponse;
646 pool: Pool<ConnectionManager<PgConnection>>,
647 _websocket_info: Option<WebsocketInfo>,
648 ) -> Result<GetCommunityResponse, Error> {
649 let data: &TransferCommunity = &self.data;
651 let claims = match Claims::decode(&data.auth) {
652 Ok(claims) => claims.claims,
653 Err(_e) => return Err(APIError::err("not_logged_in").into()),
656 let user_id = claims.id;
658 let conn = pool.get()?;
660 let read_community = Community::read(&conn, data.community_id)?;
662 let site_creator_id = Site::read(&conn, 1)?.creator_id;
663 let mut admins = UserView::admins(&conn)?;
664 let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
665 let creator_user = admins.remove(creator_index);
666 admins.insert(0, creator_user);
668 // Make sure user is the creator, or an admin
669 if user_id != read_community.creator_id && !admins.iter().map(|a| a.id).any(|x| x == user_id) {
670 return Err(APIError::err("not_an_admin").into());
673 let community_form = CommunityForm {
674 name: read_community.name,
675 title: read_community.title,
676 description: read_community.description,
677 category_id: read_community.category_id,
678 creator_id: data.user_id,
681 nsfw: read_community.nsfw,
682 updated: Some(naive_now()),
685 let _updated_community = match Community::update(&conn, data.community_id, &community_form) {
686 Ok(community) => community,
687 Err(_e) => return Err(APIError::err("couldnt_update_community").into()),
690 // You also have to re-do the community_moderator table, reordering it.
691 let mut community_mods = CommunityModeratorView::for_community(&conn, data.community_id)?;
692 let creator_index = community_mods
694 .position(|r| r.user_id == data.user_id)
696 let creator_user = community_mods.remove(creator_index);
697 community_mods.insert(0, creator_user);
699 CommunityModerator::delete_for_community(&conn, data.community_id)?;
701 for cmod in &community_mods {
702 let community_moderator_form = CommunityModeratorForm {
703 community_id: cmod.community_id,
704 user_id: cmod.user_id,
707 let _inserted_community_moderator =
708 match CommunityModerator::join(&conn, &community_moderator_form) {
710 Err(_e) => return Err(APIError::err("community_moderator_already_exists").into()),
715 let form = ModAddCommunityForm {
716 mod_user_id: user_id,
717 other_user_id: data.user_id,
718 community_id: data.community_id,
719 removed: Some(false),
721 ModAddCommunity::create(&conn, &form)?;
723 let community_view = match CommunityView::read(&conn, data.community_id, Some(user_id)) {
724 Ok(community) => community,
725 Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
728 let moderators = match CommunityModeratorView::for_community(&conn, data.community_id) {
729 Ok(moderators) => moderators,
730 Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
734 Ok(GetCommunityResponse {
735 community: community_view,