mod args;
mod audit;
mod migrations;
mod roles;
mod scaffold;
mod server;
mod tenants;
mod users;
pub mod api;
use std::io::{self, Write};
use std::path::Path;
use super::error::TenancyError;
use super::pools::TenantPools;
pub async fn run(
pools: &TenantPools,
registry_url: &str,
dir: &Path,
args: impl IntoIterator<Item = String>,
) -> Result<(), TenancyError> {
let mut stdout = io::stdout();
run_with_writer(pools, registry_url, dir, args, &mut stdout).await
}
pub async fn run_with_writer<W: Write + Send>(
pools: &TenantPools,
registry_url: &str,
dir: &Path,
args: impl IntoIterator<Item = String>,
writer: &mut W,
) -> Result<(), TenancyError> {
let args: Vec<String> = args.into_iter().collect();
let cmd = args.first().map_or("", String::as_str);
match cmd {
"" | "help" | "--help" | "-h" => {
write_help(writer)?;
Ok(())
}
"create-tenant" => {
tenants::create_tenant(pools, registry_url, dir, &args[1..], writer).await
}
"drop-tenant" => tenants::drop_tenant(pools, &args[1..], writer).await,
"purge-tenant" => tenants::purge_tenant(pools, &args[1..], writer).await,
"list-tenants" => tenants::list_tenants(pools, writer).await,
"migrate-tenants" => {
migrations::migrate_tenants_cmd(pools, registry_url, dir, writer).await
}
"migrate-registry" => migrations::migrate_registry_cmd(pools, dir, writer).await,
"create-operator" => users::create_operator_cmd(pools, &args[1..], writer).await,
"create-user" => users::create_user_cmd(pools, registry_url, &args[1..], writer).await,
"run-server" | "runserver" => {
server::run_server_cmd(pools, registry_url, &args[1..], writer).await
}
"init-tenancy" => migrations::init_tenancy_cmd(dir, writer),
"audit-cleanup" => audit::audit_cleanup_cmd(pools, &args[1..], writer).await,
"create-role" => roles::create_role_cmd(pools, &args[1..], writer).await,
"list-roles" => roles::list_roles_cmd(pools, &args[1..], writer).await,
"assign-role" => roles::assign_role_cmd(pools, &args[1..], writer).await,
"revoke-role" => roles::revoke_role_cmd(pools, &args[1..], writer).await,
"grant-perm" => roles::grant_perm_cmd(pools, &args[1..], writer).await,
"revoke-perm" => roles::revoke_perm_cmd(pools, &args[1..], writer).await,
"create-api-key" => roles::create_api_key_cmd(pools, &args[1..], writer).await,
"startapp" => scaffold::startapp_cmd(&args[1..], writer),
"migrate" => {
migrations::migrate_all_cmd(pools, registry_url, dir, &args[1..], writer).await
}
_ => rustango::migrate::manage::run_with_writer(
pools.registry(),
dir,
args,
writer,
)
.await
.map_err(TenancyError::Migrate),
}
}
pub fn write_help<W: Write>(w: &mut W) -> Result<(), TenancyError> {
writeln!(w, "rustango manage CLI — tenancy-aware dispatcher")?;
writeln!(w)?;
writeln!(w, "USAGE:")?;
writeln!(w, " cargo run -- <verb> [args]")?;
writeln!(w)?;
writeln!(w, "TENANT MANAGEMENT:")?;
writeln!(
w,
" create-tenant <slug> [--display-name <s>] [--mode schema|database]"
)?;
writeln!(
w,
" [--host-pattern <s>] [--database-url <s>] [--no-migrate]"
)?;
writeln!(
w,
" Provision a new tenant. Schema mode (default) gives the"
)?;
writeln!(
w,
" tenant its own Postgres schema; database mode points at"
)?;
writeln!(
w,
" a fully separate DB via --database-url."
)?;
writeln!(
w,
" drop-tenant <slug> [--confirm <slug>]"
)?;
writeln!(
w,
" Soft-delete (active=false). Data preserved."
)?;
writeln!(
w,
" purge-tenant <slug> [--confirm <slug>] [--purge-database]"
)?;
writeln!(
w,
" HARD-delete: drops schema (or DB with --purge-database)."
)?;
writeln!(w, " list-tenants Print every Org row in the registry.")?;
writeln!(w)?;
writeln!(w, "USER / OPERATOR MANAGEMENT:")?;
writeln!(
w,
" create-operator <username> --password <p>"
)?;
writeln!(
w,
" Operator-level account; signs into the apex /login."
)?;
writeln!(
w,
" create-user <slug> <username> --password <p> [--superuser]"
)?;
writeln!(
w,
" Tenant-scoped user; signs into <slug>.<apex>/__login."
)?;
writeln!(w)?;
writeln!(w, "MIGRATIONS:")?;
writeln!(
w,
" init-tenancy Materialize bootstrap migrations into ./migrations/."
)?;
writeln!(
w,
" makemigrations Diff models against latest snapshot, emit a new JSON file."
)?;
writeln!(
w,
" migrate Apply registry-scoped, then tenant-scoped, migrations."
)?;
writeln!(
w,
" migrate-registry Apply registry-scoped migrations only."
)?;
writeln!(
w,
" migrate-tenants Apply tenant-scoped migrations to every active org."
)?;
writeln!(
w,
" showmigrations List which migrations are applied / pending."
)?;
writeln!(
w,
" downgrade Roll back the most recent migration."
)?;
writeln!(w)?;
writeln!(w, "ROLES & PERMISSIONS:")?;
writeln!(w, " create-role <slug> <name> [--description <s>]")?;
writeln!(w, " Create a named role on a tenant.")?;
writeln!(w, " list-roles <slug> List roles and permission counts.")?;
writeln!(w, " assign-role <slug> <username> <role>")?;
writeln!(w, " Add a user to a role.")?;
writeln!(w, " revoke-role <slug> <username> <role>")?;
writeln!(w, " Remove a user from a role.")?;
writeln!(w, " grant-perm <slug> <name> <codename> [--role]")?;
writeln!(w, " Grant a codename to a user (default) or role (--role).")?;
writeln!(w, " revoke-perm <slug> <name> <codename> [--role]")?;
writeln!(w, " Revoke a codename from a user or role.")?;
writeln!(w, " create-api-key <slug> <username> [--label <s>] [--expires-days <N>]")?;
writeln!(w, " Issue a Bearer API key for a tenant user.")?;
writeln!(w)?;
writeln!(w, "AUDIT:")?;
writeln!(
w,
" audit-cleanup --days <N> Delete entries older than N days (all active tenants)."
)?;
writeln!(
w,
" audit-cleanup --keep-last <N> Keep N most recent entries per row."
)?;
writeln!(
w,
" [--tenant <s>] Scope to one tenant instead of all."
)?;
writeln!(w)?;
writeln!(w, "SERVER:")?;
writeln!(
w,
" run-server [--bind <addr>]"
)?;
writeln!(
w,
" Boot the HTTP server with admin + operator console."
)?;
writeln!(w)?;
writeln!(w, "SCAFFOLDING:")?;
writeln!(
w,
" startapp <name> [--into <dir>] [--with-manage-bin] [--with-bootstrap-migration]"
)?;
writeln!(
w,
" Scaffold a Django-shape app module."
)?;
writeln!(w)?;
writeln!(w, "EXAMPLES:")?;
writeln!(w, " cargo run -- migrate")?;
writeln!(w, " cargo run -- create-operator admin --password letmein")?;
writeln!(w, " cargo run -- create-tenant acme --display-name 'ACME Corp'")?;
writeln!(w, " cargo run -- create-user acme alice --password hunter2 --superuser")?;
writeln!(w, " cargo run -- list-tenants")?;
writeln!(w, " cargo run -- audit-cleanup --days 90")?;
writeln!(w, " cargo run -- audit-cleanup --keep-last 50 --tenant acme")?;
writeln!(w)?;
writeln!(w, "Run any verb with --help for verb-specific flags + details.")?;
Ok(())
}