use actix_web::{get, http, web, HttpResponse}; use actix_web_static_files::ResourceFiles; use anyhow::Result; use askama_actix::TemplateToResponse; use diesel::prelude::*; use crate::{cache, db}; pub mod templates; pub mod static_dir { include!(concat!(env!("OUT_DIR"), "/generated.rs")); } async fn not_found() -> HttpResponse { let mut resp = templates::StatusCode { status_code: http::StatusCode::NOT_FOUND, message: Some("maybe try a correct url?".to_string()), } .to_response(); *resp.status_mut() = http::StatusCode::NOT_FOUND; resp } #[get("/")] async fn index() -> HttpResponse { templates::Index.to_response() } #[get("/about")] async fn about() -> HttpResponse { templates::About.to_response() } pub fn get_tags_by_post(id: i32, db_conn: &mut diesel::PgConnection) -> Result> { Ok(db::schema::tags::table .select(db::schema::tags::name) .filter( db::schema::tags::id.eq_any( db::schema::post_tags::table .select(db::schema::post_tags::tag_id) .filter(db::schema::post_tags::post_id.eq(id)) .load::(db_conn)?, ), ) .order(db::schema::tags::name) .load::(db_conn)?) } #[get("/posts")] async fn posts( db_pool: web::Data, redis_pool: web::Data, ) -> HttpResponse { let db_conn = &mut match db_pool.get() { Ok(x) => x, Err(e) => return HttpResponse::InternalServerError().body(format!("{:?}", e)), }; let redis_conn = &mut match redis_pool.get() { Ok(x) => x, Err(e) => return HttpResponse::InternalServerError().body(format!("{:?}", e)), }; let posts = match cache::cache_posts(db_conn, redis_conn) { Ok(x) => x, Err(e) => return HttpResponse::InternalServerError().body(format!("{:?}", e)), }; templates::Posts { posts }.to_response() } #[get("/posts/{slug}")] async fn post_by_slug( db_pool: web::Data, redis_pool: web::Data, path: web::Path, ) -> HttpResponse { let slug = path.into_inner(); let db_conn = &mut match db_pool.get() { Ok(x) => x, Err(e) => return HttpResponse::InternalServerError().body(format!("{:?}", e)), }; let redis_conn = &mut match redis_pool.get() { Ok(x) => x, Err(e) => return HttpResponse::InternalServerError().body(format!("{:?}", e)), }; if let Some(post_id) = match db::schema::posts::table .select(db::schema::posts::id) .filter(db::schema::posts::slug.eq(&slug)) .filter(db::schema::posts::active) .first::(db_conn) .optional() { Ok(x) => x, Err(e) => return HttpResponse::InternalServerError().body(format!("{:?}", e)), } { let post = match cache::cache_post(post_id, db_conn, redis_conn) { Ok(x) => x, Err(e) => return HttpResponse::InternalServerError().body(format!("{:?}", e)), }; templates::PostBySlug { post }.to_response() } else { let mut resp = templates::StatusCode { status_code: http::StatusCode::NOT_FOUND, message: Some("this post does not exists... yet".to_string()), } .to_response(); *resp.status_mut() = http::StatusCode::NOT_FOUND; return resp; } // let post_stripped: Option<(i32, String, NaiveDate, Option)> = // match db::schema::posts::table // .select(( // db::schema::posts::id, // db::schema::posts::name, // db::schema::posts::published_at, // db::schema::posts::edited_at, // )) // .filter(db::schema::posts::slug.eq(&slug)) // .filter(db::schema::posts::active) // .first::<(i32, String, NaiveDate, Option)>(db_conn) // .optional() // { // Ok(x) => x, // Err(e) => return HttpResponse::InternalServerError().body(format!("{:?}", e)), // }; // // match post_stripped { // Some(stripped) => { // let (stripped_id, stripped_name, stripped_published_at, stripped_edited_at) = stripped; // let key = cache::keys::post_content(stripped_id); // match match redis::cmd("GET") // .arg(&key) // .query::>(redis_conn.deref_mut()) // { // Ok(x) => x, // Err(e) => return HttpResponse::InternalServerError().body(format!("{:?}", e)), // } { // Some(s) => { // let tags = match get_tags_by_post(stripped_id, db_conn) { // Ok(x) => x, // Err(e) => { // return HttpResponse::InternalServerError().body(format!("{:?}", e)) // } // }; // templates::PostBySlug { // name: stripped_name, // slug, // published_at: stripped_published_at, // edited_at: stripped_edited_at, // tags, // content: s, // } // } // .to_response(), // None => { // let post = match db::schema::posts::table // .filter(db::schema::posts::id.eq(stripped_id)) // .first::(db_conn) // { // Ok(x) => x, // Err(e) => { // return HttpResponse::InternalServerError().body(format!("{:?}", e)) // } // }; // let html = markdown::to_html(&post.content); // match redis::cmd("SET") // .arg(&key) // .arg(&html) // .query::>(redis_conn.deref_mut()) // { // Ok(x) => x, // Err(e) => { // return HttpResponse::InternalServerError().body(format!("{:?}", e)) // } // }; // if let Err(e) = redis::cmd("EXPIRE") // .arg(key) // .arg(config::CONFIG.cache_ttl) // .query::<()>(redis_conn.deref_mut()) // { // return HttpResponse::InternalServerError().body(format!("{:?}", e)); // } // let tags = match get_tags_by_post(stripped_id, db_conn) { // Ok(x) => x, // Err(e) => { // return HttpResponse::InternalServerError().body(format!("{:?}", e)) // } // }; // templates::PostBySlug { // name: post.name, // slug: post.slug, // published_at: stripped_published_at, // edited_at: stripped_edited_at, // tags, // content: html, // } // .to_response() // } // } // } // None => { // let mut resp = templates::StatusCode { // status_code: http::StatusCode::NOT_FOUND, // message: Some("this post does not exists... yet".to_string()), // } // .to_response(); // *resp.status_mut() = http::StatusCode::NOT_FOUND; // resp // } // } } #[get("/tags/{name}")] async fn tag_by_name( db_pool: web::Data, redis_pool: web::Data, path: web::Path, ) -> HttpResponse { const MESSAGE: &str = "this post does not exists... yet"; let name = path.into_inner(); let db_conn = &mut match db_pool.get() { Ok(x) => x, Err(e) => return HttpResponse::InternalServerError().body(format!("{:?}", e)), }; let redis_conn = &mut match redis_pool.get() { Ok(x) => x, Err(e) => return HttpResponse::InternalServerError().body(format!("{:?}", e)), }; let tag_id = match db::schema::tags::table .select(db::schema::tags::id) .filter(db::schema::tags::name.eq(&name)) .first::(db_conn) { Ok(x) => x, Err(e) => return HttpResponse::InternalServerError().body(format!("{:?}", e)), }; let posts_cache = match cache::cache_tag_posts(tag_id, db_conn, redis_conn) { Ok(x) => x, Err(e) => return HttpResponse::InternalServerError().body(format!("{:?}", e)), }; templates::TagByName { name, posts: posts_cache, } .to_response() } fn setup_routes(cfg: &mut web::ServiceConfig) { let generated = static_dir::generate(); cfg.service(index) .service(about) .service(posts) .service(ResourceFiles::new("/static", generated)) .service(post_by_slug) .service(tag_by_name) .default_service(web::route().to(not_found)); } pub fn init(cfg: &mut web::ServiceConfig) { cfg.app_data(web::Data::new(db::pool().unwrap())) .app_data(web::Data::new(cache::pool().unwrap())); setup_routes(cfg); }