diff --git a/api_tests/src/community.spec.ts b/api_tests/src/community.spec.ts index 5d27e664d0..c193ab6139 100644 --- a/api_tests/src/community.spec.ts +++ b/api_tests/src/community.spec.ts @@ -62,7 +62,7 @@ test("Create community", async () => { let prevName = communityRes.community_view.community.name; await jestLemmyError( () => createCommunity(alpha, prevName), - new LemmyError("already_exists", statusBadRequest), + new LemmyError("name_already_taken", statusBadRequest), ); // Cache the community on beta, make sure it has the other fields @@ -402,7 +402,7 @@ test("Get community for different casing on domain", async () => { let prevName = communityRes.community_view.community.name; await jestLemmyError( () => createCommunity(alpha, prevName), - new LemmyError("already_exists", statusBadRequest), + new LemmyError("name_already_taken", statusBadRequest), ); // Cache the community on beta, make sure it has the other fields diff --git a/crates/api/api_crud/src/community/create.rs b/crates/api/api_crud/src/community/create.rs index 049b872ad7..9c7c7f4621 100644 --- a/crates/api/api_crud/src/community/create.rs +++ b/crates/api/api_crud/src/community/create.rs @@ -1,3 +1,4 @@ +use crate::check_user_or_community_name_taken; use activitypub_federation::{config::Data, http_signatures::generate_actor_keypair}; use actix_web::web::Json; use lemmy_api_utils::{ @@ -82,13 +83,10 @@ pub async fn create_community( } is_valid_actor_name(&data.name)?; + check_user_or_community_name_taken(&data.name, &context).await?; // Double check for duplicate community actor_ids let community_ap_id = Community::generate_local_actor_url(&data.name, context.settings())?; - let community_dupe = Community::read_from_apub_id(&mut context.pool(), &community_ap_id).await?; - if community_dupe.is_some() { - Err(LemmyErrorType::AlreadyExists)? - } let keypair = generate_actor_keypair()?; let community_form = CommunityInsertForm { diff --git a/crates/api/api_crud/src/lib.rs b/crates/api/api_crud/src/lib.rs index 11840f8b25..956de50ce1 100644 --- a/crates/api/api_crud/src/lib.rs +++ b/crates/api/api_crud/src/lib.rs @@ -1,5 +1,9 @@ use lemmy_api_utils::context::LemmyContext; -use lemmy_db_schema::source::community::{Community, CommunityActions}; +use lemmy_db_schema::source::{ + community::{Community, CommunityActions}, + person::Person, +}; +use lemmy_utils::error::LemmyResult; pub mod comment; pub mod community; @@ -22,3 +26,10 @@ async fn community_use_pending(community: &Community, context: &LemmyContext) -> .await .is_ok() } + +async fn check_user_or_community_name_taken(name: &str, context: &LemmyContext) -> LemmyResult<()> { + // TODO: better to make only a single sql query + Person::check_name_taken(&mut context.pool(), name).await?; + Community::check_name_taken(&mut context.pool(), name).await?; + Ok(()) +} diff --git a/crates/api/api_crud/src/user/create.rs b/crates/api/api_crud/src/user/create.rs index c2168aec66..341d2305b3 100644 --- a/crates/api/api_crud/src/user/create.rs +++ b/crates/api/api_crud/src/user/create.rs @@ -1,3 +1,4 @@ +use crate::check_user_or_community_name_taken; use activitypub_federation::{ config::Data, fetch::object_id::ObjectId, @@ -127,7 +128,7 @@ pub async fn register( check_slurs(&data.username, &slur_regex)?; check_slurs_opt(&data.answer, &slur_regex)?; - Person::check_username_taken(pool, &data.username).await?; + check_user_or_community_name_taken(&data.username, &context).await?; if let Some(email) = &data.email { LocalUser::check_is_email_taken(pool, email).await?; @@ -391,7 +392,7 @@ pub async fn authenticate_with_oauth( check_slurs(username, &slur_regex)?; check_slurs_opt(&tx_data.answer, &slur_regex)?; - Person::check_username_taken(&mut conn.into(), username).await?; + check_user_or_community_name_taken(username, &tx_context).await?; // We have to create a person, a local_user, and an oauth_account let person = create_person(username.clone(), &site_view, &tx_context, conn).await?; diff --git a/crates/db_schema/src/impls/community.rs b/crates/db_schema/src/impls/community.rs index 832adf02df..b150bd74ca 100644 --- a/crates/db_schema/src/impls/community.rs +++ b/crates/db_schema/src/impls/community.rs @@ -281,6 +281,18 @@ impl Community { .await .with_lemmy_type(LemmyErrorType::CouldntUpdate) } + pub async fn check_name_taken(pool: &mut DbPool<'_>, username: &str) -> LemmyResult<()> { + let conn = &mut get_conn(pool).await?; + select(not(exists( + community::table + .filter(lower(community::name).eq(username.to_lowercase())) + .filter(community::local.eq(true)), + ))) + .get_result::(conn) + .await? + .then_some(()) + .ok_or(LemmyErrorType::NameAlreadyTaken.into()) + } } impl CommunityActions { diff --git a/crates/db_schema/src/impls/person.rs b/crates/db_schema/src/impls/person.rs index ad43bdbb89..efb58a234d 100644 --- a/crates/db_schema/src/impls/person.rs +++ b/crates/db_schema/src/impls/person.rs @@ -140,7 +140,7 @@ impl Person { .with_lemmy_type(LemmyErrorType::CouldntUpdate) } - pub async fn check_username_taken(pool: &mut DbPool<'_>, username: &str) -> LemmyResult<()> { + pub async fn check_name_taken(pool: &mut DbPool<'_>, username: &str) -> LemmyResult<()> { let conn = &mut get_conn(pool).await?; select(not(exists( person::table @@ -150,7 +150,7 @@ impl Person { .get_result::(conn) .await? .then_some(()) - .ok_or(LemmyErrorType::UsernameAlreadyTaken.into()) + .ok_or(LemmyErrorType::NameAlreadyTaken.into()) } } diff --git a/crates/utils/src/error.rs b/crates/utils/src/error.rs index 7d75e0ed63..cf2294c206 100644 --- a/crates/utils/src/error.rs +++ b/crates/utils/src/error.rs @@ -64,7 +64,7 @@ pub enum LemmyErrorType { RegistrationApplicationAnswerRequired, RegistrationUsernameRequired, EmailAlreadyTaken, - UsernameAlreadyTaken, + NameAlreadyTaken, PersonIsBannedFromCommunity, NoIdGiven, IncorrectLogin,