pub struct ControlDb { /* private fields */ }Implementations§
Source§impl ControlDb
impl ControlDb
pub async fn mint_api_key( &self, tenant_id: &TenantId, name: &str, scopes: Vec<ApiKeyScope>, expires_at: Option<DateTime<Utc>>, ) -> Result<ApiKeyMintResult, SaasError>
Sourcepub async fn verify_api_key(
&self,
raw_key: &str,
) -> Result<Option<ApiKey>, SaasError>
pub async fn verify_api_key( &self, raw_key: &str, ) -> Result<Option<ApiKey>, SaasError>
Verifies a raw key, updates last_used_at, and returns the matching ApiKey.
Returns None if the key is not found, revoked, or expired.
pub async fn revoke_api_key( &self, key_id: &ApiKeyId, tenant_id: &TenantId, ) -> Result<(), SaasError>
pub async fn list_api_keys_for_tenant( &self, tenant_id: &TenantId, ) -> Result<Vec<ApiKey>, SaasError>
Source§impl ControlDb
impl ControlDb
pub async fn new(pool: SqlitePool) -> Result<Self, AuthError>
pub fn pool(&self) -> &SqlitePool
pub async fn tenant_meta_by_slug( &self, slug: &str, ) -> Result<Option<TenantMeta>, SaasError>
Sourcepub async fn tenants_for_member(
&self,
email: &str,
) -> Result<Vec<(Tenant, TenantRole)>, SaasError>
pub async fn tenants_for_member( &self, email: &str, ) -> Result<Vec<(Tenant, TenantRole)>, SaasError>
Returns the (Tenant, role) pairs for every tenant the email has
accepted membership in. Sorted by t.name ASC, with deleted tenants
excluded. Used by the dashboard workspace switcher.
Sourcepub async fn member_role(
&self,
tenant_id: &TenantId,
email: &str,
) -> Result<Option<TenantRole>, SaasError>
pub async fn member_role( &self, tenant_id: &TenantId, email: &str, ) -> Result<Option<TenantRole>, SaasError>
Returns the role the given email has on the tenant, or None if the
user is not an accepted member. Used by RequireTenantMember /
RequireTenantAdmin / RequireTenantOwner.
pub async fn most_recently_seen_tenants( &self, count: i64, ) -> Result<Vec<TenantId>, SaasError>
pub async fn touch_last_seen( &self, tenant_id: &TenantId, ) -> Result<(), SaasError>
pub async fn usage_for_tenant( &self, tenant_id: &TenantId, ) -> Result<Vec<TenantUsage>, SaasError>
Sourcepub async fn log_control_audit(
&self,
actor: &str,
action: &str,
tenant_id: Option<&TenantId>,
context: &Value,
) -> Result<(), SaasError>
pub async fn log_control_audit( &self, actor: &str, action: &str, tenant_id: Option<&TenantId>, context: &Value, ) -> Result<(), SaasError>
Append an entry to control_audit_events.
Call sites are fire-and-forget: log the error and continue. An
unlogged audit event is a nuisance, never a correctness bug.
context is serialised to a JSON string before persistence so the
callers can pass arbitrary serde_json::Value shapes.
Source§impl ControlDb
impl ControlDb
Sourcepub async fn list_tenant_members(
&self,
tenant_id: &TenantId,
) -> Result<Vec<TenantMember>, SaasError>
pub async fn list_tenant_members( &self, tenant_id: &TenantId, ) -> Result<Vec<TenantMember>, SaasError>
All members of a tenant. Pending invites (accepted_at IS NULL)
surface first, then accepted rows ordered by invited_at.
pub async fn get_tenant_member( &self, member_id: &MemberId, ) -> Result<Option<TenantMember>, SaasError>
Sourcepub async fn invite_member(
&self,
tenant_id: &TenantId,
email: &str,
role: TenantRole,
token_hash: &[u8],
expires_at: i64,
) -> Result<MemberId, SaasError>
pub async fn invite_member( &self, tenant_id: &TenantId, email: &str, role: TenantRole, token_hash: &[u8], expires_at: i64, ) -> Result<MemberId, SaasError>
Insert a pending member row. Returns the new MemberId. The
(tenant_id, email) UNIQUE constraint maps to
SaasError::MemberAlreadyExists on collision.
Sourcepub async fn find_pending_invite_by_hash(
&self,
token_hash: &[u8],
) -> Result<Option<TenantMember>, SaasError>
pub async fn find_pending_invite_by_hash( &self, token_hash: &[u8], ) -> Result<Option<TenantMember>, SaasError>
Look up a pending invite by token_hash (read-only — does not
consume the token). Used by the GET /invite/{token} page so the
invitee can preview the tenant + role before posting back.
Sourcepub async fn accept_invite(
&self,
token_hash: &[u8],
) -> Result<TenantMember, SaasError>
pub async fn accept_invite( &self, token_hash: &[u8], ) -> Result<TenantMember, SaasError>
Validate a pending invite by token_hash, mark it accepted, and
return the row as it was before the UPDATE so the caller has the
pre-clear email + role. Counterintuitive naming, but the
alternative (return the post-UPDATE row with NULL token columns)
is strictly worse for callers.
Source§impl ControlDb
impl ControlDb
Sourcepub async fn count_owners(&self, tenant_id: &TenantId) -> Result<i64, SaasError>
pub async fn count_owners(&self, tenant_id: &TenantId) -> Result<i64, SaasError>
Number of accepted owners for the given tenant.
Sourcepub async fn update_member_role(
&self,
member_id: &MemberId,
new_role: TenantRole,
) -> Result<(), SaasError>
pub async fn update_member_role( &self, member_id: &MemberId, new_role: TenantRole, ) -> Result<(), SaasError>
Update a member’s role. Rejects with SaasError::CannotDemoteLastOwner
if this would leave the tenant with zero accepted owners.
Sourcepub async fn remove_member(&self, member_id: &MemberId) -> Result<(), SaasError>
pub async fn remove_member(&self, member_id: &MemberId) -> Result<(), SaasError>
Remove a member. Rejects with SaasError::CannotRemoveLastOwner
if removing this row would leave the tenant with zero accepted owners.
Source§impl ControlDb
impl ControlDb
Sourcepub async fn count_tenants_by_status(
&self,
status: TenantStatus,
) -> Result<i64, SaasError>
pub async fn count_tenants_by_status( &self, status: TenantStatus, ) -> Result<i64, SaasError>
Count tenants in a given status (active / suspended / deleted).
Sourcepub async fn count_tenants_created_since(
&self,
since: DateTime<Utc>,
) -> Result<i64, SaasError>
pub async fn count_tenants_created_since( &self, since: DateTime<Utc>, ) -> Result<i64, SaasError>
Count tenants whose created_at is >= since.
Sourcepub async fn count_tenants_grouped_by_plan(
&self,
) -> Result<Vec<(String, i64)>, SaasError>
pub async fn count_tenants_grouped_by_plan( &self, ) -> Result<Vec<(String, i64)>, SaasError>
Return (plan_name, tenant_count) pairs ordered by count descending.
Deleted tenants are excluded. Plans with no tenants still appear with 0.
Sourcepub async fn search_tenants(
&self,
p: SearchTenantsParams<'_>,
) -> Result<SearchTenantsResult, SaasError>
pub async fn search_tenants( &self, p: SearchTenantsParams<'_>, ) -> Result<SearchTenantsResult, SaasError>
Paginated, sortable, filterable tenant search.
Mirrors the Box::leak+dynamic-WHERE pattern from Db::search_users.
plan_id (BLOB) and current_period are bound separately from the
string-typed binds to preserve positional correctness.
Sourcepub async fn aggregate_usage_for_period(
&self,
period: &str,
) -> Result<PeriodAggregate, SaasError>
pub async fn aggregate_usage_for_period( &self, period: &str, ) -> Result<PeriodAggregate, SaasError>
Aggregate MAU + active-tenant count for a single period (e.g. "2026-05").
Returns zeros when no usage rows exist for the period.
Sourcepub async fn aggregate_usage_history(
&self,
n_months: u32,
) -> Result<Vec<PeriodAggregate>, SaasError>
pub async fn aggregate_usage_history( &self, n_months: u32, ) -> Result<Vec<PeriodAggregate>, SaasError>
Return the n_months most-recent per-period aggregates, newest first.
Sourcepub async fn count_dormant_tenants(
&self,
before: DateTime<Utc>,
) -> Result<i64, SaasError>
pub async fn count_dormant_tenants( &self, before: DateTime<Utc>, ) -> Result<i64, SaasError>
Count non-deleted tenants that have not been seen since before
(i.e. last_seen_at is NULL or earlier than the threshold).
Sourcepub async fn set_tenant_status(
&self,
tenant_id: &TenantId,
status: TenantStatus,
) -> Result<(), SaasError>
pub async fn set_tenant_status( &self, tenant_id: &TenantId, status: TenantStatus, ) -> Result<(), SaasError>
Set a tenant’s status. Bumps updated_at.
Sourcepub async fn set_tenant_plan(
&self,
tenant_id: &TenantId,
plan_id: &[u8],
) -> Result<(), SaasError>
pub async fn set_tenant_plan( &self, tenant_id: &TenantId, plan_id: &[u8], ) -> Result<(), SaasError>
Set a tenant’s plan. Bumps updated_at.
Sourcepub async fn list_plans(&self) -> Result<Vec<TenantPlan>, SaasError>
pub async fn list_plans(&self) -> Result<Vec<TenantPlan>, SaasError>
All available plans ordered by price_cents ASC.
Sourcepub async fn get_plan_by_id(
&self,
plan_id: &[u8],
) -> Result<Option<TenantPlan>, SaasError>
pub async fn get_plan_by_id( &self, plan_id: &[u8], ) -> Result<Option<TenantPlan>, SaasError>
Look up a plan by its raw BLOB id. Returns None if not found.
Source§impl ControlDb
impl ControlDb
Sourcepub async fn create_tenant_domain(
&self,
tenant_id: &TenantId,
domain: &str,
dns_target: &str,
) -> Result<TenantDomain, SaasError>
pub async fn create_tenant_domain( &self, tenant_id: &TenantId, domain: &str, dns_target: &str, ) -> Result<TenantDomain, SaasError>
Insert a new domain row for the tenant with status = pending_verification.
Maps a UNIQUE violation on domain to SaasError::DomainAlreadyExists.
Sourcepub async fn list_tenant_domains(
&self,
tenant_id: &TenantId,
) -> Result<Vec<TenantDomain>, SaasError>
pub async fn list_tenant_domains( &self, tenant_id: &TenantId, ) -> Result<Vec<TenantDomain>, SaasError>
All domains for the given tenant, ordered newest-first.
Sourcepub async fn get_tenant_domain_scoped(
&self,
domain_id: &DomainId,
tenant_id: &TenantId,
) -> Result<Option<TenantDomain>, SaasError>
pub async fn get_tenant_domain_scoped( &self, domain_id: &DomainId, tenant_id: &TenantId, ) -> Result<Option<TenantDomain>, SaasError>
Fetch a single domain row only if it belongs to tenant_id.
Returns None when the domain does not exist or belongs to another
tenant (caller should treat both as 404 at the API boundary).
Sourcepub async fn set_tenant_domain_status(
&self,
domain_id: &DomainId,
status: DomainStatus,
verified_at: Option<DateTime<Utc>>,
last_error: Option<&str>,
) -> Result<(), SaasError>
pub async fn set_tenant_domain_status( &self, domain_id: &DomainId, status: DomainStatus, verified_at: Option<DateTime<Utc>>, last_error: Option<&str>, ) -> Result<(), SaasError>
Update the status of a domain row, bumping updated_at.
verified_atis set only when transitioning toVerified.last_erroris cleared onVerifiedand set onFailed.
Sourcepub async fn delete_tenant_domain_scoped(
&self,
domain_id: &DomainId,
tenant_id: &TenantId,
) -> Result<bool, SaasError>
pub async fn delete_tenant_domain_scoped( &self, domain_id: &DomainId, tenant_id: &TenantId, ) -> Result<bool, SaasError>
Delete a domain row only if it belongs to tenant_id.
Returns true when a row was deleted, false when the domain did not
exist or belonged to another tenant.
Sourcepub async fn tenant_by_custom_domain(
&self,
domain: &str,
) -> Result<Option<Tenant>, SaasError>
pub async fn tenant_by_custom_domain( &self, domain: &str, ) -> Result<Option<Tenant>, SaasError>
Find the tenant whose custom domain matches domain and whose status
is verified or active. Used by 38y.2’s router fallback.
Input is lowercased before binding (DNS is case-insensitive; storage is always lowercase).
Sourcepub async fn list_domains_for_sweep(
&self,
limit: i64,
) -> Result<Vec<TenantDomain>, SaasError>
pub async fn list_domains_for_sweep( &self, limit: i64, ) -> Result<Vec<TenantDomain>, SaasError>
Rows eligible for the background verification sweep: those with status
pending_verification or failed, ordered oldest-first, capped at
limit.
Source§impl ControlDb
impl ControlDb
pub async fn tenant_by_slug( &self, slug: &str, ) -> Result<Option<Tenant>, SaasError>
pub async fn tenant_by_id( &self, id: &TenantId, ) -> Result<Option<Tenant>, SaasError>
pub async fn tenant_by_owner_email( &self, email: &str, ) -> Result<Vec<Tenant>, SaasError>
pub async fn list_tenants(&self) -> Result<Vec<Tenant>, SaasError>
Source§impl ControlDb
impl ControlDb
pub async fn provision_tenant( &self, name: String, slug: String, owner_email: String, tenant_data_dir: &Path, config: &TenantBuilderConfig, ) -> Result<ProvisionResult, SaasError>
pub async fn update_tenant_slug( &self, id: &TenantId, new_slug: String, tenant_pool: &SqlitePool, ) -> Result<(), SaasError>
Auto Trait Implementations§
impl !RefUnwindSafe for ControlDb
impl !UnwindSafe for ControlDb
impl Freeze for ControlDb
impl Send for ControlDb
impl Sync for ControlDb
impl Unpin for ControlDb
impl UnsafeUnpin for ControlDb
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more