use crate::{ datasources::comments::CommentsDatasource, state::AppState, utils::{ datetime::*, pagination::{Pagination, PaginationQuery}, }, }; use axum::{ extract::{Path, Query, State}, http::StatusCode, response::IntoResponse, routing::{get, post}, Json, }; use cache::{Expiration, SetOptions}; use chrono::Utc; 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(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("/comments/post/{id}", get(CommentsRoute::get_post_comments)) .route("/comments/add", post(CommentsRoute::insert_comment)) .route("/comments/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, Query(query): Query, ) -> impl IntoResponse { let pagination = Pagination { page: query.page.unwrap_or(1), limit: query.limit.unwrap_or(12), }; 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())), } } }