backend signup
This commit is contained in:
+91
-9
@@ -1,11 +1,18 @@
|
||||
use axum::{async_trait, extract::FromRequestParts, http::request::Parts};
|
||||
use diesel::{pg::PgConnection, prelude::*, sql_types::Text};
|
||||
use diesel::{
|
||||
dsl::{exists, select},
|
||||
pg::PgConnection,
|
||||
prelude::*,
|
||||
sql_types::Text,
|
||||
};
|
||||
use serde_json::json;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
db::PgPool,
|
||||
error::{AppError, AppResult},
|
||||
models::Tenant,
|
||||
jobs::{enqueue_job, JOB_PROVISION_TENANT},
|
||||
models::{Tenant, TenantStatus},
|
||||
schema::tenants::dsl,
|
||||
state::AppState,
|
||||
};
|
||||
@@ -50,17 +57,72 @@ impl TenantService {
|
||||
Ok(self.get_by_slug(slug)?.id)
|
||||
}
|
||||
|
||||
pub fn create_tenant(
|
||||
&self,
|
||||
slug: &str,
|
||||
storage_root: Option<&str>,
|
||||
quickwit_index: Option<&str>,
|
||||
status: TenantStatus,
|
||||
initial_members: &[Uuid],
|
||||
created_by: Option<Uuid>,
|
||||
) -> AppResult<Tenant> {
|
||||
let slug = slug.trim();
|
||||
if slug.is_empty() {
|
||||
return Err(AppError::bad_request("tenant slug must not be empty"));
|
||||
}
|
||||
|
||||
let mut conn = self.pool.get().map_err(|err| {
|
||||
tracing::error!(error = ?err, "database pool error");
|
||||
AppError::internal("database pool error")
|
||||
})?;
|
||||
|
||||
let exists: bool =
|
||||
select(exists(dsl::tenants.filter(dsl::slug.eq(slug)))).get_result(&mut conn)?;
|
||||
if exists {
|
||||
return Err(AppError::conflict(format!(
|
||||
"tenant '{}' already exists",
|
||||
slug
|
||||
)));
|
||||
}
|
||||
|
||||
let id = Uuid::new_v4();
|
||||
let storage_root = normalize_storage_root(storage_root, id);
|
||||
let quickwit_index = normalize_quickwit_index(quickwit_index, id);
|
||||
|
||||
diesel::insert_into(dsl::tenants)
|
||||
.values((
|
||||
dsl::id.eq(id),
|
||||
dsl::slug.eq(slug),
|
||||
dsl::storage_root.eq(Some(storage_root.clone())),
|
||||
dsl::quickwit_index.eq(Some(quickwit_index.clone())),
|
||||
dsl::config.eq(json!({})),
|
||||
dsl::status.eq(status),
|
||||
dsl::created_by.eq(created_by),
|
||||
))
|
||||
.execute(&mut conn)?;
|
||||
|
||||
if status == TenantStatus::Creating {
|
||||
let payload = json!({
|
||||
"members": initial_members,
|
||||
});
|
||||
|
||||
enqueue_job(&mut conn, id, JOB_PROVISION_TENANT, payload, None).map_err(|err| {
|
||||
tracing::error!(error = ?err, tenant_id = %id, "failed to enqueue tenant provisioning job");
|
||||
AppError::internal("failed to enqueue tenant provisioning job")
|
||||
})?;
|
||||
}
|
||||
|
||||
TenantRepository::get_by_id(&mut conn, 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| {
|
||||
tracing::error!(error = ?err, "database pool error");
|
||||
AppError::internal("database pool error")
|
||||
})?;
|
||||
let mut conn = self.pool.get().map_err(|err| {
|
||||
tracing::error!(error = ?err, "database pool error");
|
||||
AppError::internal("database pool error")
|
||||
})?;
|
||||
let tenant = loader(&mut conn)?;
|
||||
Ok(tenant)
|
||||
}
|
||||
@@ -92,3 +154,23 @@ impl FromRequestParts<AppState> for TenantContext {
|
||||
Ok(Self { tenant })
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_storage_root(raw: Option<&str>, tenant_id: Uuid) -> String {
|
||||
match raw.map(str::trim) {
|
||||
Some(root) if !root.is_empty() => {
|
||||
let mut owned = root.to_owned();
|
||||
if !owned.ends_with('/') {
|
||||
owned.push('/');
|
||||
}
|
||||
owned
|
||||
}
|
||||
_ => format!("tenants/{tenant_id}/"),
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_quickwit_index(raw: Option<&str>, tenant_id: Uuid) -> String {
|
||||
match raw.map(str::trim) {
|
||||
Some(value) if !value.is_empty() => value.to_owned(),
|
||||
_ => format!("documents-{tenant_id}"),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user