Posts dedicated page with cards linking to specific post
This commit is contained in:
@@ -2,6 +2,7 @@ mod admin;
|
||||
mod health_check;
|
||||
mod home;
|
||||
mod login;
|
||||
mod posts;
|
||||
mod subscriptions;
|
||||
mod subscriptions_confirm;
|
||||
|
||||
@@ -9,5 +10,6 @@ pub use admin::*;
|
||||
pub use health_check::*;
|
||||
pub use home::*;
|
||||
pub use login::*;
|
||||
pub use posts::*;
|
||||
pub use subscriptions::*;
|
||||
pub use subscriptions_confirm::*;
|
||||
|
||||
@@ -9,6 +9,7 @@ use askama::Template;
|
||||
use axum::{
|
||||
Form, Json,
|
||||
extract::State,
|
||||
http::HeaderMap,
|
||||
response::{Html, IntoResponse, Response},
|
||||
};
|
||||
use axum::{http::StatusCode, response::Redirect};
|
||||
@@ -114,11 +115,9 @@ pub async fn post_login(
|
||||
.await
|
||||
.map_err(|e| LoginError::UnexpectedError(e.into()))?;
|
||||
|
||||
let mut response = Redirect::to("/admin/dashboard").into_response();
|
||||
response
|
||||
.headers_mut()
|
||||
.insert("HX-Redirect", "/admin/dashboard".parse().unwrap());
|
||||
Ok(response)
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("HX-Redirect", "/admin/dashboard".parse().unwrap());
|
||||
Ok((StatusCode::OK, headers).into_response())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
103
src/routes/posts.rs
Normal file
103
src/routes/posts.rs
Normal file
@@ -0,0 +1,103 @@
|
||||
use crate::startup::AppState;
|
||||
use askama::Template;
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
response::{Html, IntoResponse, Response},
|
||||
};
|
||||
use chrono::{DateTime, Utc};
|
||||
use reqwest::StatusCode;
|
||||
use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
|
||||
struct PostEntry {
|
||||
post_id: Uuid,
|
||||
author: Option<String>,
|
||||
title: String,
|
||||
content: String,
|
||||
published_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl PostEntry {
|
||||
#[allow(dead_code)]
|
||||
fn formatted_date(&self) -> String {
|
||||
self.published_at.format("%B %d, %Y").to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "../templates/posts.html")]
|
||||
struct PostsTemplate {
|
||||
posts: Vec<PostEntry>,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "../templates/post.html")]
|
||||
struct PostTemplate {
|
||||
post: PostEntry,
|
||||
}
|
||||
|
||||
pub async fn list_posts(
|
||||
State(AppState {
|
||||
connection_pool, ..
|
||||
}): State<AppState>,
|
||||
) -> Response {
|
||||
match get_latest_posts(&connection_pool, 5).await {
|
||||
Err(e) => {
|
||||
tracing::error!("Could not fetch latest posts: {}", e);
|
||||
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
||||
}
|
||||
Ok(posts) => {
|
||||
let template = PostsTemplate { posts };
|
||||
Html(template.render().unwrap()).into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_latest_posts(connection_pool: &PgPool, n: i64) -> Result<Vec<PostEntry>, sqlx::Error> {
|
||||
sqlx::query_as!(
|
||||
PostEntry,
|
||||
r#"
|
||||
SELECT p.post_id, u.username AS author, p.title, p.content, p.published_at
|
||||
FROM posts p
|
||||
LEFT JOIN users u ON p.author_id = u.user_id
|
||||
ORDER BY p.published_at DESC
|
||||
LIMIT $1
|
||||
"#,
|
||||
n
|
||||
)
|
||||
.fetch_all(connection_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn see_post(
|
||||
State(AppState {
|
||||
connection_pool, ..
|
||||
}): State<AppState>,
|
||||
Path(post_id): Path<Uuid>,
|
||||
) -> Response {
|
||||
match get_post(&connection_pool, post_id).await {
|
||||
Err(e) => {
|
||||
tracing::error!("Could not fetch post #{}: {}", post_id, e);
|
||||
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
||||
}
|
||||
Ok(post) => {
|
||||
let template = PostTemplate { post };
|
||||
Html(template.render().unwrap()).into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_post(connection_pool: &PgPool, post_id: Uuid) -> Result<PostEntry, sqlx::Error> {
|
||||
sqlx::query_as!(
|
||||
PostEntry,
|
||||
r#"
|
||||
SELECT p.post_id, u.username AS author, p.title, p.content, p.published_at
|
||||
FROM posts p
|
||||
LEFT JOIN users u ON p.author_id = u.user_id
|
||||
WHERE p.post_id = $1
|
||||
"#,
|
||||
post_id
|
||||
)
|
||||
.fetch_one(connection_pool)
|
||||
.await
|
||||
}
|
||||
@@ -129,6 +129,8 @@ pub fn app(
|
||||
.route("/health_check", get(health_check))
|
||||
.route("/subscriptions", post(subscribe))
|
||||
.route("/subscriptions/confirm", get(confirm))
|
||||
.route("/posts", get(list_posts))
|
||||
.route("/posts/{post_id}", get(see_post))
|
||||
.nest("/admin", admin_routes)
|
||||
.layer(
|
||||
TraceLayer::new_for_http().make_span_with(|request: &Request<_>| {
|
||||
|
||||
Reference in New Issue
Block a user