104 lines
2.5 KiB
Rust
104 lines
2.5 KiB
Rust
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
|
|
}
|