diff --git a/crates/handlers/src/views/register.rs b/crates/handlers/src/views/register.rs index 904d3364..c592b328 100644 --- a/crates/handlers/src/views/register.rs +++ b/crates/handlers/src/views/register.rs @@ -26,7 +26,7 @@ use mas_axum_utils::{ }; use mas_config::Encrypter; use mas_router::Route; -use mas_storage::user::{register_user, start_session}; +use mas_storage::user::{register_user, start_session, username_exists}; use mas_templates::{ FieldError, FormError, RegisterContext, RegisterFormField, TemplateContext, Templates, ToFormState, @@ -96,6 +96,8 @@ pub(crate) async fn post( if form.username.is_empty() { state.add_error_on_field(RegisterFormField::Username, FieldError::Required); + } else if username_exists(&mut txn, &form.username).await? { + state.add_error_on_field(RegisterFormField::Username, FieldError::Exists); } if form.password.is_empty() { diff --git a/crates/storage/sqlx-data.json b/crates/storage/sqlx-data.json index 420d4548..79fb022a 100644 --- a/crates/storage/sqlx-data.json +++ b/crates/storage/sqlx-data.json @@ -1974,6 +1974,26 @@ }, "query": "\n UPDATE compat_sessions\n SET deleted_at = NOW()\n FROM compat_access_tokens\n WHERE compat_access_tokens.token = $1\n AND compat_sessions.id = compat_access_tokens.id \n AND compat_sessions.deleted_at IS NULL\n " }, + "af77bad7259175464c5ad57f9662571c17b29552ebb70e4b6022584b41bdff0d": { + "describe": { + "columns": [ + { + "name": "exists!", + "ordinal": 0, + "type_info": "Bool" + } + ], + "nullable": [ + null + ], + "parameters": { + "Left": [ + "Text" + ] + } + }, + "query": "\n SELECT EXISTS(\n SELECT 1 FROM users WHERE username = $1\n ) AS \"exists!\"\n " + }, "b0fec01072df856ba9cd8be0ecf7a58dd4709a0efca4035a2c6f99c43d5a12be": { "describe": { "columns": [ diff --git a/crates/storage/src/user.rs b/crates/storage/src/user.rs index 1ca939b6..3ff7404c 100644 --- a/crates/storage/src/user.rs +++ b/crates/storage/src/user.rs @@ -476,6 +476,22 @@ pub async fn lookup_user_by_username( }) } +pub async fn username_exists( + executor: impl PgExecutor<'_>, + username: &str, +) -> Result { + sqlx::query_scalar!( + r#" + SELECT EXISTS( + SELECT 1 FROM users WHERE username = $1 + ) AS "exists!" + "#, + username + ) + .fetch_one(executor) + .await +} + #[derive(Debug, Clone)] struct UserEmailLookup { user_email_id: i64, diff --git a/crates/templates/src/forms.rs b/crates/templates/src/forms.rs index 8d88a825..f3898f04 100644 --- a/crates/templates/src/forms.rs +++ b/crates/templates/src/forms.rs @@ -27,11 +27,14 @@ pub trait FormField: Copy + Hash + PartialEq + Eq + Serialize + for<'de> Deseria #[derive(Debug, Serialize)] #[serde(rename_all = "snake_case", tag = "kind")] pub enum FieldError { - /// A reuired field is missing + /// A required field is missing Required, /// An unspecified error on the field Unspecified, + + /// That value already exists + Exists, } /// An error on the whole form diff --git a/crates/templates/src/res/components/field.html b/crates/templates/src/res/components/field.html index 203f24ba..a25463a8 100644 --- a/crates/templates/src/res/components/field.html +++ b/crates/templates/src/res/components/field.html @@ -44,6 +44,8 @@ limitations under the License.
{% if error.kind == "required" %} This field is required + {% elif error.kind == "exists" and name == "username" %} + This username is already taken {% else %} {{ error.kind }} {% endif %}