use crate::{datasources::comments::CommentsDatasource, state::AppState, utils::datetime::*}; use axum::{ extract::{Path, State}, http::StatusCode, response::IntoResponse, routing::{get, post}, Json, }; use chrono::Utc; use fred::types::{Expiration, SetOptions}; use serde::{Deserialize, Serialize}; #[derive(Deserialize, Serialize, Debug)] pub struct CommentInputPayload { pub name: String, pub body: String, pub post_id: i32, } #[derive(Deserialize, Serialize)] pub struct CommentPathParams { id: i32, } #[derive(Deserialize, Serialize)] pub struct Pagination { pub page_number: i64, pub page_size: i64, } #[derive(sqlx::FromRow, Deserialize, Serialize, Debug, Clone)] pub struct Comment { pub comment_id: i32, pub name: String, pub body: String, #[serde(serialize_with = "serialize_datetime")] #[serde(deserialize_with = "deserialize_datetime")] pub created_at: Option>, } pub struct CommentsRoute; impl CommentsRoute { pub fn routes(app_state: &AppState) -> axum::Router { // add more comment routes here! axum::Router::new() .route("/post/:id", get(CommentsRoute::get_post_comments)) .route("/add", post(CommentsRoute::insert_comment)) .route("/index", get(CommentsRoute::get_comments_index)) .with_state(app_state.clone()) } async fn get_post_comments( State(app_state): State, Path(params): Path, ) -> impl IntoResponse { let state = app_state.lock().await; match CommentsDatasource::get_posts_comments(&state.database, params.id).await { Ok(c) => Ok(Json(c)), Err(e) => Err((StatusCode::INTERNAL_SERVER_ERROR, e.to_string())), } } async fn insert_comment( State(app_state): State, Json(input): Json, ) -> impl IntoResponse { let state = app_state.lock().await; match CommentsDatasource::insert_comment(&state.database, input).await { Ok(c) => { if let co = &c { let co = c.clone(); let state = app_state.clone(); tokio::spawn(async move { tracing::info!("update cache if key already exists!"); let mut s = state.lock().await; let _ = s .cache .set( format!("comments:{}", co.comment_id), &co, Some(Expiration::EX(60 * 15)), Some(SetOptions::XX), false, ) .await; }); } Ok((StatusCode::CREATED, Json(c))) } Err(e) => Err((StatusCode::INTERNAL_SERVER_ERROR, e.to_string())), } } async fn get_comments_index( State(app_state): State, Json(pagination): Json, ) -> impl IntoResponse { let state = app_state.lock().await; match CommentsDatasource::get_index_comments(&state.database, pagination).await { Ok(c) => Ok(Json(c)), Err(e) => Err((StatusCode::INTERNAL_SERVER_ERROR, e.to_string())), } } }