diff --git a/migrations/20230829220628_name.sql b/migrations/20230829220628_name.sql new file mode 100644 index 0000000..d98000f --- /dev/null +++ b/migrations/20230829220628_name.sql @@ -0,0 +1,2 @@ +-- Add migration script here +ALTER TABLE accounts ADD COLUMN name TEXT UNIQUE; diff --git a/src/main.rs b/src/main.rs index e8d883b..dc8d886 100644 --- a/src/main.rs +++ b/src/main.rs @@ -567,6 +567,11 @@ async fn home( ) .fetch(&state.db); + let name = sqlx::query!("SELECT name FROM accounts WHERE id = $1", user) + .fetch_optional(&state.db) + .await? + .and_then(|r| r.name); + let mut aliases: HashMap<_, _> = aliases.into_iter().map(|a| (a.mail, Vec::new())).collect(); while let Some(alias) = alias_stream.try_next().await? { aliases.get_mut(&alias.alias).unwrap().push(alias.recipient); @@ -582,6 +587,7 @@ async fn home( context.insert("mails", &mails); context.insert("mail_domain", &state.mail_domain); context.insert("aliases", &aliases); + context.insert("name", &name); if let Some(err) = query.user_error { tracing::info!("User error: {err:?}"); context.insert("user_error", &err.to_string()); @@ -649,6 +655,7 @@ async fn delete_alias( #[derive(Serialize, Deserialize, Debug)] enum UserError { MailAlreadyExists, + NameAlreadyExists(String), } impl Display for UserError { @@ -657,10 +664,25 @@ impl Display for UserError { UserError::MailAlreadyExists => { write!(f, "email address is already used by another user") } + UserError::NameAlreadyExists(n) => { + write!(f, "account name '{n}' is already used by another user") + } } } } +impl From for Redirect { + fn from(value: UserError) -> Self { + Redirect::to(&format!( + "/?{}", + serde_urlencoded::to_string(HomeQuery { + user_error: Some(value) + }) + .expect("could not generate user error") + )) + } +} + async fn add_mail( state: State>, User(user): User, @@ -677,13 +699,7 @@ async fn add_mail( .expect("count should not be null"); if has_mail != 0 { - Ok(Redirect::to(&format!( - "/?{}", - serde_urlencoded::to_string(&HomeQuery { - user_error: Some(UserError::MailAlreadyExists) - }) - .expect("could not generate query") - ))) + Ok(UserError::MailAlreadyExists.into()) } else { sqlx::query!( "INSERT INTO emails (id, mail) VALUES ($1, $2) ON CONFLICT DO NOTHING", @@ -713,13 +729,7 @@ async fn add_alias( .expect("count should not be null"); if has_mail != 0 { - Ok(Redirect::to(&format!( - "/?{}", - serde_urlencoded::to_string(&HomeQuery { - user_error: Some(UserError::MailAlreadyExists) - }) - .expect("could not generate query") - ))) + Ok(UserError::MailAlreadyExists.into()) } else { sqlx::query!( "INSERT INTO emails (id, mail, alias) VALUES ($1, $2, true) ON CONFLICT DO NOTHING", @@ -834,6 +844,43 @@ async fn set_password( Ok(Redirect::to("/")) } +#[derive(Deserialize, Debug)] +struct Name { + name: String, +} + +#[tracing::instrument(skip(state))] +async fn set_name( + state: State>, + User(user): User, + Form(name): Form, +) -> Result { + let taken = sqlx::query!( + "SELECT COUNT(*) FROM accounts WHERE name = $1 AND id != $2", + name.name, + user + ) + .fetch_one(&state.db) + .await? + .count + .expect("count returned null") + != 0; + + if taken { + Ok(UserError::NameAlreadyExists(name.name).into()) + } else { + sqlx::query!( + "UPDATE accounts SET name = $1 WHERE id = $2", + name.name, + user + ) + .execute(&state.db) + .await?; + + Ok(Redirect::to("/")) + } +} + #[tokio::main] async fn main() -> color_eyre::Result<()> { color_eyre::install()?; @@ -871,6 +918,7 @@ async fn main() -> color_eyre::Result<()> { .route("/alias/recipient/delete", post(delete_recipient)) .route("/alias/delete", post(delete_alias)) .route("/password", post(set_password)) + .route("/name", post(set_name)) .fallback(page_not_found) .with_state(Arc::new(AppState { db, diff --git a/templates/home.html b/templates/home.html index f44e140..7554050 100644 --- a/templates/home.html +++ b/templates/home.html @@ -99,18 +99,44 @@ {% if user_error %}
{{ user_error }}
{% endif %} -

Password

-
-
- - +

+ Account + {% if name %} + ({{ name }}) + {% else %} + (no name, disabled) + {% endif %} +

+
+
+
+ +
+ + +
+ + +
+
+
+
+ + +
+ +
+
- - +

Mails

    {% for mail in mails %}