use axum::{ extract::{Path, State}, http::StatusCode, response::IntoResponse, routing::get, Json, }; use fred::types::Expiration; use serde::{Deserialize, Serialize}; use crate::{datasources::authors::AuthorsDatasource, state::AppState}; use super::comments::Pagination; #[derive(Deserialize, Serialize, Clone)] pub struct Author { pub author_id: i32, pub first_name: String, pub last_name: String, pub bio: Option, pub image: Option, } #[derive(Deserialize, Serialize, Clone)] pub struct AuthorGetOneParams { pub id: i32, } pub struct AuthorsRoute; impl AuthorsRoute { pub fn routes(app_state: &AppState) -> axum::Router { axum::Router::new() .route("/", get(AuthorsRoute::get_all)) .route("/:id", get(AuthorsRoute::get_one)) .route("/:id/posts", get(AuthorsRoute::get_authors_posts)) .with_state(app_state.clone()) } async fn get_all( State(app_state): State, Json(pagination): Json, ) -> impl IntoResponse { let mut state = app_state.lock().await; let cached: Option> = state .cache .get(String::from("authors:all")) .await .unwrap_or(None); if let Some(authors) = cached { tracing::info!("grabbing all authors from cache"); return Ok(Json(authors)); } match AuthorsDatasource::get_all(&state.database, pagination).await { Ok(authors) => { tracing::info!("grabbing all authors from the database"); if let a = &authors { let author_cloned = a.clone(); let state = app_state.clone(); tracing::info!("storing database data in cache"); tokio::spawn(async move { let mut s = state.lock().await; let _ = s .cache .set( String::from("authors:all"), &author_cloned, Some(Expiration::EX(5)), None, false, ) .await; }); } Ok(Json(authors)) } Err(e) => Err((StatusCode::INTERNAL_SERVER_ERROR, e.to_string())), } } async fn get_one( State(app_state): State, Path(params): Path, ) -> impl IntoResponse { let mut state = app_state.lock().await; let cached: Option = state .cache .get(format!("authors:{}", params.id)) .await .unwrap_or(None); if let Some(author) = cached { tracing::info!("grabbing one author from cache"); return Ok(Json(author)); } match AuthorsDatasource::get_one(&state.database, params.id).await { Ok(author) => { tracing::info!("grabbing all authors from the database"); if let a = &author { let author_cloned = a.clone(); let state = app_state.clone(); tracing::info!("storing database data in cache"); tokio::spawn(async move { let mut s = state.lock().await; let _ = s .cache .set( format!("authors:{}", author_cloned.author_id), &author_cloned, Some(Expiration::EX(5)), None, false, ) .await; }); } Ok(Json(author)) } Err(e) => Err((StatusCode::INTERNAL_SERVER_ERROR, e.to_string())), } } async fn get_authors_posts( State(app_state): State, Path(params): Path, ) -> impl IntoResponse { let state = app_state.lock().await; match AuthorsDatasource::get_authors_posts(&state.database, params.id).await { Ok(p) => Ok(Json(p)), Err(e) => Err((StatusCode::INTERNAL_SERVER_ERROR, e.to_string())), } } }