Allow to set an account name
This commit is contained in:
parent
87d99c4875
commit
21d93e379c
3 changed files with 101 additions and 25 deletions
2
migrations/20230829220628_name.sql
Normal file
2
migrations/20230829220628_name.sql
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
-- Add migration script here
|
||||||
|
ALTER TABLE accounts ADD COLUMN name TEXT UNIQUE;
|
||||||
76
src/main.rs
76
src/main.rs
|
|
@ -567,6 +567,11 @@ async fn home(
|
||||||
)
|
)
|
||||||
.fetch(&state.db);
|
.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();
|
let mut aliases: HashMap<_, _> = aliases.into_iter().map(|a| (a.mail, Vec::new())).collect();
|
||||||
while let Some(alias) = alias_stream.try_next().await? {
|
while let Some(alias) = alias_stream.try_next().await? {
|
||||||
aliases.get_mut(&alias.alias).unwrap().push(alias.recipient);
|
aliases.get_mut(&alias.alias).unwrap().push(alias.recipient);
|
||||||
|
|
@ -582,6 +587,7 @@ async fn home(
|
||||||
context.insert("mails", &mails);
|
context.insert("mails", &mails);
|
||||||
context.insert("mail_domain", &state.mail_domain);
|
context.insert("mail_domain", &state.mail_domain);
|
||||||
context.insert("aliases", &aliases);
|
context.insert("aliases", &aliases);
|
||||||
|
context.insert("name", &name);
|
||||||
if let Some(err) = query.user_error {
|
if let Some(err) = query.user_error {
|
||||||
tracing::info!("User error: {err:?}");
|
tracing::info!("User error: {err:?}");
|
||||||
context.insert("user_error", &err.to_string());
|
context.insert("user_error", &err.to_string());
|
||||||
|
|
@ -649,6 +655,7 @@ async fn delete_alias(
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
enum UserError {
|
enum UserError {
|
||||||
MailAlreadyExists,
|
MailAlreadyExists,
|
||||||
|
NameAlreadyExists(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for UserError {
|
impl Display for UserError {
|
||||||
|
|
@ -657,9 +664,24 @@ impl Display for UserError {
|
||||||
UserError::MailAlreadyExists => {
|
UserError::MailAlreadyExists => {
|
||||||
write!(f, "email address is already used by another user")
|
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<UserError> 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(
|
async fn add_mail(
|
||||||
state: State<Arc<AppState>>,
|
state: State<Arc<AppState>>,
|
||||||
|
|
@ -677,13 +699,7 @@ async fn add_mail(
|
||||||
.expect("count should not be null");
|
.expect("count should not be null");
|
||||||
|
|
||||||
if has_mail != 0 {
|
if has_mail != 0 {
|
||||||
Ok(Redirect::to(&format!(
|
Ok(UserError::MailAlreadyExists.into())
|
||||||
"/?{}",
|
|
||||||
serde_urlencoded::to_string(&HomeQuery {
|
|
||||||
user_error: Some(UserError::MailAlreadyExists)
|
|
||||||
})
|
|
||||||
.expect("could not generate query")
|
|
||||||
)))
|
|
||||||
} else {
|
} else {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"INSERT INTO emails (id, mail) VALUES ($1, $2) ON CONFLICT DO NOTHING",
|
"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");
|
.expect("count should not be null");
|
||||||
|
|
||||||
if has_mail != 0 {
|
if has_mail != 0 {
|
||||||
Ok(Redirect::to(&format!(
|
Ok(UserError::MailAlreadyExists.into())
|
||||||
"/?{}",
|
|
||||||
serde_urlencoded::to_string(&HomeQuery {
|
|
||||||
user_error: Some(UserError::MailAlreadyExists)
|
|
||||||
})
|
|
||||||
.expect("could not generate query")
|
|
||||||
)))
|
|
||||||
} else {
|
} else {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"INSERT INTO emails (id, mail, alias) VALUES ($1, $2, true) ON CONFLICT DO NOTHING",
|
"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("/"))
|
Ok(Redirect::to("/"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct Name {
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip(state))]
|
||||||
|
async fn set_name(
|
||||||
|
state: State<Arc<AppState>>,
|
||||||
|
User(user): User,
|
||||||
|
Form(name): Form<Name>,
|
||||||
|
) -> Result<Redirect, Error> {
|
||||||
|
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]
|
#[tokio::main]
|
||||||
async fn main() -> color_eyre::Result<()> {
|
async fn main() -> color_eyre::Result<()> {
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
|
|
@ -871,6 +918,7 @@ async fn main() -> color_eyre::Result<()> {
|
||||||
.route("/alias/recipient/delete", post(delete_recipient))
|
.route("/alias/recipient/delete", post(delete_recipient))
|
||||||
.route("/alias/delete", post(delete_alias))
|
.route("/alias/delete", post(delete_alias))
|
||||||
.route("/password", post(set_password))
|
.route("/password", post(set_password))
|
||||||
|
.route("/name", post(set_name))
|
||||||
.fallback(page_not_found)
|
.fallback(page_not_found)
|
||||||
.with_state(Arc::new(AppState {
|
.with_state(Arc::new(AppState {
|
||||||
db,
|
db,
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,30 @@
|
||||||
{% if user_error %}
|
{% if user_error %}
|
||||||
<div class="alert alert-danger">{{ user_error }}</div>
|
<div class="alert alert-danger">{{ user_error }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<h2>Password</h2>
|
<h2>
|
||||||
|
Account
|
||||||
|
{% if name %}
|
||||||
|
({{ name }})
|
||||||
|
{% else %}
|
||||||
|
(no name, disabled)
|
||||||
|
{% endif %}
|
||||||
|
</h2>
|
||||||
|
<div class="container text-center">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<form action="/name" method="post">
|
||||||
|
<div class="form-floating mb-3">
|
||||||
|
<input type="text"
|
||||||
|
class="form-control"
|
||||||
|
id="newName"
|
||||||
|
placeholder="account name"
|
||||||
|
name="name">
|
||||||
|
<label for="newName">New Name</label>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Change name</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
<form action="/password" method="post">
|
<form action="/password" method="post">
|
||||||
<div class="form-floating mb-3">
|
<div class="form-floating mb-3">
|
||||||
<input type="password"
|
<input type="password"
|
||||||
|
|
@ -111,6 +134,9 @@
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-primary">Change password</button>
|
<button type="submit" class="btn btn-primary">Change password</button>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<h2 class="title is-2 mt-2">Mails</h2>
|
<h2 class="title is-2 mt-2">Mails</h2>
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
{% for mail in mails %}
|
{% for mail in mails %}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue