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; use crate::auth; use crate::base_pooler::WorkspacePooler; use crate::sessions::PgStore; use crate::settings::Settings; /// Global app configuration #[derive(Clone, Debug)] pub struct App { pub app_db: sqlx::PgPool, pub workspace_pooler: WorkspacePooler, 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 workspace_pooler = WorkspacePooler::builder() .app_db_pool(app_db.clone()) .db_role_prefix(settings.db_role_prefix.clone()) .build()?; Ok(Self { app_db, workspace_pooler, oauth_client, reqwest_client, session_store, settings, }) } } /// State extractor for shared reqwest client #[derive(Clone)] pub struct ReqwestClient(pub reqwest::Client); impl FromRef for ReqwestClient { fn from_ref(state: &App) -> Self { ReqwestClient(state.reqwest_client.clone()) } } /// Extractor to automatically obtain a sqlx connection for the application /// database. pub struct AppDbConn(pub AppDbClient); impl FromRequestParts for AppDbConn { type Rejection = AppError; async fn from_request_parts(_: &mut Parts, state: &App) -> Result { let conn = state.app_db.acquire().await?; Ok(Self(AppDbClient::from_pool_conn(conn))) } }