Edit profile and templates update
All checks were successful
Rust / Test (push) Successful in 6m6s
Rust / Rustfmt (push) Successful in 22s
Rust / Clippy (push) Successful in 1m36s
Rust / Code coverage (push) Successful in 4m47s

This commit is contained in:
Alphonse Paix
2025-10-06 19:13:51 +02:00
parent da590fb7c6
commit b252216709
27 changed files with 596 additions and 262 deletions

View File

@@ -1,5 +1,7 @@
use crate::authentication::AuthenticatedUser;
use crate::routes::verify_password;
use crate::templates::MessageTemplate;
use crate::session_state::TypedSession;
use crate::templates::{ErrorTemplate, MessageTemplate, UserEditTemplate};
use crate::{
authentication::Role,
domain::{PostEntry, UserEntry},
@@ -9,7 +11,7 @@ use crate::{
};
use anyhow::Context;
use axum::{
Form,
Extension, Form,
extract::{Path, State},
response::{IntoResponse, Response},
};
@@ -17,9 +19,102 @@ use secrecy::{ExposeSecret, SecretString};
use sqlx::PgPool;
use uuid::Uuid;
pub async fn get_user_edit(
Path(username): Path<String>,
Extension(AuthenticatedUser {
user_id,
username: session_username,
..
}): Extension<AuthenticatedUser>,
State(AppState {
connection_pool, ..
}): State<AppState>,
) -> Result<Response, AppError> {
if username != session_username {
let template = HtmlTemplate(ErrorTemplate::Forbidden);
return Ok(template.into_response());
}
let user = sqlx::query_as!(
UserEntry,
r#"
SELECT user_id, username, role as "role: Role", full_name, bio, member_since
FROM users
WHERE user_id = $1
"#,
user_id
)
.fetch_one(&connection_pool)
.await
.context("Could not fetch user in database.")?;
let template = HtmlTemplate(UserEditTemplate { user });
Ok(template.into_response())
}
#[derive(serde::Deserialize)]
pub struct ProfilePath {
pub struct EditProfileForm {
username: String,
full_name: String,
bio: String,
}
pub async fn put_user_edit(
State(AppState {
connection_pool, ..
}): State<AppState>,
session: TypedSession,
Extension(AuthenticatedUser {
user_id,
username: session_username,
..
}): Extension<AuthenticatedUser>,
Path(username): Path<String>,
Form(form): Form<EditProfileForm>,
) -> Result<Response, AppError> {
if username != session_username {
let template = HtmlTemplate(ErrorTemplate::Forbidden);
return Ok(template.into_response());
}
let updated_username = form.username.trim();
if updated_username != session_username
&& sqlx::query!(
"SELECT user_id FROM users WHERE username = $1",
updated_username
)
.fetch_optional(&connection_pool)
.await
.context("Could not fetch users table.")?
.is_some()
{
let template = HtmlTemplate(MessageTemplate::error(
"The username is already taken.".into(),
));
return Ok(template.into_response());
}
let updated_full_name = form.full_name.trim();
let bio = form.bio.trim();
sqlx::query!(
"
UPDATE users
SET username = $1, full_name = $2, bio = $3
WHERE user_id = $4
",
updated_username,
updated_full_name,
bio,
user_id
)
.execute(&connection_pool)
.await
.context("Failed to apply changes.")
.map_err(AppError::FormError)?;
session
.insert_username(updated_username.to_owned())
.await
.context("Could not update session username.")?;
let template = HtmlTemplate(MessageTemplate::success(
"Your profile has been updated.".into(),
));
Ok(template.into_response())
}
#[tracing::instrument(name = "Get users from database", skip(connection_pool))]
@@ -144,12 +239,13 @@ pub async fn delete_user(
Ok(template.into_response())
}
#[tracing::instrument(name = "Fetching user data", skip(connection_pool))]
#[tracing::instrument(name = "Fetching user data", skip(connection_pool, session))]
pub async fn user_profile(
session: TypedSession,
State(AppState {
connection_pool, ..
}): State<AppState>,
Path(ProfilePath { username }): Path<ProfilePath>,
Path(username): Path<String>,
) -> Result<Response, AppError> {
match fetch_user_data(&connection_pool, &username)
.await
@@ -159,7 +255,15 @@ pub async fn user_profile(
let posts = fetch_user_posts(&connection_pool, &user.user_id)
.await
.context("Could not fetch user posts.")?;
let template = HtmlTemplate(UserTemplate { user, posts });
let session_username = session
.get_username()
.await
.context("Could not fetch session username.")?;
let template = HtmlTemplate(UserTemplate {
user,
session_username,
posts,
});
Ok(template.into_response())
}
None => {