2 use diesel::PgConnection;
5 #[derive(Serialize, Deserialize)]
6 pub struct GetCommunity {
8 pub name: Option<String>,
12 #[derive(Serialize, Deserialize)]
13 pub struct GetCommunityResponse {
15 pub community: CommunityView,
16 pub moderators: Vec<CommunityModeratorView>,
17 pub admins: Vec<UserView>,
20 #[derive(Serialize, Deserialize)]
21 pub struct CreateCommunity {
24 description: Option<String>,
30 #[derive(Serialize, Deserialize, Clone)]
31 pub struct CommunityResponse {
33 pub community: CommunityView,
36 #[derive(Serialize, Deserialize)]
37 pub struct ListCommunities {
44 #[derive(Serialize, Deserialize, Debug)]
45 pub struct ListCommunitiesResponse {
47 pub communities: Vec<CommunityView>,
50 #[derive(Serialize, Deserialize, Clone)]
51 pub struct BanFromCommunity {
52 pub community_id: i32,
55 reason: Option<String>,
60 #[derive(Serialize, Deserialize)]
61 pub struct BanFromCommunityResponse {
67 #[derive(Serialize, Deserialize)]
68 pub struct AddModToCommunity {
69 pub community_id: i32,
75 #[derive(Serialize, Deserialize)]
76 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 {
111 communities: Vec<CommunityFollowerView>,
114 #[derive(Serialize, Deserialize)]
115 pub struct TransferCommunity {
121 impl Perform<GetCommunityResponse> for Oper<GetCommunity> {
122 fn perform(&self, conn: &PgConnection) -> 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 community_id = match data.id {
139 match Community::read_from_name(
141 data.name.to_owned().unwrap_or_else(|| "main".to_string()),
143 Ok(community) => community.id,
144 Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_community").into()),
149 let community_view = match CommunityView::read(&conn, community_id, user_id) {
150 Ok(community) => community,
151 Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_community").into()),
154 let moderators = match CommunityModeratorView::for_community(&conn, community_id) {
155 Ok(moderators) => moderators,
156 Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_community").into()),
159 let site_creator_id = Site::read(&conn, 1)?.creator_id;
160 let mut admins = UserView::admins(&conn)?;
161 let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
162 let creator_user = admins.remove(creator_index);
163 admins.insert(0, creator_user);
166 Ok(GetCommunityResponse {
167 op: self.op.to_string(),
168 community: community_view,
175 impl Perform<CommunityResponse> for Oper<CreateCommunity> {
176 fn perform(&self, conn: &PgConnection) -> Result<CommunityResponse, Error> {
177 let data: &CreateCommunity = &self.data;
179 let claims = match Claims::decode(&data.auth) {
180 Ok(claims) => claims.claims,
181 Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()),
184 if has_slurs(&data.name)
185 || has_slurs(&data.title)
186 || (data.description.is_some() && has_slurs(&data.description.to_owned().unwrap()))
188 return Err(APIError::err(&self.op, "no_slurs").into());
191 let user_id = claims.id;
193 // Check for a site ban
194 if UserView::read(&conn, user_id)?.banned {
195 return Err(APIError::err(&self.op, "site_ban").into());
198 // When you create a community, make sure the user becomes a moderator and a follower
199 let community_form = CommunityForm {
200 name: data.name.to_owned(),
201 title: data.title.to_owned(),
202 description: data.description.to_owned(),
203 category_id: data.category_id,
211 let inserted_community = match Community::create(&conn, &community_form) {
212 Ok(community) => community,
213 Err(_e) => return Err(APIError::err(&self.op, "community_already_exists").into()),
216 let community_moderator_form = CommunityModeratorForm {
217 community_id: inserted_community.id,
221 let _inserted_community_moderator =
222 match CommunityModerator::join(&conn, &community_moderator_form) {
225 return Err(APIError::err(&self.op, "community_moderator_already_exists").into())
229 let community_follower_form = CommunityFollowerForm {
230 community_id: inserted_community.id,
234 let _inserted_community_follower =
235 match CommunityFollower::follow(&conn, &community_follower_form) {
237 Err(_e) => return Err(APIError::err(&self.op, "community_follower_already_exists").into()),
240 let community_view = CommunityView::read(&conn, inserted_community.id, Some(user_id))?;
242 Ok(CommunityResponse {
243 op: self.op.to_string(),
244 community: community_view,
249 impl Perform<CommunityResponse> for Oper<EditCommunity> {
250 fn perform(&self, conn: &PgConnection) -> Result<CommunityResponse, Error> {
251 let data: &EditCommunity = &self.data;
253 if has_slurs(&data.name) || has_slurs(&data.title) {
254 return Err(APIError::err(&self.op, "no_slurs").into());
257 let claims = match Claims::decode(&data.auth) {
258 Ok(claims) => claims.claims,
259 Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()),
262 let user_id = claims.id;
264 // Check for a site ban
265 if UserView::read(&conn, user_id)?.banned {
266 return Err(APIError::err(&self.op, "site_ban").into());
270 let mut editors: Vec<i32> = Vec::new();
272 &mut CommunityModeratorView::for_community(&conn, data.edit_id)?
277 editors.append(&mut UserView::admins(&conn)?.into_iter().map(|a| a.id).collect());
278 if !editors.contains(&user_id) {
279 return Err(APIError::err(&self.op, "no_community_edit_allowed").into());
282 let community_form = CommunityForm {
283 name: data.name.to_owned(),
284 title: data.title.to_owned(),
285 description: data.description.to_owned(),
286 category_id: data.category_id.to_owned(),
288 removed: data.removed.to_owned(),
289 deleted: data.deleted.to_owned(),
291 updated: Some(naive_now()),
294 let _updated_community = match Community::update(&conn, data.edit_id, &community_form) {
295 Ok(community) => community,
296 Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_community").into()),
300 if let Some(removed) = data.removed.to_owned() {
301 let expires = match data.expires {
302 Some(time) => Some(naive_from_unix(time)),
305 let form = ModRemoveCommunityForm {
306 mod_user_id: user_id,
307 community_id: data.edit_id,
308 removed: Some(removed),
309 reason: data.reason.to_owned(),
312 ModRemoveCommunity::create(&conn, &form)?;
315 let community_view = CommunityView::read(&conn, data.edit_id, Some(user_id))?;
317 Ok(CommunityResponse {
318 op: self.op.to_string(),
319 community: community_view,
324 impl Perform<ListCommunitiesResponse> for Oper<ListCommunities> {
325 fn perform(&self, conn: &PgConnection) -> Result<ListCommunitiesResponse, Error> {
326 let data: &ListCommunities = &self.data;
328 let user_claims: Option<Claims> = match &data.auth {
329 Some(auth) => match Claims::decode(&auth) {
330 Ok(claims) => Some(claims.claims),
336 let user_id = match &user_claims {
337 Some(claims) => Some(claims.id),
341 let show_nsfw = match &user_claims {
342 Some(claims) => claims.show_nsfw,
346 let sort = SortType::from_str(&data.sort)?;
348 let communities = CommunityQueryBuilder::create(&conn)
351 .show_nsfw(show_nsfw)
357 Ok(ListCommunitiesResponse {
358 op: self.op.to_string(),
364 impl Perform<CommunityResponse> for Oper<FollowCommunity> {
365 fn perform(&self, conn: &PgConnection) -> Result<CommunityResponse, Error> {
366 let data: &FollowCommunity = &self.data;
368 let claims = match Claims::decode(&data.auth) {
369 Ok(claims) => claims.claims,
370 Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()),
373 let user_id = claims.id;
375 let community_follower_form = CommunityFollowerForm {
376 community_id: data.community_id,
381 match CommunityFollower::follow(&conn, &community_follower_form) {
383 Err(_e) => return Err(APIError::err(&self.op, "community_follower_already_exists").into()),
386 match CommunityFollower::ignore(&conn, &community_follower_form) {
388 Err(_e) => return Err(APIError::err(&self.op, "community_follower_already_exists").into()),
392 let community_view = CommunityView::read(&conn, data.community_id, Some(user_id))?;
394 Ok(CommunityResponse {
395 op: self.op.to_string(),
396 community: community_view,
401 impl Perform<GetFollowedCommunitiesResponse> for Oper<GetFollowedCommunities> {
402 fn perform(&self, conn: &PgConnection) -> Result<GetFollowedCommunitiesResponse, Error> {
403 let data: &GetFollowedCommunities = &self.data;
405 let claims = match Claims::decode(&data.auth) {
406 Ok(claims) => claims.claims,
407 Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()),
410 let user_id = claims.id;
412 let communities: Vec<CommunityFollowerView> =
413 match CommunityFollowerView::for_user(&conn, user_id) {
414 Ok(communities) => communities,
415 Err(_e) => return Err(APIError::err(&self.op, "system_err_login").into()),
419 Ok(GetFollowedCommunitiesResponse {
420 op: self.op.to_string(),
426 impl Perform<BanFromCommunityResponse> for Oper<BanFromCommunity> {
427 fn perform(&self, conn: &PgConnection) -> Result<BanFromCommunityResponse, Error> {
428 let data: &BanFromCommunity = &self.data;
430 let claims = match Claims::decode(&data.auth) {
431 Ok(claims) => claims.claims,
432 Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()),
435 let user_id = claims.id;
437 let community_user_ban_form = CommunityUserBanForm {
438 community_id: data.community_id,
439 user_id: data.user_id,
443 match CommunityUserBan::ban(&conn, &community_user_ban_form) {
445 Err(_e) => return Err(APIError::err(&self.op, "community_user_already_banned").into()),
448 match CommunityUserBan::unban(&conn, &community_user_ban_form) {
450 Err(_e) => return Err(APIError::err(&self.op, "community_user_already_banned").into()),
455 let expires = match data.expires {
456 Some(time) => Some(naive_from_unix(time)),
460 let form = ModBanFromCommunityForm {
461 mod_user_id: user_id,
462 other_user_id: data.user_id,
463 community_id: data.community_id,
464 reason: data.reason.to_owned(),
465 banned: Some(data.ban),
468 ModBanFromCommunity::create(&conn, &form)?;
470 let user_view = UserView::read(&conn, data.user_id)?;
472 Ok(BanFromCommunityResponse {
473 op: self.op.to_string(),
480 impl Perform<AddModToCommunityResponse> for Oper<AddModToCommunity> {
481 fn perform(&self, conn: &PgConnection) -> Result<AddModToCommunityResponse, Error> {
482 let data: &AddModToCommunity = &self.data;
484 let claims = match Claims::decode(&data.auth) {
485 Ok(claims) => claims.claims,
486 Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()),
489 let user_id = claims.id;
491 let community_moderator_form = CommunityModeratorForm {
492 community_id: data.community_id,
493 user_id: data.user_id,
497 match CommunityModerator::join(&conn, &community_moderator_form) {
500 return Err(APIError::err(&self.op, "community_moderator_already_exists").into())
504 match CommunityModerator::leave(&conn, &community_moderator_form) {
507 return Err(APIError::err(&self.op, "community_moderator_already_exists").into())
513 let form = ModAddCommunityForm {
514 mod_user_id: user_id,
515 other_user_id: data.user_id,
516 community_id: data.community_id,
517 removed: Some(!data.added),
519 ModAddCommunity::create(&conn, &form)?;
521 let moderators = CommunityModeratorView::for_community(&conn, data.community_id)?;
523 Ok(AddModToCommunityResponse {
524 op: self.op.to_string(),
530 impl Perform<GetCommunityResponse> for Oper<TransferCommunity> {
531 fn perform(&self, conn: &PgConnection) -> Result<GetCommunityResponse, Error> {
532 let data: &TransferCommunity = &self.data;
534 let claims = match Claims::decode(&data.auth) {
535 Ok(claims) => claims.claims,
536 Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()),
539 let user_id = claims.id;
541 let read_community = Community::read(&conn, data.community_id)?;
543 let site_creator_id = Site::read(&conn, 1)?.creator_id;
544 let mut admins = UserView::admins(&conn)?;
545 let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
546 let creator_user = admins.remove(creator_index);
547 admins.insert(0, creator_user);
549 // Make sure user is the creator, or an admin
550 if user_id != read_community.creator_id && !admins.iter().map(|a| a.id).any(|x| x == user_id) {
551 return Err(APIError::err(&self.op, "not_an_admin").into());
554 let community_form = CommunityForm {
555 name: read_community.name,
556 title: read_community.title,
557 description: read_community.description,
558 category_id: read_community.category_id,
559 creator_id: data.user_id,
562 nsfw: read_community.nsfw,
563 updated: Some(naive_now()),
566 let _updated_community = match Community::update(&conn, data.community_id, &community_form) {
567 Ok(community) => community,
568 Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_community").into()),
571 // You also have to re-do the community_moderator table, reordering it.
572 let mut community_mods = CommunityModeratorView::for_community(&conn, data.community_id)?;
573 let creator_index = community_mods
575 .position(|r| r.user_id == data.user_id)
577 let creator_user = community_mods.remove(creator_index);
578 community_mods.insert(0, creator_user);
580 CommunityModerator::delete_for_community(&conn, data.community_id)?;
582 for cmod in &community_mods {
583 let community_moderator_form = CommunityModeratorForm {
584 community_id: cmod.community_id,
585 user_id: cmod.user_id,
588 let _inserted_community_moderator =
589 match CommunityModerator::join(&conn, &community_moderator_form) {
592 return Err(APIError::err(&self.op, "community_moderator_already_exists").into())
598 let form = ModAddCommunityForm {
599 mod_user_id: user_id,
600 other_user_id: data.user_id,
601 community_id: data.community_id,
602 removed: Some(false),
604 ModAddCommunity::create(&conn, &form)?;
606 let community_view = match CommunityView::read(&conn, data.community_id, Some(user_id)) {
607 Ok(community) => community,
608 Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_community").into()),
611 let moderators = match CommunityModeratorView::for_community(&conn, data.community_id) {
612 Ok(moderators) => moderators,
613 Err(_e) => return Err(APIError::err(&self.op, "couldnt_find_community").into()),
617 Ok(GetCommunityResponse {
618 op: self.op.to_string(),
619 community: community_view,