X-Git-Url: http://these/git/?a=blobdiff_plain;f=crates%2Fapi_crud%2Fsrc%2Fcommunity%2Fcreate.rs;h=7c84a21502bfa68067cc3a637c280fb0b6f25af6;hb=3471f3533cb724b2cf6953d563aadfcc9f66c1d2;hp=393b3746a22c4168b4d762e681d5164e89380f4a;hpb=c3d64f996e5b9ed33b2ea9e7226f1f6d51006c4c;p=lemmy.git diff --git a/crates/api_crud/src/community/create.rs b/crates/api_crud/src/community/create.rs index 393b3746..7c84a215 100644 --- a/crates/api_crud/src/community/create.rs +++ b/crates/api_crud/src/community/create.rs @@ -1,98 +1,113 @@ use crate::PerformCrud; +use activitypub_federation::http_signatures::generate_actor_keypair; use actix_web::web::Data; use lemmy_api_common::{ - blocking, + build_response::build_community_response, community::{CommunityResponse, CreateCommunity}, - get_local_user_view_from_jwt, + context::LemmyContext, + utils::{ + generate_followers_url, + generate_inbox_url, + generate_local_apub_endpoint, + generate_shared_inbox_url, + is_admin, + local_site_to_slur_regex, + local_user_view_from_jwt, + sanitize_html, + sanitize_html_opt, + EndpointType, + }, }; -use lemmy_apub::{ - generate_apub_endpoint, - generate_followers_url, - generate_inbox_url, - generate_shared_inbox_url, - EndpointType, +use lemmy_db_schema::{ + source::{ + actor_language::{CommunityLanguage, SiteLanguage}, + community::{ + Community, + CommunityFollower, + CommunityFollowerForm, + CommunityInsertForm, + CommunityModerator, + CommunityModeratorForm, + }, + }, + traits::{ApubActor, Crud, Followable, Joinable}, + utils::diesel_option_overwrite_to_url_create, }; -use lemmy_db_queries::{diesel_option_overwrite_to_url, ApubObject, Crud, Followable, Joinable}; -use lemmy_db_schema::source::community::{ - Community, - CommunityFollower, - CommunityFollowerForm, - CommunityForm, - CommunityModerator, - CommunityModeratorForm, -}; -use lemmy_db_views_actor::community_view::CommunityView; +use lemmy_db_views::structs::SiteView; use lemmy_utils::{ - apub::generate_actor_keypair, - utils::{check_slurs, check_slurs_opt, is_valid_community_name}, - ApiError, - ConnectionId, - LemmyError, + error::{LemmyError, LemmyErrorExt, LemmyErrorType}, + utils::{ + slurs::{check_slurs, check_slurs_opt}, + validation::{is_valid_actor_name, is_valid_body_field}, + }, }; -use lemmy_websocket::LemmyContext; #[async_trait::async_trait(?Send)] impl PerformCrud for CreateCommunity { type Response = CommunityResponse; - async fn perform( - &self, - context: &Data, - _websocket_id: Option, - ) -> Result { - 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)?; + #[tracing::instrument(skip(context))] + async fn perform(&self, context: &Data) -> Result { + let data: &CreateCommunity = self; + let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; + let site_view = SiteView::read_local(&mut context.pool()).await?; + let local_site = site_view.local_site; - if !is_valid_community_name(&data.name) { - return Err(ApiError::err("invalid_community_name").into()); + if local_site.community_creation_admin_only && is_admin(&local_user_view).is_err() { + return Err(LemmyErrorType::OnlyAdminsCanCreateCommunities)?; } + // Check to make sure the icon and banners are urls + let icon = diesel_option_overwrite_to_url_create(&data.icon)?; + let banner = diesel_option_overwrite_to_url_create(&data.banner)?; + + let name = sanitize_html(&data.name); + let title = sanitize_html(&data.title); + let description = sanitize_html_opt(&data.description); + + let slur_regex = local_site_to_slur_regex(&local_site); + check_slurs(&name, &slur_regex)?; + check_slurs(&title, &slur_regex)?; + check_slurs_opt(&description, &slur_regex)?; + + is_valid_actor_name(&data.name, local_site.actor_name_max_length as usize)?; + is_valid_body_field(&data.description, false)?; + // 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()); + let community_actor_id = generate_local_apub_endpoint( + EndpointType::Community, + &data.name, + &context.settings().get_protocol_and_hostname(), + )?; + let community_dupe = + Community::read_from_apub_id(&mut context.pool(), &community_actor_id).await?; + if community_dupe.is_some() { + return Err(LemmyErrorType::CommunityAlreadyExists)?; } - // 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, - nsfw: data.nsfw, - actor_id: Some(community_actor_id.to_owned()), - private_key: Some(keypair.private_key), - public_key: Some(keypair.public_key), - 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)?)), - ..CommunityForm::default() - }; + let community_form = CommunityInsertForm::builder() + .name(name) + .title(title) + .description(description) + .icon(icon) + .banner(banner) + .nsfw(data.nsfw) + .actor_id(Some(community_actor_id.clone())) + .private_key(Some(keypair.private_key)) + .public_key(keypair.public_key) + .followers_url(Some(generate_followers_url(&community_actor_id)?)) + .inbox_url(Some(generate_inbox_url(&community_actor_id)?)) + .shared_inbox_url(Some(generate_shared_inbox_url(&community_actor_id)?)) + .posting_restricted_to_mods(data.posting_restricted_to_mods) + .instance_id(site_view.site.instance_id) + .build(); - 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()), - }; + let inserted_community = Community::create(&mut context.pool(), &community_form) + .await + .with_lemmy_type(LemmyErrorType::CommunityAlreadyExists)?; // The community creator becomes a moderator let community_moderator_form = CommunityModeratorForm { @@ -100,10 +115,9 @@ impl PerformCrud for CreateCommunity { 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()); - } + CommunityModerator::join(&mut context.pool(), &community_moderator_form) + .await + .with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)?; // Follow your own community let community_follower_form = CommunityFollowerForm { @@ -112,17 +126,23 @@ impl PerformCrud for CreateCommunity { 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()); - } + CommunityFollower::follow(&mut context.pool(), &community_follower_form) + .await + .with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)?; - 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??; + // Update the discussion_languages if that's provided + let community_id = inserted_community.id; + if let Some(languages) = data.discussion_languages.clone() { + let site_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?; + // check that community languages are a subset of site languages + // https://stackoverflow.com/a/64227550 + let is_subset = languages.iter().all(|item| site_languages.contains(item)); + if !is_subset { + return Err(LemmyErrorType::LanguageNotAllowed)?; + } + CommunityLanguage::update(&mut context.pool(), languages, community_id).await?; + } - Ok(CommunityResponse { community_view }) + build_community_response(context, local_user_view, community_id).await } }