-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 {
}
}
-#[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;
})
}
}
-
-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,
- });
-}