use std::sync::{Arc, Mutex}; use axum::{ http::{Response, StatusCode}, response::{IntoResponse, Redirect}, routing::{get, post, Router}, Extension, }; use rand::{seq::SliceRandom, thread_rng, RngCore, SeedableRng}; use rand_chacha::ChaCha8Rng; use rand_core::OsRng; use sqlx::PgPool; use std::error::Error; use tower_http::services::ServeDir; use crate::{ auth::{auth, logout_response, post_login, post_signup}, database::{ link::{Link, LinkType}, PsqlData, }, }; use super::{ admin, blog::{self, get_articles_date_sorted}, garden, templates::{ AboutTemplate, AiTemplate, BlogrollTemplate, ContactTemplate, GiftsTemplate, HomeTemplate, HtmlTemplate, InterestsTemplate, LinksPageTemplate, LoginTemplate, NowTemplate, ResumeTemplate, SignupTemplate, UsesTemplate, WorkTemplate, }, }; pub fn get_router(pool: PgPool) -> Router { let assets_path = std::env::current_dir().unwrap(); let random = ChaCha8Rng::seed_from_u64(OsRng.next_u64()); let middleware_database = pool.clone(); Router::new() .nest("/blog", blog::get_router()) .nest("/garden", garden::get_router()) .nest("/admin", admin::get_router()) .nest_service( "/assets", ServeDir::new(format!("{}/assets", assets_path.to_str().unwrap())), ) .route("/", get(home)) .route("/now", get(now)) .route("/about", get(about)) .route("/contact", get(contact)) .route("/uses", get(uses)) .route("/ai", get(ai)) .route("/blogroll", get(blogroll)) .route("/links", get(links)) .route("/interests", get(interests)) .route("/resume", get(resume)) .route("/gifts", get(gifts)) .route("/hire", get(work)) .route("/login", get(get_login).post(post_login)) .route("/signup", get(get_signup).post(post_signup)) .route("/logout", post(logout_response)) .route( "/robots.txt", get(|| async { Redirect::permanent("/assets/robots.txt") }), ) .layer(axum::middleware::from_fn(move |req, next| { auth(req, next, middleware_database.clone()) })) .layer(Extension(pool)) .layer(Extension(Arc::new(Mutex::new(random)))) } async fn home(Extension(pool): Extension) -> impl IntoResponse { let mut articles = get_articles_date_sorted(&pool).await.unwrap(); articles.truncate(5); HtmlTemplate(HomeTemplate { recent_articles: articles, }) } async fn now() -> impl IntoResponse { HtmlTemplate(NowTemplate {}) } async fn about() -> impl IntoResponse { HtmlTemplate(AboutTemplate {}) } async fn contact() -> impl IntoResponse { HtmlTemplate(ContactTemplate {}) } async fn uses() -> impl IntoResponse { HtmlTemplate(UsesTemplate {}) } async fn ai() -> impl IntoResponse { HtmlTemplate(AiTemplate {}) } async fn interests() -> impl IntoResponse { HtmlTemplate(InterestsTemplate {}) } async fn resume() -> impl IntoResponse { HtmlTemplate(ResumeTemplate {}) } async fn work() -> impl IntoResponse { HtmlTemplate(WorkTemplate {}) } async fn gifts() -> impl IntoResponse { HtmlTemplate(GiftsTemplate {}) } async fn blogroll(Extension(pool): Extension) -> impl IntoResponse { let blogroll_page = BlogrollTemplate { blogs: match get_links_as_list(&pool, LinkType::BLOG).await { Ok(list) => list, Err(_) => Vec::new(), }, }; HtmlTemplate(blogroll_page) } async fn links(Extension(pool): Extension) -> impl IntoResponse { let links_page = LinksPageTemplate { articles: match get_links_as_list(&pool, LinkType::ARTICLE).await { Ok(list) => list, Err(_) => Vec::new(), }, }; HtmlTemplate(links_page) } pub async fn get_links_as_list( pool: &PgPool, link_type: LinkType, ) -> Result, Box> { let mut links: Vec = match Link::read_all(pool).await { Ok(a) => a.iter().map(|x| *x.clone()).collect(), Err(_) => Vec::new(), }; let mut rng = thread_rng(); links.shuffle(&mut rng); let list: Vec = links .into_iter() .filter(|link| link.link_type == link_type) .collect(); Ok(list) } pub fn error_page(err: &dyn std::error::Error) -> impl IntoResponse { Response::builder() .status(StatusCode::INTERNAL_SERVER_ERROR) .body(format!("Err: {}", err)) .unwrap() } pub async fn get_login() -> impl IntoResponse { HtmlTemplate(LoginTemplate { username: None }) } pub async fn get_signup() -> impl IntoResponse { HtmlTemplate(SignupTemplate {}) }