]> Untitled Git - lemmy.git/blobdiff - crates/api/src/community.rs
Split api crate into api_structs and api
[lemmy.git] / crates / api / src / community.rs
index f7e8e23cee2a3f17e2277564fb406698ab6e5486..1ee2a9a76ae15d3702c93938872d353c8a8b8bc5 100644 (file)
-use crate::{
+use crate::Perform;
+use actix_web::web::Data;
+use anyhow::Context;
+use lemmy_api_common::{
+  blocking,
   check_community_ban,
+  community::*,
   get_local_user_view_from_jwt,
-  get_local_user_view_from_jwt_opt,
-  is_admin,
   is_mod_or_admin,
-  Perform,
-};
-use actix_web::web::Data;
-use anyhow::Context;
-use lemmy_api_structs::{blocking, community::*};
-use lemmy_apub::{
-  generate_apub_endpoint,
-  generate_followers_url,
-  generate_inbox_url,
-  generate_shared_inbox_url,
-  ActorType,
-  CommunityType,
-  EndpointType,
-  UserType,
 };
+use lemmy_apub::{ActorType, CommunityType, UserType};
 use lemmy_db_queries::{
-  diesel_option_overwrite_to_url,
   source::{
     comment::Comment_,
     community::{CommunityModerator_, Community_},
     post::Post_,
   },
-  ApubObject,
   Bannable,
   Crud,
   Followable,
   Joinable,
-  ListingType,
-  SortType,
 };
-use lemmy_db_schema::{
-  naive_now,
-  source::{comment::Comment, community::*, moderator::*, person::Person, post::Post, site::*},
-  PersonId,
+use lemmy_db_schema::source::{
+  comment::Comment,
+  community::*,
+  moderator::*,
+  person::Person,
+  post::Post,
+  site::*,
 };
 use lemmy_db_views::comment_view::CommentQueryBuilder;
 use lemmy_db_views_actor::{
-  community_follower_view::CommunityFollowerView,
   community_moderator_view::CommunityModeratorView,
-  community_view::{CommunityQueryBuilder, CommunityView},
+  community_view::CommunityView,
   person_view::PersonViewSafe,
 };
-use lemmy_utils::{
-  apub::generate_actor_keypair,
-  location_info,
-  utils::{check_slurs, check_slurs_opt, is_valid_community_name, naive_from_unix},
-  ApiError,
-  ConnectionId,
-  LemmyError,
-};
-use lemmy_websocket::{
-  messages::{GetCommunityUsersOnline, SendCommunityRoomMessage},
-  LemmyContext,
-  UserOperation,
-};
-use std::str::FromStr;
-
-#[async_trait::async_trait(?Send)]
-impl Perform for GetCommunity {
-  type Response = GetCommunityResponse;
-
-  async fn perform(
-    &self,
-    context: &Data<LemmyContext>,
-    _websocket_id: Option<ConnectionId>,
-  ) -> Result<GetCommunityResponse, LemmyError> {
-    let data: &GetCommunity = &self;
-    let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?;
-    let person_id = local_user_view.map(|u| u.person.id);
-
-    let community_id = match data.id {
-      Some(id) => id,
-      None => {
-        let name = data.name.to_owned().unwrap_or_else(|| "main".to_string());
-        match blocking(context.pool(), move |conn| {
-          Community::read_from_name(conn, &name)
-        })
-        .await?
-        {
-          Ok(community) => community,
-          Err(_e) => return Err(ApiError::err("couldnt_find_community").into()),
-        }
-        .id
-      }
-    };
-
-    let community_view = match blocking(context.pool(), move |conn| {
-      CommunityView::read(conn, community_id, person_id)
-    })
-    .await?
-    {
-      Ok(community) => community,
-      Err(_e) => return Err(ApiError::err("couldnt_find_community").into()),
-    };
-
-    let moderators: Vec<CommunityModeratorView> = match blocking(context.pool(), move |conn| {
-      CommunityModeratorView::for_community(conn, community_id)
-    })
-    .await?
-    {
-      Ok(moderators) => moderators,
-      Err(_e) => return Err(ApiError::err("couldnt_find_community").into()),
-    };
-
-    let online = context
-      .chat_server()
-      .send(GetCommunityUsersOnline { community_id })
-      .await
-      .unwrap_or(1);
-
-    let res = GetCommunityResponse {
-      community_view,
-      moderators,
-      online,
-    };
-
-    // Return the jwt
-    Ok(res)
-  }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for CreateCommunity {
-  type Response = CommunityResponse;
-
-  async fn perform(
-    &self,
-    context: &Data<LemmyContext>,
-    _websocket_id: Option<ConnectionId>,
-  ) -> Result<CommunityResponse, LemmyError> {
-    let data: &CreateCommunity = &self;
-    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
-
-    check_slurs(&data.name)?;
-    check_slurs(&data.title)?;
-    check_slurs_opt(&data.description)?;
-
-    if !is_valid_community_name(&data.name) {
-      return Err(ApiError::err("invalid_community_name").into());
-    }
-
-    // Double check for duplicate community actor_ids
-    let community_actor_id = generate_apub_endpoint(EndpointType::Community, &data.name)?;
-    let actor_id_cloned = community_actor_id.to_owned();
-    let community_dupe = blocking(context.pool(), move |conn| {
-      Community::read_from_apub_id(conn, &actor_id_cloned)
-    })
-    .await?;
-    if community_dupe.is_ok() {
-      return Err(ApiError::err("community_already_exists").into());
-    }
-
-    // Check to make sure the icon and banners are urls
-    let icon = diesel_option_overwrite_to_url(&data.icon)?;
-    let banner = diesel_option_overwrite_to_url(&data.banner)?;
-
-    // When you create a community, make sure the user becomes a moderator and a follower
-    let keypair = generate_actor_keypair()?;
-
-    let community_form = CommunityForm {
-      name: data.name.to_owned(),
-      title: data.title.to_owned(),
-      description: data.description.to_owned(),
-      icon,
-      banner,
-      creator_id: local_user_view.person.id,
-      removed: None,
-      deleted: None,
-      nsfw: data.nsfw,
-      updated: None,
-      actor_id: Some(community_actor_id.to_owned()),
-      local: true,
-      private_key: Some(keypair.private_key),
-      public_key: Some(keypair.public_key),
-      last_refreshed_at: None,
-      published: None,
-      followers_url: Some(generate_followers_url(&community_actor_id)?),
-      inbox_url: Some(generate_inbox_url(&community_actor_id)?),
-      shared_inbox_url: Some(Some(generate_shared_inbox_url(&community_actor_id)?)),
-    };
-
-    let inserted_community = match blocking(context.pool(), move |conn| {
-      Community::create(conn, &community_form)
-    })
-    .await?
-    {
-      Ok(community) => community,
-      Err(_e) => return Err(ApiError::err("community_already_exists").into()),
-    };
-
-    // The community creator becomes a moderator
-    let community_moderator_form = CommunityModeratorForm {
-      community_id: inserted_community.id,
-      person_id: local_user_view.person.id,
-    };
-
-    let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
-    if blocking(context.pool(), join).await?.is_err() {
-      return Err(ApiError::err("community_moderator_already_exists").into());
-    }
-
-    // Follow your own community
-    let community_follower_form = CommunityFollowerForm {
-      community_id: inserted_community.id,
-      person_id: local_user_view.person.id,
-      pending: false,
-    };
-
-    let follow = move |conn: &'_ _| CommunityFollower::follow(conn, &community_follower_form);
-    if blocking(context.pool(), follow).await?.is_err() {
-      return Err(ApiError::err("community_follower_already_exists").into());
-    }
-
-    let person_id = local_user_view.person.id;
-    let community_view = blocking(context.pool(), move |conn| {
-      CommunityView::read(conn, inserted_community.id, Some(person_id))
-    })
-    .await??;
-
-    Ok(CommunityResponse { community_view })
-  }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for EditCommunity {
-  type Response = CommunityResponse;
-
-  async fn perform(
-    &self,
-    context: &Data<LemmyContext>,
-    websocket_id: Option<ConnectionId>,
-  ) -> Result<CommunityResponse, LemmyError> {
-    let data: &EditCommunity = &self;
-    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
-
-    check_slurs(&data.title)?;
-    check_slurs_opt(&data.description)?;
-
-    // Verify its a mod (only mods can edit it)
-    let community_id = data.community_id;
-    let mods: Vec<PersonId> = blocking(context.pool(), move |conn| {
-      CommunityModeratorView::for_community(conn, community_id)
-        .map(|v| v.into_iter().map(|m| m.moderator.id).collect())
-    })
-    .await??;
-    if !mods.contains(&local_user_view.person.id) {
-      return Err(ApiError::err("not_a_moderator").into());
-    }
-
-    let community_id = data.community_id;
-    let read_community = blocking(context.pool(), move |conn| {
-      Community::read(conn, community_id)
-    })
-    .await??;
-
-    let icon = diesel_option_overwrite_to_url(&data.icon)?;
-    let banner = diesel_option_overwrite_to_url(&data.banner)?;
-
-    let community_form = CommunityForm {
-      name: read_community.name,
-      title: data.title.to_owned(),
-      description: data.description.to_owned(),
-      icon,
-      banner,
-      creator_id: read_community.creator_id,
-      removed: Some(read_community.removed),
-      deleted: Some(read_community.deleted),
-      nsfw: data.nsfw,
-      updated: Some(naive_now()),
-      actor_id: Some(read_community.actor_id),
-      local: read_community.local,
-      private_key: read_community.private_key,
-      public_key: read_community.public_key,
-      last_refreshed_at: None,
-      published: None,
-      followers_url: None,
-      inbox_url: None,
-      shared_inbox_url: None,
-    };
-
-    let community_id = data.community_id;
-    match blocking(context.pool(), move |conn| {
-      Community::update(conn, community_id, &community_form)
-    })
-    .await?
-    {
-      Ok(community) => community,
-      Err(_e) => return Err(ApiError::err("couldnt_update_community").into()),
-    };
-
-    // TODO there needs to be some kind of an apub update
-    // process for communities and users
-
-    let community_id = data.community_id;
-    let person_id = local_user_view.person.id;
-    let community_view = blocking(context.pool(), move |conn| {
-      CommunityView::read(conn, community_id, Some(person_id))
-    })
-    .await??;
-
-    let res = CommunityResponse { community_view };
-
-    send_community_websocket(&res, context, websocket_id, UserOperation::EditCommunity);
-
-    Ok(res)
-  }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for DeleteCommunity {
-  type Response = CommunityResponse;
-
-  async fn perform(
-    &self,
-    context: &Data<LemmyContext>,
-    websocket_id: Option<ConnectionId>,
-  ) -> Result<CommunityResponse, LemmyError> {
-    let data: &DeleteCommunity = &self;
-    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
-
-    // Verify its the creator (only a creator can delete the community)
-    let community_id = data.community_id;
-    let read_community = blocking(context.pool(), move |conn| {
-      Community::read(conn, community_id)
-    })
-    .await??;
-    if read_community.creator_id != local_user_view.person.id {
-      return Err(ApiError::err("no_community_edit_allowed").into());
-    }
-
-    // Do the delete
-    let community_id = data.community_id;
-    let deleted = data.deleted;
-    let updated_community = match blocking(context.pool(), move |conn| {
-      Community::update_deleted(conn, community_id, deleted)
-    })
-    .await?
-    {
-      Ok(community) => community,
-      Err(_e) => return Err(ApiError::err("couldnt_update_community").into()),
-    };
-
-    // Send apub messages
-    if deleted {
-      updated_community.send_delete(context).await?;
-    } else {
-      updated_community.send_undo_delete(context).await?;
-    }
-
-    let community_id = data.community_id;
-    let person_id = local_user_view.person.id;
-    let community_view = blocking(context.pool(), move |conn| {
-      CommunityView::read(conn, community_id, Some(person_id))
-    })
-    .await??;
-
-    let res = CommunityResponse { community_view };
-
-    send_community_websocket(&res, context, websocket_id, UserOperation::DeleteCommunity);
-
-    Ok(res)
-  }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for RemoveCommunity {
-  type Response = CommunityResponse;
-
-  async fn perform(
-    &self,
-    context: &Data<LemmyContext>,
-    websocket_id: Option<ConnectionId>,
-  ) -> Result<CommunityResponse, LemmyError> {
-    let data: &RemoveCommunity = &self;
-    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
-
-    // Verify its an admin (only an admin can remove a community)
-    is_admin(&local_user_view)?;
-
-    // Do the remove
-    let community_id = data.community_id;
-    let removed = data.removed;
-    let updated_community = match blocking(context.pool(), move |conn| {
-      Community::update_removed(conn, community_id, removed)
-    })
-    .await?
-    {
-      Ok(community) => community,
-      Err(_e) => return Err(ApiError::err("couldnt_update_community").into()),
-    };
-
-    // Mod tables
-    let expires = match data.expires {
-      Some(time) => Some(naive_from_unix(time)),
-      None => None,
-    };
-    let form = ModRemoveCommunityForm {
-      mod_person_id: local_user_view.person.id,
-      community_id: data.community_id,
-      removed: Some(removed),
-      reason: data.reason.to_owned(),
-      expires,
-    };
-    blocking(context.pool(), move |conn| {
-      ModRemoveCommunity::create(conn, &form)
-    })
-    .await??;
-
-    // Apub messages
-    if removed {
-      updated_community.send_remove(context).await?;
-    } else {
-      updated_community.send_undo_remove(context).await?;
-    }
-
-    let community_id = data.community_id;
-    let person_id = local_user_view.person.id;
-    let community_view = blocking(context.pool(), move |conn| {
-      CommunityView::read(conn, community_id, Some(person_id))
-    })
-    .await??;
-
-    let res = CommunityResponse { community_view };
-
-    send_community_websocket(&res, context, websocket_id, UserOperation::RemoveCommunity);
-
-    Ok(res)
-  }
-}
-
-#[async_trait::async_trait(?Send)]
-impl Perform for ListCommunities {
-  type Response = ListCommunitiesResponse;
-
-  async fn perform(
-    &self,
-    context: &Data<LemmyContext>,
-    _websocket_id: Option<ConnectionId>,
-  ) -> Result<ListCommunitiesResponse, LemmyError> {
-    let data: &ListCommunities = &self;
-    let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?;
-
-    let person_id = match &local_user_view {
-      Some(uv) => Some(uv.person.id),
-      None => None,
-    };
-
-    // Don't show NSFW by default
-    let show_nsfw = match &local_user_view {
-      Some(uv) => uv.local_user.show_nsfw,
-      None => false,
-    };
-
-    let type_ = ListingType::from_str(&data.type_)?;
-    let sort = SortType::from_str(&data.sort)?;
-
-    let page = data.page;
-    let limit = data.limit;
-    let communities = blocking(context.pool(), move |conn| {
-      CommunityQueryBuilder::create(conn)
-        .listing_type(&type_)
-        .sort(&sort)
-        .show_nsfw(show_nsfw)
-        .my_person_id(person_id)
-        .page(page)
-        .limit(limit)
-        .list()
-    })
-    .await??;
-
-    // Return the jwt
-    Ok(ListCommunitiesResponse { communities })
-  }
-}
+use lemmy_utils::{location_info, utils::naive_from_unix, ApiError, ConnectionId, LemmyError};
+use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext, UserOperation};
 
 #[async_trait::async_trait(?Send)]
 impl Perform for FollowCommunity {
@@ -553,33 +111,6 @@ impl Perform for FollowCommunity {
   }
 }
 
-#[async_trait::async_trait(?Send)]
-impl Perform for GetFollowedCommunities {
-  type Response = GetFollowedCommunitiesResponse;
-
-  async fn perform(
-    &self,
-    context: &Data<LemmyContext>,
-    _websocket_id: Option<ConnectionId>,
-  ) -> Result<GetFollowedCommunitiesResponse, LemmyError> {
-    let data: &GetFollowedCommunities = &self;
-    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
-
-    let person_id = local_user_view.person.id;
-    let communities = match blocking(context.pool(), move |conn| {
-      CommunityFollowerView::for_person(conn, person_id)
-    })
-    .await?
-    {
-      Ok(communities) => communities,
-      _ => return Err(ApiError::err("system_err_login").into()),
-    };
-
-    // Return the jwt
-    Ok(GetFollowedCommunitiesResponse { communities })
-  }
-}
-
 #[async_trait::async_trait(?Send)]
 impl Perform for BanFromCommunity {
   type Response = BanFromCommunityResponse;
@@ -907,21 +438,3 @@ impl Perform for TransferCommunity {
     })
   }
 }
-
-fn send_community_websocket(
-  res: &CommunityResponse,
-  context: &Data<LemmyContext>,
-  websocket_id: Option<ConnectionId>,
-  op: UserOperation,
-) {
-  // Strip out the person id and subscribed when sending to others
-  let mut res_sent = res.clone();
-  res_sent.community_view.subscribed = false;
-
-  context.chat_server().do_send(SendCommunityRoomMessage {
-    op,
-    response: res_sent,
-    community_id: res.community_view.community.id,
-    websocket_id,
-  });
-}