linting, tenant slug -> name

This commit is contained in:
2025-10-29 18:26:59 +01:00
parent b6bcb72165
commit abc0116945
23 changed files with 324 additions and 487 deletions
+76 -76
View File
@@ -39,23 +39,23 @@ enum Command {
username: String,
},
CreateTenant {
slug: String,
name: String,
storage_root: Option<String>,
quickwit_index: Option<String>,
},
DeleteTenant {
slug: String,
name: String,
},
AddUserToTenant {
username: String,
slug: String,
name: String,
},
RemoveUserFromTenant {
username: String,
slug: String,
name: String,
},
ReanalyzeDocuments {
slug: String,
name: String,
},
ListTenants,
DeleteAssets(String),
@@ -70,15 +70,15 @@ impl Command {
set-password <username> <password>\n\
list-users\n\
delete-user <username>\n\
create-tenant <slug> [storage_root] [quickwit_index]\n\
delete-tenant <slug>\n\
add-user-to-tenant <username> <slug>\n\
remove-user-from-tenant <username> <slug>\n\
reanalyze-documents <slug>\n\
create-tenant <name> [storage_root] [quickwit_index]\n\
delete-tenant <name>\n\
add-user-to-tenant <username> <name>\n\
remove-user-from-tenant <username> <name>\n\
reanalyze-documents <name>\n\
list-tenants\n\
delete-assets <slug>\n\
quickwit-create-index <slug>\n\
quickwit-delete-index <slug>"
delete-assets <name>\n\
quickwit-create-index <name>\n\
quickwit-delete-index <name>"
}
fn parse() -> Result<Self> {
@@ -97,33 +97,33 @@ impl Command {
username: args.next().ok_or_else(|| anyhow!("username required"))?,
}),
Some("create-tenant") => Ok(Self::CreateTenant {
slug: args.next().ok_or_else(|| anyhow!("tenant slug required"))?,
name: args.next().ok_or_else(|| anyhow!("tenant name required"))?,
storage_root: args.next(),
quickwit_index: args.next(),
}),
Some("delete-tenant") => Ok(Self::DeleteTenant {
slug: args.next().ok_or_else(|| anyhow!("tenant slug required"))?,
name: args.next().ok_or_else(|| anyhow!("tenant name required"))?,
}),
Some("add-user-to-tenant") => Ok(Self::AddUserToTenant {
username: args.next().ok_or_else(|| anyhow!("username required"))?,
slug: args.next().ok_or_else(|| anyhow!("tenant slug required"))?,
name: args.next().ok_or_else(|| anyhow!("tenant name required"))?,
}),
Some("remove-user-from-tenant") => Ok(Self::RemoveUserFromTenant {
username: args.next().ok_or_else(|| anyhow!("username required"))?,
slug: args.next().ok_or_else(|| anyhow!("tenant slug required"))?,
name: args.next().ok_or_else(|| anyhow!("tenant name required"))?,
}),
Some("reanalyze-documents") => Ok(Self::ReanalyzeDocuments {
slug: args.next().ok_or_else(|| anyhow!("tenant slug required"))?,
name: args.next().ok_or_else(|| anyhow!("tenant name required"))?,
}),
Some("list-tenants") => Ok(Self::ListTenants),
Some("delete-assets") => Ok(Self::DeleteAssets(
args.next().ok_or_else(|| anyhow!("tenant slug required"))?,
args.next().ok_or_else(|| anyhow!("tenant name required"))?,
)),
Some("quickwit-create-index") => Ok(Self::QuickwitCreate(
args.next().ok_or_else(|| anyhow!("tenant slug required"))?,
args.next().ok_or_else(|| anyhow!("tenant name required"))?,
)),
Some("quickwit-delete-index") => Ok(Self::QuickwitDelete(
args.next().ok_or_else(|| anyhow!("tenant slug required"))?,
args.next().ok_or_else(|| anyhow!("tenant name required"))?,
)),
_ => Err(anyhow!(Self::usage())),
}
@@ -143,23 +143,23 @@ async fn main() -> Result<()> {
Command::ListUsers => list_users(&pool)?,
Command::DeleteUser { username } => delete_user(&pool, &username)?,
Command::CreateTenant {
slug,
name,
storage_root,
quickwit_index,
} => create_tenant(&pool, &slug, storage_root, quickwit_index)?,
Command::DeleteTenant { slug } => delete_tenant(&pool, &slug)?,
Command::AddUserToTenant { username, slug } => add_user_to_tenant(&pool, &username, &slug)?,
Command::RemoveUserFromTenant { username, slug } => {
remove_user_from_tenant(&pool, &username, &slug)?
} => create_tenant(&pool, &name, storage_root, quickwit_index)?,
Command::DeleteTenant { name } => delete_tenant(&pool, &name)?,
Command::AddUserToTenant { username, name } => add_user_to_tenant(&pool, &username, &name)?,
Command::RemoveUserFromTenant { username, name } => {
remove_user_from_tenant(&pool, &username, &name)?
}
Command::ReanalyzeDocuments { slug } => reanalyze_documents(&pool, &slug)?,
Command::ReanalyzeDocuments { name } => reanalyze_documents(&pool, &name)?,
Command::ListTenants => list_tenants(&pool)?,
Command::DeleteAssets(slug) => delete_assets_for_tenant(&config, &pool, &slug).await?,
Command::QuickwitCreate(slug) => {
quickwit_index(&config, &pool, &slug, Method::POST).await?
Command::DeleteAssets(name) => delete_assets_for_tenant(&config, &pool, &name).await?,
Command::QuickwitCreate(name) => {
quickwit_index(&config, &pool, &name, Method::POST).await?
}
Command::QuickwitDelete(slug) => {
quickwit_index(&config, &pool, &slug, Method::DELETE).await?
Command::QuickwitDelete(name) => {
quickwit_index(&config, &pool, &name, Method::DELETE).await?
}
}
@@ -229,8 +229,8 @@ fn list_users(pool: &PgPool) -> Result<()> {
let memberships: Vec<String> = user_memberships::table
.inner_join(tenants::table)
.filter(user_memberships::user_id.eq(user.id))
.select(tenants::slug)
.order(tenants::slug.asc())
.select(tenants::name)
.order(tenants::name.asc())
.load(&mut conn)?;
if memberships.is_empty() {
@@ -267,14 +267,14 @@ fn delete_user(pool: &PgPool, username: &str) -> Result<()> {
fn create_tenant(
pool: &PgPool,
slug: &str,
name: &str,
storage_root_arg: Option<String>,
quickwit_index_arg: Option<String>,
) -> Result<()> {
let service = TenantService::new(pool.clone());
let tenant = service
.create_tenant(
slug,
name,
storage_root_arg.as_deref(),
quickwit_index_arg.as_deref(),
TenantStatus::Creating,
@@ -288,7 +288,7 @@ fn create_tenant(
println!(
"created tenant '{}' with id {}, storage_root '{}', quickwit_index '{}', status '{}'",
tenant.slug,
tenant.name,
tenant.id,
storage_root,
quickwit_index,
@@ -297,29 +297,29 @@ fn create_tenant(
Ok(())
}
fn delete_tenant(pool: &PgPool, slug: &str) -> Result<()> {
fn delete_tenant(pool: &PgPool, name: &str) -> Result<()> {
let mut conn = pool.get().context("failed to get database connection")?;
let tenant: Tenant = tenants::table
.filter(tenants::slug.eq(slug))
.filter(tenants::name.eq(name))
.first(&mut conn)
.optional()?
.ok_or_else(|| anyhow!("tenant '{}' not found", slug))?;
.ok_or_else(|| anyhow!("tenant '{}' not found", name))?;
let member_exists: bool = select(exists(
user_memberships::table.filter(user_memberships::tenant_id.eq(tenant.id)),
))
.get_result(&mut conn)?;
if member_exists {
bail!("tenant '{}' still has user memberships", slug);
bail!("tenant '{}' still has user memberships", name);
}
diesel::delete(tenants::table.filter(tenants::id.eq(tenant.id))).execute(&mut conn)?;
println!("deleted tenant '{}'", slug);
println!("deleted tenant '{}'", name);
Ok(())
}
fn add_user_to_tenant(pool: &PgPool, username: &str, slug: &str) -> Result<()> {
fn add_user_to_tenant(pool: &PgPool, username: &str, name: &str) -> Result<()> {
let mut conn = pool.get().context("failed to get database connection")?;
let user: User = users::table
@@ -329,10 +329,10 @@ fn add_user_to_tenant(pool: &PgPool, username: &str, slug: &str) -> Result<()> {
.ok_or_else(|| anyhow!("user '{}' not found", username))?;
let tenant: Tenant = tenants::table
.filter(tenants::slug.eq(slug))
.filter(tenants::name.eq(name))
.first(&mut conn)
.optional()?
.ok_or_else(|| anyhow!("tenant '{}' not found", slug))?;
.ok_or_else(|| anyhow!("tenant '{}' not found", name))?;
let membership = NewUserMembership {
id: Uuid::new_v4(),
@@ -346,11 +346,11 @@ fn add_user_to_tenant(pool: &PgPool, username: &str, slug: &str) -> Result<()> {
.do_nothing()
.execute(&mut conn)?;
println!("added user '{}' to tenant '{}'", username, slug);
println!("added user '{}' to tenant '{}'", username, name);
Ok(())
}
fn remove_user_from_tenant(pool: &PgPool, username: &str, slug: &str) -> Result<()> {
fn remove_user_from_tenant(pool: &PgPool, username: &str, name: &str) -> Result<()> {
let mut conn = pool.get().context("failed to get database connection")?;
let user: User = users::table
@@ -360,10 +360,10 @@ fn remove_user_from_tenant(pool: &PgPool, username: &str, slug: &str) -> Result<
.ok_or_else(|| anyhow!("user '{}' not found", username))?;
let tenant: Tenant = tenants::table
.filter(tenants::slug.eq(slug))
.filter(tenants::name.eq(name))
.first(&mut conn)
.optional()?
.ok_or_else(|| anyhow!("tenant '{}' not found", slug))?;
.ok_or_else(|| anyhow!("tenant '{}' not found", name))?;
let removed = diesel::delete(
user_memberships::table
@@ -373,21 +373,21 @@ fn remove_user_from_tenant(pool: &PgPool, username: &str, slug: &str) -> Result<
.execute(&mut conn)?;
if removed == 0 {
println!("user '{}' was not a member of tenant '{}'", username, slug);
println!("user '{}' was not a member of tenant '{}'", username, name);
} else {
println!("removed user '{}' from tenant '{}'", username, slug);
println!("removed user '{}' from tenant '{}'", username, name);
}
Ok(())
}
fn reanalyze_documents(pool: &PgPool, slug: &str) -> Result<()> {
fn reanalyze_documents(pool: &PgPool, name: &str) -> Result<()> {
let mut conn = pool.get().context("failed to get database connection")?;
let tenant: Tenant = tenants::table
.filter(tenants::slug.eq(slug))
.filter(tenants::name.eq(name))
.first(&mut conn)
.optional()?
.ok_or_else(|| anyhow!("tenant '{}' not found", slug))?;
.ok_or_else(|| anyhow!("tenant '{}' not found", name))?;
let targets: Vec<(Uuid, Uuid)> = documents::table
.filter(documents::tenant_id.eq(tenant.id))
@@ -396,7 +396,7 @@ fn reanalyze_documents(pool: &PgPool, slug: &str) -> Result<()> {
.load(&mut conn)?;
if targets.is_empty() {
println!("tenant '{}' has no active documents", slug);
println!("tenant '{}' has no active documents", name);
return Ok(());
}
@@ -419,7 +419,7 @@ fn reanalyze_documents(pool: &PgPool, slug: &str) -> Result<()> {
println!(
"queued {} documents for re-analysis in tenant '{}'",
queued, slug
queued, name
);
Ok(())
}
@@ -427,7 +427,7 @@ fn reanalyze_documents(pool: &PgPool, slug: &str) -> Result<()> {
fn list_tenants(pool: &PgPool) -> Result<()> {
let mut conn = pool.get().context("failed to get database connection")?;
let tenants: Vec<Tenant> = tenants::table
.order(tenants::slug.asc())
.order(tenants::name.asc())
.load(&mut conn)
.context("failed to load tenants")?;
@@ -437,7 +437,7 @@ fn list_tenants(pool: &PgPool) -> Result<()> {
}
for tenant in tenants {
println!("{} ({})", tenant.slug, tenant.id);
println!("{} ({})", tenant.name, tenant.id);
}
Ok(())
@@ -446,7 +446,7 @@ fn list_tenants(pool: &PgPool) -> Result<()> {
async fn delete_assets_for_tenant(
config: &AppConfig,
pool: &PgPool,
tenant_slug: &str,
tenant_name: &str,
) -> Result<()> {
let s3_client = s3::build_client(config).await?;
let storage: Arc<dyn ObjectStorage> =
@@ -454,28 +454,28 @@ async fn delete_assets_for_tenant(
let mut conn = pool.get().context("failed to get database connection")?;
let tenant: Tenant = tenants::table
.filter(tenants::slug.eq(tenant_slug))
.filter(tenants::name.eq(tenant_name))
.first(&mut conn)
.optional()
.context("failed to load tenant")?
.ok_or_else(|| anyhow!("tenant '{}' not found", tenant_slug))?;
.ok_or_else(|| anyhow!("tenant '{}' not found", tenant_name))?;
let tenant_storage = TenantStorage::new(Arc::clone(&storage), &tenant)
.with_context(|| format!("missing storage root for tenant {}", tenant.slug))?;
.with_context(|| format!("missing storage root for tenant {}", tenant.name))?;
let assets: Vec<DocumentAsset> = document_assets::table
.filter(document_assets::tenant_id.eq(tenant.id))
.load(&mut conn)
.with_context(|| format!("failed to load assets for tenant {}", tenant.slug))?;
.with_context(|| format!("failed to load assets for tenant {}", tenant.name))?;
if assets.is_empty() {
println!("Tenant {}: no assets", tenant.slug);
println!("Tenant {}: no assets", tenant.name);
return Ok(());
}
println!(
"Tenant {} ({}): deleting {} assets…",
tenant.slug,
tenant.name,
tenant.id,
assets.len()
);
@@ -486,13 +486,13 @@ async fn delete_assets_for_tenant(
.filter(document_asset_objects::tenant_id.eq(tenant.id))
.filter(document_asset_objects::asset_id.eq_any(&asset_ids))
.load(&mut conn)
.with_context(|| format!("failed to load asset objects for tenant {}", tenant.slug))?;
.with_context(|| format!("failed to load asset objects for tenant {}", tenant.name))?;
for object in &objects {
if let Err(err) = tenant_storage.delete_object(&object.s3_key).await {
eprintln!(
"Failed to delete object {} (tenant {}): {err}",
object.s3_key, tenant.slug
object.s3_key, tenant.name
);
}
}
@@ -503,20 +503,20 @@ async fn delete_assets_for_tenant(
.filter(document_asset_objects::asset_id.eq_any(&asset_ids)),
)
.execute(&mut conn)
.with_context(|| format!("failed to remove asset objects for tenant {}", tenant.slug))?;
.with_context(|| format!("failed to remove asset objects for tenant {}", tenant.name))?;
diesel::delete(document_assets::table.filter(document_assets::tenant_id.eq(tenant.id)))
.execute(&mut conn)
.with_context(|| format!("failed to remove asset records for tenant {}", tenant.slug))?;
.with_context(|| format!("failed to remove asset records for tenant {}", tenant.name))?;
println!("Tenant {}: asset records deleted.", tenant.slug);
println!("Tenant {}: asset records deleted.", tenant.name);
Ok(())
}
async fn quickwit_index(
config: &AppConfig,
pool: &PgPool,
slug: &str,
name: &str,
method: Method,
) -> Result<()> {
let endpoint = config
@@ -526,11 +526,11 @@ async fn quickwit_index(
let mut conn = pool.get().context("failed to get database connection")?;
let tenant: Tenant = tenants::table
.filter(tenants::slug.eq(slug))
.filter(tenants::name.eq(name))
.first(&mut conn)
.optional()
.context("failed to query tenants")?
.ok_or_else(|| anyhow!("tenant '{}' not found", slug))?;
.ok_or_else(|| anyhow!("tenant '{}' not found", name))?;
let client = Client::new();
let index_id = format!("documents-{}", tenant.id);
@@ -549,7 +549,7 @@ async fn quickwit_index(
println!(
"Tenant '{}' quickwit index set to '{}'.",
tenant.slug, index_id
tenant.name, index_id
);
}
Method::DELETE => {
@@ -566,7 +566,7 @@ async fn quickwit_index(
.execute(&mut conn)
.context("failed to clear tenant quickwit_index")?;
println!("Tenant '{}' quickwit index cleared.", tenant.slug);
println!("Tenant '{}' quickwit index cleared.", tenant.name);
}
status => {
let body = response.text().await.unwrap_or_default();