Edit posts
All checks were successful
Rust / Test (push) Successful in 5m43s
Rust / Rustfmt (push) Successful in 22s
Rust / Clippy (push) Successful in 1m37s
Rust / Code coverage (push) Successful in 4m49s

Use fix routes for user profile edit handles to make it easier when user decides to change his username
This commit is contained in:
Alphonse Paix
2025-10-06 22:33:05 +02:00
parent b252216709
commit 8b5f55db6f
12 changed files with 313 additions and 180 deletions

View File

@@ -1,10 +1,11 @@
use crate::routes::{COMMENTS_PER_PAGE, get_max_page};
use crate::templates::PostsPageDashboardTemplate;
use crate::authentication::AuthenticatedUser;
use crate::routes::{COMMENTS_PER_PAGE, Query, get_max_page};
use crate::session_state::TypedSession;
use crate::templates::{ErrorTemplate, MessageTemplate, PostsPageDashboardTemplate};
use crate::{
domain::PostEntry,
routes::{
AppError, Path, Query, get_comments_count_for_post, get_comments_page_for_post,
not_found_html,
AppError, Path, get_comments_count_for_post, get_comments_page_for_post, not_found_html,
},
startup::AppState,
templates::{HtmlTemplate, PostListTemplate, PostTemplate, PostsTemplate},
@@ -12,6 +13,7 @@ use crate::{
use anyhow::Context;
use askama::Template;
use axum::{
Extension, Form,
extract::State,
response::{Html, IntoResponse, Redirect, Response},
};
@@ -118,17 +120,65 @@ pub async fn get_posts_count(connection_pool: &PgPool) -> Result<i64, sqlx::Erro
}
#[derive(serde::Deserialize)]
pub struct PostParams {
pub struct EditPostForm {
pub title: String,
pub content: String,
}
#[tracing::instrument(name = "Editing post", skip_all, fields(post_id = %post_id))]
pub async fn update_post(
State(AppState {
connection_pool, ..
}): State<AppState>,
Extension(AuthenticatedUser { user_id, .. }): Extension<AuthenticatedUser>,
Path(post_id): Path<Uuid>,
Form(form): Form<EditPostForm>,
) -> Result<Response, AppError> {
let record = sqlx::query!("SELECT author_id FROM posts WHERE post_id = $1", post_id)
.fetch_optional(&connection_pool)
.await
.context("Could not fetch post author.")?;
match record {
None => Ok(HtmlTemplate(ErrorTemplate::NotFound).into_response()),
Some(record) if record.author_id == user_id => {
sqlx::query!(
"
UPDATE posts
SET title = $1, content = $2 WHERE post_id = $3
",
form.title,
form.content,
post_id
)
.execute(&connection_pool)
.await
.context("Could not update post")?;
Ok(HtmlTemplate(MessageTemplate::success(
"Your changes have been saved.".into(),
))
.into_response())
}
_ => Ok(HtmlTemplate(ErrorTemplate::Forbidden).into_response()),
}
}
#[derive(serde::Deserialize)]
pub struct OriginQueryParam {
origin: Option<Uuid>,
}
#[tracing::instrument(name = "Fetching post from database", skip(connection_pool, origin))]
#[tracing::instrument(
name = "Fetching post from database",
skip(connection_pool, origin, session)
)]
pub async fn see_post(
session: TypedSession,
State(AppState {
connection_pool, ..
}): State<AppState>,
Path(post_id): Path<Uuid>,
Query(PostParams { origin }): Query<PostParams>,
Query(OriginQueryParam { origin }): Query<OriginQueryParam>,
) -> Result<Response, AppError> {
if let Some(origin) = origin {
mark_email_as_opened(&connection_pool, origin).await?;
@@ -140,7 +190,7 @@ pub async fn see_post(
.context(format!("Failed to fetch post #{}", post_id))
.map_err(AppError::unexpected_page)?
{
let post = post
let post_html = post
.to_html()
.context("Could not render markdown with extension.")?;
let current_page = 1;
@@ -152,13 +202,19 @@ pub async fn see_post(
.await
.context("Failed to fetch latest comments")?;
let idempotency_key = Uuid::new_v4().to_string();
let session_username = session
.get_username()
.await
.context("Could not check for session username")?;
let template = HtmlTemplate(PostTemplate {
post,
post_html,
comments,
idempotency_key,
current_page,
max_page,
comments_count,
session_username,
});
Ok(template.into_response())
} else {