phonograph/src/app_state.rs

91 lines
2.8 KiB
Rust
Raw Normal View History

2025-05-02 23:48:54 -07:00
use std::sync::Arc;
use anyhow::Result;
use axum::{
extract::{FromRef, FromRequestParts},
http::request::Parts,
};
use deadpool_diesel::postgres::{Connection, Pool};
use oauth2::basic::BasicClient;
use crate::{
abstract_::escape_identifier, app_error::AppError, auth, nav::NavbarBuilder, sessions::PgStore,
settings::Settings,
};
/// Global app configuration
pub struct App {
pub db_pool: Pool,
pub navbar_template: NavbarBuilder,
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<Self> {
let database_url = settings.database_url.clone();
let manager = deadpool_diesel::postgres::Manager::from_config(
database_url,
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(format!(
"SET ROLE {}",
escape_identifier(&settings.pg_root_role)
)),
),
},
);
let db_pool = deadpool_diesel::postgres::Pool::builder(manager).build()?;
let session_store = PgStore::new(db_pool.clone());
let reqwest_client = reqwest::ClientBuilder::new().https_only(true).build()?;
let oauth_client = auth::new_oauth_client(&settings)?;
Ok(Self {
db_pool,
navbar_template: NavbarBuilder::default().with_base_path(&settings.base_path),
oauth_client,
reqwest_client,
session_store,
settings,
})
}
}
/// Global app configuration, arced for relatively inexpensive clones
pub type AppState = Arc<App>;
/// State extractor for shared reqwest client
#[derive(Clone)]
pub struct ReqwestClient(pub reqwest::Client);
impl<S> FromRef<S> for ReqwestClient
where
S: Into<AppState> + Clone,
{
fn from_ref(state: &S) -> Self {
ReqwestClient(Into::<AppState>::into(state.clone()).reqwest_client.clone())
}
}
/// Extractor to automatically obtain a Deadpool database connection
pub struct DbConn(pub Connection);
impl<S> FromRequestParts<S> for DbConn
where
S: Into<AppState> + Clone + Sync,
{
type Rejection = AppError;
async fn from_request_parts(_: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
let conn = Into::<AppState>::into(state.clone()).db_pool.get().await?;
Ok(Self(conn))
}
}