use std::sync::Arc; use anyhow::Result; use axum::{ extract::{FromRef, FromRequestParts}, http::request::Parts, }; use oauth2::basic::BasicClient; use crate::{app_error::AppError, auth, nav::NavbarBuilder, sessions::PgStore, settings::Settings}; /// Global app configuration pub struct App { pub diesel_pool: deadpool_diesel::postgres::Pool, pub navbar_template: NavbarBuilder, pub oauth_client: BasicClient, pub pg_pool: deadpool_postgres::Pool, pub reqwest_client: reqwest::Client, pub session_store: PgStore, pub settings: Settings, } impl App { /// Initialize global application functions based on config values pub async fn from_settings(settings: Settings) -> Result { let database_url = settings.database_url.clone(); let diesel_manager = deadpool_diesel::postgres::Manager::from_config( database_url.clone(), deadpool_diesel::Runtime::Tokio1, deadpool_diesel::ManagerConfig { // Reset role after each interaction is recycled so that user // sessions remain isolated by deadpool interaction recycling_method: deadpool_diesel::RecyclingMethod::CustomQuery( std::borrow::Cow::Owned("RESET ROLE;".to_owned()), ), }, ); let diesel_pool = deadpool_diesel::postgres::Pool::builder(diesel_manager).build()?; let pg_config = deadpool_postgres::Config { url: Some(database_url), manager: Some(deadpool_postgres::ManagerConfig { recycling_method: deadpool_postgres::RecyclingMethod::Clean, }), ..Default::default() }; let pg_pool = pg_config.create_pool( Some(deadpool_postgres::Runtime::Tokio1), postgres_native_tls::MakeTlsConnector::new(native_tls::TlsConnector::new()?), )?; let session_store = PgStore::new(diesel_pool.clone()); let reqwest_client = reqwest::ClientBuilder::new().https_only(true).build()?; let oauth_client = auth::new_oauth_client(&settings)?; Ok(Self { diesel_pool, navbar_template: NavbarBuilder::default().with_base_path(&settings.base_path), oauth_client, pg_pool, reqwest_client, session_store, settings, }) } } /// Global app configuration, arced for relatively inexpensive clones pub type AppState = Arc; /// State extractor for shared reqwest client #[derive(Clone)] pub struct ReqwestClient(pub reqwest::Client); impl FromRef for ReqwestClient where S: Into + Clone, { fn from_ref(state: &S) -> Self { ReqwestClient(Into::::into(state.clone()).reqwest_client.clone()) } } /// Extractor to automatically obtain a Deadpool Diesel connection pub struct DieselConn(pub deadpool_diesel::postgres::Connection); impl FromRequestParts for DieselConn where S: Into + Clone + Sync, { type Rejection = AppError; async fn from_request_parts(_: &mut Parts, state: &S) -> Result { let conn = Into::::into(state.clone()) .diesel_pool .get() .await?; Ok(Self(conn)) } } /// Extractor to automatically obtain a Deadpool tokio-postgres connection pub struct PgConn(pub deadpool_postgres::Object); impl FromRequestParts for PgConn where S: Into + Clone + Sync, { type Rejection = AppError; async fn from_request_parts(_: &mut Parts, state: &S) -> Result { let conn = Into::::into(state.clone()).pg_pool.get().await?; Ok(Self(conn)) } }