Skip to main content

ControlDb

Struct ControlDb 

Source
pub struct ControlDb { /* private fields */ }

Implementations§

Source§

impl ControlDb

Source

pub async fn mint_api_key( &self, tenant_id: &TenantId, name: &str, scopes: Vec<ApiKeyScope>, expires_at: Option<DateTime<Utc>>, ) -> Result<ApiKeyMintResult, SaasError>

Source

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.

Source

pub async fn revoke_api_key( &self, key_id: &ApiKeyId, tenant_id: &TenantId, ) -> Result<(), SaasError>

Source

pub async fn list_api_keys_for_tenant( &self, tenant_id: &TenantId, ) -> Result<Vec<ApiKey>, SaasError>

Source§

impl ControlDb

Source

pub async fn new(pool: SqlitePool) -> Result<Self, AuthError>

Source

pub fn pool(&self) -> &SqlitePool

Source

pub async fn tenant_meta_by_slug( &self, slug: &str, ) -> Result<Option<TenantMeta>, SaasError>

Source

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.

Source

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.

Source

pub async fn most_recently_seen_tenants( &self, count: i64, ) -> Result<Vec<TenantId>, SaasError>

Source

pub async fn touch_last_seen( &self, tenant_id: &TenantId, ) -> Result<(), SaasError>

Source

pub async fn usage_for_tenant( &self, tenant_id: &TenantId, ) -> Result<Vec<TenantUsage>, SaasError>

Source

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

Source

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.

Source

pub async fn get_tenant_member( &self, member_id: &MemberId, ) -> Result<Option<TenantMember>, SaasError>

Source

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.

Source

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.

Source

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

Source

pub async fn count_owners(&self, tenant_id: &TenantId) -> Result<i64, SaasError>

Number of accepted owners for the given tenant.

Source

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.

Source

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

Source

pub async fn count_tenants_by_status( &self, status: TenantStatus, ) -> Result<i64, SaasError>

Count tenants in a given status (active / suspended / deleted).

Source

pub async fn count_tenants_created_since( &self, since: DateTime<Utc>, ) -> Result<i64, SaasError>

Count tenants whose created_at is >= since.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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).

Source

pub async fn set_tenant_status( &self, tenant_id: &TenantId, status: TenantStatus, ) -> Result<(), SaasError>

Set a tenant’s status. Bumps updated_at.

Source

pub async fn set_tenant_plan( &self, tenant_id: &TenantId, plan_id: &[u8], ) -> Result<(), SaasError>

Set a tenant’s plan. Bumps updated_at.

Source

pub async fn list_plans(&self) -> Result<Vec<TenantPlan>, SaasError>

All available plans ordered by price_cents ASC.

Source

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

Source

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.

Source

pub async fn list_tenant_domains( &self, tenant_id: &TenantId, ) -> Result<Vec<TenantDomain>, SaasError>

All domains for the given tenant, ordered newest-first.

Source

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).

Source

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_at is set only when transitioning to Verified.
  • last_error is cleared on Verified and set on Failed.
Source

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.

Source

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).

Source

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

Source

pub async fn tenant_by_slug( &self, slug: &str, ) -> Result<Option<Tenant>, SaasError>

Source

pub async fn tenant_by_id( &self, id: &TenantId, ) -> Result<Option<Tenant>, SaasError>

Source

pub async fn tenant_by_owner_email( &self, email: &str, ) -> Result<Vec<Tenant>, SaasError>

Source

pub async fn list_tenants(&self) -> Result<Vec<Tenant>, SaasError>

Source§

impl ControlDb

Source

pub async fn update_tenant_name( &self, id: &TenantId, name: String, ) -> Result<(), SaasError>

Source

pub async fn suspend_tenant(&self, id: &TenantId) -> Result<(), SaasError>

Source

pub async fn delete_tenant(&self, id: &TenantId) -> Result<(), SaasError>

Source§

impl ControlDb

Source

pub async fn provision_tenant( &self, name: String, slug: String, owner_email: String, tenant_data_dir: &Path, config: &TenantBuilderConfig, ) -> Result<ProvisionResult, SaasError>

Source

pub async fn update_tenant_slug( &self, id: &TenantId, new_slug: String, tenant_pool: &SqlitePool, ) -> Result<(), SaasError>

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more