Comments management
Some checks failed
Rust / Rustfmt (push) Has been cancelled
Rust / Clippy (push) Has been cancelled
Rust / Code coverage (push) Has been cancelled
Rust / Test (push) Has been cancelled

This commit is contained in:
Alphonse Paix
2025-10-03 21:12:17 +02:00
parent af9cbdcafb
commit 50a7af2b06
7 changed files with 228 additions and 25 deletions

View File

@@ -1,4 +1,7 @@
use crate::routes::{POSTS_PER_PAGE, SUBS_PER_PAGE, get_posts_count, get_posts_page, get_users};
use crate::routes::{
COMMENTS_PER_PAGE, POSTS_PER_PAGE, SUBS_PER_PAGE, get_comments_count, get_comments_page,
get_posts_count, get_posts_page, get_users,
};
use crate::{
authentication::AuthenticatedUser,
routes::{AppError, get_max_page, get_subs, get_total_subs},
@@ -57,6 +60,14 @@ pub async fn admin_dashboard(
.await
.context("Could not fetch posts count.")?;
let posts_max_page = get_max_page(posts_count, POSTS_PER_PAGE);
let comments_current_page = 1;
let comments = get_comments_page(&connection_pool, comments_current_page)
.await
.context("Could not fetch comments.")?;
let comments_count = get_comments_count(&connection_pool)
.await
.context("Could not fetch comments count.")?;
let comments_max_page = get_max_page(comments_count, COMMENTS_PER_PAGE);
let template = DashboardTemplate {
user,
idempotency_key_1,
@@ -71,6 +82,10 @@ pub async fn admin_dashboard(
posts_current_page,
posts_max_page,
posts_count,
comments,
comments_current_page,
comments_max_page,
comments_count,
};
Ok(Html(template.render().unwrap()).into_response())
}

View File

@@ -1,3 +1,5 @@
use crate::routes::get_max_page;
use crate::templates::CommentsPageDashboardTemplate;
use crate::{
domain::CommentEntry,
routes::AppError,
@@ -5,6 +7,7 @@ use crate::{
templates::{CommentsList, HtmlTemplate, MessageTemplate},
};
use anyhow::Context;
use askama::Template;
use axum::{
Form,
extract::{Path, Query, State},
@@ -77,7 +80,7 @@ async fn insert_comment(
Ok(comment_id)
}
const COMMENTS_PER_PAGE: i64 = 5;
pub const COMMENTS_PER_PAGE: i64 = 5;
#[derive(serde::Deserialize)]
pub struct GetCommentsQueryParams {
@@ -92,13 +95,13 @@ pub async fn get_comments(
connection_pool, ..
}): State<AppState>,
) -> Result<Response, AppError> {
let comments = fetch_comments_page(&connection_pool, post_id, page)
let comments = get_comments_page_for_post(&connection_pool, post_id, page)
.await
.context("Could not fetch comments.")?;
let count = fetch_comments_count(&connection_pool, post_id)
let count = get_comments_count_for_post(&connection_pool, post_id)
.await
.context("Could not fetch comments count")?;
let max_page = get_comments_page_count(count);
let max_page = get_max_page(count, COMMENTS_PER_PAGE);
let template = HtmlTemplate(CommentsList {
comments,
current_page: page,
@@ -107,7 +110,50 @@ pub async fn get_comments(
Ok(template.into_response())
}
pub async fn fetch_comments_page(
#[tracing::instrument(name = "Fetching all comments", skip(connection_pool))]
pub async fn get_all_comments(
Query(GetCommentsQueryParams { page }): Query<GetCommentsQueryParams>,
State(AppState {
connection_pool, ..
}): State<AppState>,
) -> Result<Response, AppError> {
let comments = get_comments_page(&connection_pool, page)
.await
.context("Could not fetch comments.")?;
let count = get_comments_count(&connection_pool)
.await
.context("Could not fetch comments count")?;
let comments_max_page = get_max_page(count, COMMENTS_PER_PAGE);
let template = HtmlTemplate(CommentsPageDashboardTemplate {
comments,
comments_current_page: page,
comments_max_page,
});
Ok(template.into_response())
}
pub async fn delete_comment(
State(AppState {
connection_pool, ..
}): State<AppState>,
crate::routes::Path(comment_id): crate::routes::Path<Uuid>,
) -> Result<Response, AppError> {
let res = sqlx::query!("DELETE FROM comments WHERE comment_id = $1", comment_id)
.execute(&connection_pool)
.await
.context("Failed to delete comment from database.")
.map_err(AppError::unexpected_message)?;
if res.rows_affected() > 1 {
Err(AppError::unexpected_message(anyhow::anyhow!(
"We could not find the comment in the database."
)))
} else {
let template = MessageTemplate::success("The comment has been deleted.".into());
Ok(template.render().unwrap().into_response())
}
}
pub async fn get_comments_page_for_post(
connection_pool: &PgPool,
post_id: Uuid,
page: i64,
@@ -116,13 +162,13 @@ pub async fn fetch_comments_page(
let comments = sqlx::query_as!(
CommentEntry,
"
SELECT comment_id, post_id, author, content, published_at
FROM comments
WHERE post_id = $1
ORDER BY published_at DESC
LIMIT $2
OFFSET $3
",
SELECT comment_id, post_id, author, content, published_at
FROM comments
WHERE post_id = $1
ORDER BY published_at DESC
LIMIT $2
OFFSET $3
",
post_id,
COMMENTS_PER_PAGE,
offset
@@ -132,7 +178,7 @@ pub async fn fetch_comments_page(
Ok(comments)
}
pub async fn fetch_comments_count(
pub async fn get_comments_count_for_post(
connection_pool: &PgPool,
post_id: Uuid,
) -> Result<i64, sqlx::Error> {
@@ -143,10 +189,32 @@ pub async fn fetch_comments_count(
Ok(count)
}
pub fn get_comments_page_count(count: i64) -> i64 {
let mut max_page = count.div_euclid(COMMENTS_PER_PAGE);
if count % COMMENTS_PER_PAGE > 0 {
max_page += 1;
}
max_page
pub async fn get_comments_page(
connection_pool: &PgPool,
page: i64,
) -> Result<Vec<CommentEntry>, sqlx::Error> {
let offset = (page - 1) * COMMENTS_PER_PAGE;
let comments = sqlx::query_as!(
CommentEntry,
"
SELECT comment_id, post_id, author, content, published_at
FROM comments
ORDER BY published_at DESC
LIMIT $1
OFFSET $2
",
COMMENTS_PER_PAGE,
offset
)
.fetch_all(connection_pool)
.await?;
Ok(comments)
}
pub async fn get_comments_count(connection_pool: &PgPool) -> Result<i64, sqlx::Error> {
let count = sqlx::query_scalar!("SELECT count(*) FROM comments")
.fetch_one(connection_pool)
.await?
.unwrap_or(0);
Ok(count)
}

View File

@@ -1,9 +1,9 @@
use crate::routes::get_max_page;
use crate::routes::{COMMENTS_PER_PAGE, get_max_page};
use crate::templates::PostsPageDashboardTemplate;
use crate::{
domain::PostEntry,
routes::{
AppError, Path, Query, fetch_comments_count, fetch_comments_page, get_comments_page_count,
AppError, Path, Query, get_comments_count_for_post, get_comments_page_for_post,
not_found_html,
},
startup::AppState,
@@ -144,11 +144,11 @@ pub async fn see_post(
.to_html()
.context("Could not render markdown with extension.")?;
let current_page = 1;
let comments_count = fetch_comments_count(&connection_pool, post_id)
let comments_count = get_comments_count_for_post(&connection_pool, post_id)
.await
.context("Could not fetch comment count")?;
let max_page = get_comments_page_count(comments_count);
let comments = fetch_comments_page(&connection_pool, post_id, 1)
let max_page = get_max_page(comments_count, COMMENTS_PER_PAGE);
let comments = get_comments_page_for_post(&connection_pool, post_id, 1)
.await
.context("Failed to fetch latest comments")?;
let template = HtmlTemplate(PostTemplate {