use std::sync::Arc; use anyhow::Result; use axum::{ extract::{FromRef, FromRequestParts}, http::request::Parts, }; use interim_models::client::AppDbClient; use oauth2::basic::BasicClient; use sqlx::postgres::PgPoolOptions; use crate::{ app_error::AppError, auth, base_pooler::BasePooler, sessions::PgStore, settings::Settings, }; /// Global app configuration pub struct App { pub app_db: sqlx::PgPool, pub base_pooler: BasePooler, pub oauth_client: BasicClient, 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 app_db = PgPoolOptions::new() .max_connections(settings.app_db_max_connections) .connect(&settings.database_url) .await?; let session_store = PgStore::new(app_db.clone()); let reqwest_client = reqwest::ClientBuilder::new().https_only(true).build()?; let oauth_client = auth::new_oauth_client(&settings)?; let base_pooler = BasePooler::new_with_app_db(app_db.clone()); Ok(Self { app_db, base_pooler, oauth_client, 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 AppDbConn(pub AppDbClient); impl FromRequestParts for AppDbConn where S: Into + Clone + Sync, { type Rejection = AppError; async fn from_request_parts(_: &mut Parts, state: &S) -> Result { let conn = Into::::into(state.clone()) .app_db .acquire() .await?; Ok(Self(AppDbClient::from_pool_conn(conn))) } }