92 lines
2.4 KiB
Rust
92 lines
2.4 KiB
Rust
use axum::{async_trait, extract::FromRequestParts, http::request::Parts};
|
|
use diesel::{pg::PgConnection, prelude::*, sql_types::Text};
|
|
use uuid::Uuid;
|
|
|
|
use crate::{
|
|
db::PgPool,
|
|
error::{AppError, AppResult},
|
|
models::Tenant,
|
|
schema::tenants::dsl,
|
|
state::AppState,
|
|
};
|
|
|
|
pub struct TenantRepository;
|
|
|
|
impl TenantRepository {
|
|
pub fn get_by_id(conn: &mut PgConnection, tenant_id: Uuid) -> AppResult<Tenant> {
|
|
dsl::tenants.find(tenant_id).first(conn).map_err(Into::into)
|
|
}
|
|
|
|
pub fn get_by_slug(conn: &mut PgConnection, slug: &str) -> AppResult<Tenant> {
|
|
dsl::tenants
|
|
.filter(dsl::slug.eq(slug))
|
|
.first(conn)
|
|
.map_err(Into::into)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct TenantService {
|
|
pool: PgPool,
|
|
}
|
|
|
|
impl TenantService {
|
|
pub fn new(pool: PgPool) -> Self {
|
|
Self { pool }
|
|
}
|
|
|
|
pub fn get_by_id(&self, tenant_id: Uuid) -> AppResult<Tenant> {
|
|
let tenant = self.load(|conn| TenantRepository::get_by_id(conn, tenant_id))?;
|
|
Ok(tenant)
|
|
}
|
|
|
|
pub fn get_by_slug(&self, slug: &str) -> AppResult<Tenant> {
|
|
let slug_owned = slug.to_owned();
|
|
let tenant = self.load(|conn| TenantRepository::get_by_slug(conn, &slug_owned))?;
|
|
Ok(tenant)
|
|
}
|
|
|
|
pub fn tenant_id_for_slug(&self, slug: &str) -> AppResult<Uuid> {
|
|
Ok(self.get_by_slug(slug)?.id)
|
|
}
|
|
|
|
fn load<F>(&self, loader: F) -> AppResult<Tenant>
|
|
where
|
|
F: FnOnce(&mut PgConnection) -> AppResult<Tenant>,
|
|
{
|
|
let mut conn = self
|
|
.pool
|
|
.get()
|
|
.map_err(|err| AppError::internal(format!("database pool error: {err}")))?;
|
|
let tenant = loader(&mut conn)?;
|
|
Ok(tenant)
|
|
}
|
|
}
|
|
|
|
pub fn apply_tenant_guc(conn: &mut PgConnection, tenant_id: Uuid) -> AppResult<()> {
|
|
diesel::sql_query("SELECT set_config('papercrate.tenant_id', $1, true)")
|
|
.bind::<Text, _>(tenant_id.to_string())
|
|
.execute(conn)
|
|
.map(|_| ())
|
|
.map_err(AppError::from)
|
|
}
|
|
|
|
pub struct TenantContext {
|
|
pub tenant: Tenant,
|
|
}
|
|
|
|
#[async_trait]
|
|
impl FromRequestParts<AppState> for TenantContext {
|
|
type Rejection = AppError;
|
|
|
|
async fn from_request_parts(
|
|
_parts: &mut Parts,
|
|
state: &AppState,
|
|
) -> Result<Self, Self::Rejection> {
|
|
let tenant = state
|
|
.tenants
|
|
.get_by_slug(&state.config.default_tenant_slug)?;
|
|
Ok(Self { tenant })
|
|
}
|
|
}
|