pub struct Admin { /* private fields */ }Expand description
Builder for the admin. Register models with .model::<M>(), then
hand it to the router via register_admin_routes.
Implementations§
Source§impl Admin
impl Admin
Sourcepub fn new() -> Self
pub fn new() -> Self
Constructs a new Admin with the framework’s core entries
pre-seeded. The only core entry is User; project models are
added on top via Self::model. The outbound mailer
defaults to LogMailer — safe for dev / CI / testing,
not suitable for production (recovery emails are written
to log::info! instead of being sent). Projects opt into a
real mailer via Self::mailer.
Sourcepub fn site_branding(self, branding: SiteBranding) -> Self
pub fn site_branding(self, branding: SiteBranding) -> Self
Replace the entire SiteBranding block. For finer-grained
adjustments, use the per-field builders below
(Admin::app_name, Admin::app_tagline, …).
Sourcepub fn app_name(self, name: impl Into<String>) -> Self
pub fn app_name(self, name: impl Into<String>) -> Self
Set the user-facing product identity. Recommended for every production deployment — the framework name “RustIO” should not appear in operational user surfaces.
Example: Admin::new().app_name("Library Circulation").
Also mirrors the value into the legacy site_title /
site_header fields so older paths that still read them stay
coherent with the new identity.
Sourcepub fn app_tagline(self, tagline: impl Into<String>) -> Self
pub fn app_tagline(self, tagline: impl Into<String>) -> Self
Set an optional secondary line shown under the brand wordmark in emails + auth pages.
Sourcepub fn support_email(self, email: impl Into<String>) -> Self
pub fn support_email(self, email: impl Into<String>) -> Self
Set the support contact surfaced in recovery emails.
Sourcepub fn public_url(self, url: impl Into<String>) -> Self
pub fn public_url(self, url: impl Into<String>) -> Self
Set the canonical public URL — used as a base when composing reset links if request-header derivation is unreliable.
Sourcepub fn show_powered_by(self, show: bool) -> Self
pub fn show_powered_by(self, show: bool) -> Self
Opt in to the small “Powered by RustIO” credit in chrome footer + email footer. Off by default; the framework name stays invisible to end users unless this is enabled.
Sourcepub fn read_only(self, on: bool) -> Self
pub fn read_only(self, on: bool) -> Self
Whole-admin read-only toggle. When true every mutating
POST / PUT / DELETE under /admin/* is rejected with
403 by the read_only_guard middleware; auth-flow routes
(login, logout, MFA verify, password recovery, own-session
management, own MFA management) are explicitly allowlisted
so operators can still sign in / out and complete forced
rotations. Templates that read Self::is_read_only
surface a banner and hide top-level “Add” affordances; per-
row Edit / Delete buttons still render in v1 (clicking
through hits the middleware), documented as a known
scoping trade-off.
Sourcepub fn is_read_only(&self) -> bool
pub fn is_read_only(&self) -> bool
true when the admin was constructed with Self::read_only.
Consumed by the chrome (banner) and the list/dashboard
templates (suppress “Add” buttons).
Sourcepub fn read_only_model(self, admin_name: impl Into<String>) -> Self
pub fn read_only_model(self, admin_name: impl Into<String>) -> Self
Freeze one model. Mutating requests under /admin/<admin_name>/...
return 403; the rest of the admin stays writable. Useful for
archive tables, regulatory holds, or per-model incident
response without flipping the whole admin to read-only.
admin_name is the model’s URL slug (the same value the
router matches :admin_name against — pluralised, e.g.
"posts", "users"). Wrong slugs silently no-op; the
middleware checks set membership, so a typo simply doesn’t
freeze anything.
Sourcepub fn is_model_read_only(&self, admin_name: &str) -> bool
pub fn is_model_read_only(&self, admin_name: &str) -> bool
true when admin_name was registered via
Self::read_only_model. The read_only_guard middleware
consults this to gate per-model mutations.
Sourcepub fn uploads_dir(self, dir: impl Into<PathBuf>) -> Self
pub fn uploads_dir(self, dir: impl Into<PathBuf>) -> Self
Set the storage root for uploaded files. Models declaring
#[rustio(file)] columns persist relative paths under this
directory; the framework serves the bytes back via
GET /admin/uploads/<rel>. The directory is created lazily
on first write. Leaving this unset (the default) makes any
file-input field inert at submit — the form renders, but
the multipart parse path skips file writes.
Path safety contract. The framework canonicalises the configured root once at builder time and refuses any serve-route lookup whose resolved path lands outside the canonical root; rejected lookups return 404 (no information leak about whether the path could exist).
Sourcepub fn uploads_dir_path(&self) -> Option<&Path>
pub fn uploads_dir_path(&self) -> Option<&Path>
Read-only access to the configured uploads root, if any.
Sourcepub fn branding(&self) -> &SiteBranding
pub fn branding(&self) -> &SiteBranding
Read-only access to the active branding.
Sourcepub fn accent_color(self, color: impl Into<String>) -> Self
pub fn accent_color(self, color: impl Into<String>) -> Self
Set the admin chrome’s accent colour. Hex form, with or without
the leading # ("#1e6ba8" and "1e6ba8" both work). Replaces
any prior accent override; other AdminTheme fields are
left untouched.
Sourcepub fn theme(self, theme: AdminTheme) -> Self
pub fn theme(self, theme: AdminTheme) -> Self
Replace the entire admin chrome palette patch in one call. See
AdminTheme for the field-by-field contract.
Sourcepub fn accent(&self) -> Option<&str>
pub fn accent(&self) -> Option<&str>
Read-only access to the configured accent colour, if any. None
means “no override — admin.css owns it”.
Sourcepub fn active_theme(&self) -> &AdminTheme
pub fn active_theme(&self) -> &AdminTheme
Read-only access to the active theme override patch.
Sourcepub fn mailer(self, mailer: SharedMailer) -> Self
pub fn mailer(self, mailer: SharedMailer) -> Self
Replace the outbound mailer. Closes the
documented-but-unimplemented gap from 0.4.0 where the doc
comments described this method while the Admin struct had
no mailer field; landed in 0.5.0 alongside the R1 recovery
pipeline that consumes it (DESIGN_RECOVERY.md §10.3).
Typical project wiring:
use std::sync::Arc;
let admin = Admin::new()
.mailer(Arc::new(MyProjectMailer::new(/* SES, Mailgun, … */)));The framework imposes no transport. Anything that implements
the crate::email::Mailer trait (which is Send + Sync
and async-friendly) plugs in here. R1’s recovery flow reads
this via Self::active_mailer and dispatches reset
emails through it.
Sourcepub fn active_mailer(&self) -> &SharedMailer
pub fn active_mailer(&self) -> &SharedMailer
Read-only access to the registered mailer. Returns a borrow
of the Arc so handlers can .clone() it cheaply when they
need to move the handle into an async future. Always returns
a live mailer — Admin::new() seeds LogMailer as the
default, so this never returns None.
Sourcepub fn has_custom_mailer(&self) -> bool
pub fn has_custom_mailer(&self) -> bool
Whether the project explicitly called Self::mailer to
register a mailer. Returns false for Admin::new() (the
framework’s LogMailer default is in place); flips to true
on any subsequent call to mailer(...), regardless of the
concrete type supplied — the framework trusts the operator’s
explicit override.
Read by the R1 strict-mailer boot guard: when
RecoveryPolicy::strict_mailer_required() == true and this
returns false, register_admin_routes panics at startup
rather than registering the recovery routes against a
production-unsafe default mailer.
Sourcepub fn password_policy(self, policy: SharedPasswordPolicy) -> Self
pub fn password_policy(self, policy: SharedPasswordPolicy) -> Self
Replace the active password policy. R1 ships with the
length-only DefaultPasswordPolicy (min_len = 10);
production deployments commonly override to 12+, and
regulated deployments may ship a full custom impl with breach
blocklists or organisational complexity rules
(DESIGN_RECOVERY.md §13).
Typical project wiring:
use std::sync::Arc;
use rustio_admin::auth::DefaultPasswordPolicy;
let admin = Admin::new()
.password_policy(Arc::new(DefaultPasswordPolicy::with_min_len(16)));Sourcepub fn active_password_policy(&self) -> &SharedPasswordPolicy
pub fn active_password_policy(&self) -> &SharedPasswordPolicy
Read-only access to the registered password policy. Returns
a borrow of the Arc so handlers can .clone() it cheaply
when needed. Always returns a live policy — Admin::new()
seeds DefaultPasswordPolicy so this never returns None.
Sourcepub fn recovery_policy(self, policy: SharedRecoveryPolicy) -> Self
pub fn recovery_policy(self, policy: SharedRecoveryPolicy) -> Self
Replace the active recovery policy. R1 ships with
DefaultRecoveryPolicy (TTL 1h, request 5/15min, consume
10/5min, strict-mailer guard off); production deployments
commonly opt into the strict guard via
with_strict_mailer_required(true) after registering a real
mailer (DESIGN_RECOVERY.md §12).
Typical project wiring:
use std::sync::Arc;
use rustio_admin::auth::DefaultRecoveryPolicy;
let admin = Admin::new()
.recovery_policy(Arc::new(
DefaultRecoveryPolicy::new()
.with_strict_mailer_required(true),
));Sourcepub fn active_recovery_policy(&self) -> &SharedRecoveryPolicy
pub fn active_recovery_policy(&self) -> &SharedRecoveryPolicy
Read-only access to the registered recovery policy. Returns
a borrow of the Arc. Always live — Admin::new() seeds
DefaultRecoveryPolicy so this never returns None.
Sourcepub fn require_mfa(self, policy: MfaPolicy) -> Self
pub fn require_mfa(self, policy: MfaPolicy) -> Self
Replace the active MFA enforcement policy. R3 ships with
MfaPolicy::Optional as the default — pre-R3 framework
behaviour, no opt-in required. Production deployments that
want MFA enforcement opt in via this builder.
Forward-only enforcement (D6). Switching to
MfaPolicy::Required does NOT retroactively revoke
existing sessions; the login_guard redirects users
without MFA to /admin/mfa/enroll at the next request.
The pattern mirrors R2’s must_change_password
interstitial (DESIGN_R3_MFA.md §12.3).
Boot guard (D1). When MfaPolicy != Disabled, the
framework refuses to boot if RUSTIO_SECRET_KEY is
unset — the env var is required for AES-256-GCM
encryption of TOTP secrets at rest. The boot check lands
in a later R3 commit; this builder records the policy
without the check.
Typical project wiring:
use rustio_admin::auth::{MfaPolicy, Role};
// Universal:
let admin = Admin::new().require_mfa(MfaPolicy::Required);
// Privileged roles only:
const PRIVILEGED: &[Role] = &[Role::Administrator, Role::Supervisor];
let admin = Admin::new()
.require_mfa(MfaPolicy::RequiredForRoles(PRIVILEGED));Sourcepub fn active_mfa_policy(&self) -> MfaPolicy
pub fn active_mfa_policy(&self) -> MfaPolicy
Read-only access to the active MFA policy. Returns by
value — the policy is Copy. Always live — Admin::new()
seeds MfaPolicy::default (Optional) so this never
returns None.
pub fn model<M>(self) -> Selfwhere
M: ModelAdmin + Model,
pub fn entries(&self) -> &[AdminEntry]
Sourcepub fn user_profile_extension<F, Fut>(self, ext: F) -> Selfwhere
F: Fn(Db, UserProfile) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<Vec<UserProfileSection>>> + Send + 'static,
pub fn user_profile_extension<F, Fut>(self, ext: F) -> Selfwhere
F: Fn(Db, UserProfile) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<Vec<UserProfileSection>>> + Send + 'static,
Register a project-specific extension that contributes extra
sections to the built-in user profile page. The closure is
invoked on every render of GET /admin/users/:id (Overview tab);
it receives the Db handle and the loaded
crate::auth::UserProfile (no password_hash) and returns a
Vec<UserProfileSection>. Sections render in the order returned,
immediately after the core profile show-grid.
Zero-config baseline: don’t call this method, and the extension
area stays empty. Projects that need richer layout than key-value
rows override the {% block project_user_fields %} template
block in templates/admin/user_view.html instead.
pub fn find(&self, admin_name: &str) -> Option<&AdminEntry>
Sourcepub async fn seed_permissions(&self, db: &Db) -> Result<()>
pub async fn seed_permissions(&self, db: &Db) -> Result<()>
Register the canonical (add/change/delete/view) permissions for
every model. Call during startup after init_tables.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for Admin
impl !RefUnwindSafe for Admin
impl Send for Admin
impl Sync for Admin
impl Unpin for Admin
impl UnsafeUnpin for Admin
impl !UnwindSafe for Admin
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
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