phonograph/interim-server/src/app.rs
2025-10-09 08:01:01 +00:00

74 lines
2.1 KiB
Rust

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::{
auth, errors::AppError, sessions::PgStore, settings::Settings,
workspace_pooler::WorkspacePooler,
};
/// 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<Self> {
let app_db = PgPoolOptions::new()
.max_connections(settings.app_db_max_connections)
.connect(settings.database_url.as_str())
.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<App> 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<App> for AppDbConn {
type Rejection = AppError;
async fn from_request_parts(_: &mut Parts, state: &App) -> Result<Self, Self::Rejection> {
let conn = state.app_db.acquire().await?;
Ok(Self(AppDbClient::from_pool_conn(conn)))
}
}