pub struct App { /* private fields */ }Expand description
The application builder.
fn main() -> renox::Result {
App::new()
.migrations(renox::migrations!())
.module(Products)
.job::<SendReceipt>()
.schedule(|s| { s.daily_at("02:00", "cleanup", cleanup); })
.seeder(seed)
.run()
}The built binary is also the app’s command line, like Laravel’s artisan:
my-app migrate, my-app queue:work, my-app help.
Implementations§
Source§impl App
impl App
Sourcepub fn detect_locale(self) -> Self
pub fn detect_locale(self) -> Self
Picks a visitor’s language from their browser (Accept-Language)
when they haven’t chosen one: the first of their languages this app
has texts for (the built-in en and id, or a
resources/lang/<locale>.json), else APP_LOCALE. A language set
with i18n::remember_locale still wins. Responses then carry
Vary: Accept-Language, so caches keep the languages apart.
App::new().detect_locale()Sourcepub fn layer<L>(self, layer: L) -> Selfwhere
L: Layer<Route> + Clone + Send + Sync + 'static,
L::Service: Service<Request> + Clone + Send + Sync + 'static,
<L::Service as Service<Request>>::Response: IntoResponse + 'static,
<L::Service as Service<Request>>::Error: Into<Infallible> + 'static,
<L::Service as Service<Request>>::Future: Send + 'static,
pub fn layer<L>(self, layer: L) -> Selfwhere
L: Layer<Route> + Clone + Send + Sync + 'static,
L::Service: Service<Request> + Clone + Send + Sync + 'static,
<L::Service as Service<Request>>::Response: IntoResponse + 'static,
<L::Service as Service<Request>>::Error: Into<Infallible> + 'static,
<L::Service as Service<Request>>::Future: Send + 'static,
Wraps every route of the app’s modules in a tower layer, e.g. a
middleware function (framework routes such as /health and
public/ files aren’t wrapped). It runs after Renox has loaded the
session and the user, so it can use AuthUser or Session:
use renox::axum::extract::Request;
use renox::axum::middleware::{Next, from_fn};
async fn stamp(user: Option<AuthUser>, req: Request, next: Next) -> Response {
let mut res = next.run(req).await;
let who = if user.is_some() { "member" } else { "guest" };
res.headers_mut().insert("x-visitor", who.parse().unwrap());
res
}
App::new().layer(from_fn(stamp))Layers run in the order added: the first one sees the request first.
Sourcepub fn csp(self, allow: impl FnOnce(&mut Csp)) -> Self
pub fn csp(self, allow: impl FnOnce(&mut Csp)) -> Self
Allows other sites in the Content-Security-Policy, e.g.
.csp(|csp| { csp.allow("script-src", "https://www.googletagmanager.com"); }).
Sourcepub fn embed(self, embedded: Embedded) -> Self
pub fn embed(self, embedded: Embedded) -> Self
Views, translations and public files compiled into the binary:
.embed(renox::embedded!()). They’re used when APP_DEBUG is off;
while debugging, files are read from disk so edits show up at once.
Sourcepub fn with_config(config: Config) -> Self
pub fn with_config(config: Config) -> Self
Uses the given configuration instead of loading it from the environment.
Sourcepub fn module(self, module: impl Module) -> Self
pub fn module(self, module: impl Module) -> Self
Adds a module: its routes, migrations and registrations.
Sourcepub fn migrations(self, migrations: &[Migration]) -> Self
pub fn migrations(self, migrations: &[Migration]) -> Self
Registers app-level migrations, usually renox::migrations!().
Sourcepub fn seeder<F, Fut>(self, seeder: F) -> Self
pub fn seeder<F, Fut>(self, seeder: F) -> Self
Registers a seeder for db:seed. Seeders run in registration order,
in the app’s context (model hooks and renox::context::app() see
it), and get the app’s state: state.db, its config, storage…
App::new().seeder(|state| async move {
Product::factory().count(50).create(&state.db).await?;
Ok(())
})Sourcepub fn gate(
self,
name: &str,
check: impl Fn(&User) -> bool + Send + Sync + 'static,
) -> Self
pub fn gate( self, name: &str, check: impl Fn(&User) -> bool + Send + Sync + 'static, ) -> Self
Defines a gate: an ability that depends only on the user.
App::new().gate("admin", |user| user.email.ends_with("@shop.example"))
// in a handler: auth.gate("admin")?; in a template: {% if can('admin') %}Sourcepub fn gate_before(
self,
check: impl Fn(&User, &str) -> Option<bool> + Send + Sync + 'static,
) -> Self
pub fn gate_before( self, check: impl Fn(&User, &str) -> Option<bool> + Send + Sync + 'static, ) -> Self
A gate that may query the database, e.g. whether the user belongs to
a team. Check it in handlers with auth.gate_async(name).await?;
templates can’t wait for it (can() denies it), so pass its answer in
the view’s context.
App::new().gate_async("billing", |user, state| async move {
let n: i64 = renox::db::sql("SELECT COUNT(*) FROM team_admins WHERE user_id = ?")
.bind(user.id)
.scalar(&state.db)
.await?;
Ok(n > 0)
})
// in a handler: auth.gate_async("billing").await?;Asked before every gate, permission and policy check: Some(true)
allows, Some(false) denies, None goes on to the check itself.
Typically lets super-admins do everything.
App::new().gate_before(|user, _ability| {
(user.get::<String>("role").as_deref() == Some("owner")).then_some(true)
})Sourcepub fn gate_async<F, Fut>(self, name: &str, check: F) -> Self
pub fn gate_async<F, Fut>(self, name: &str, check: F) -> Self
Defines the gate name with a check that can await, e.g. to query
the database. can(name) and require_gate ask it like any gate.
Sourcepub fn webhook<W: Webhook>(self) -> Self
pub fn webhook<W: Webhook>(self) -> Self
Receives W’s webhooks: see renox::webhook. Also add the route
with Routes::webhook::<W>(path).
Sourcepub fn listen<E, F, Fut>(self, listener: F) -> Self
pub fn listen<E, F, Fut>(self, listener: F) -> Self
Runs listener whenever an E is emitted with state.emit(..).
Sourcepub fn command<F, Fut>(self, name: &str, about: &str, run: F) -> Self
pub fn command<F, Fut>(self, name: &str, about: &str, run: F) -> Self
Adds a command the app binary runs: my-app <name> [args], e.g. to
create the first admin or run an import. See crate::command.
Sourcepub fn typed_command<T: AppCommand>(self) -> Self
pub fn typed_command<T: AppCommand>(self) -> Self
A command whose arguments are declared with clap; see
AppCommand.
Sourcepub fn rate_limiter(
self,
name: &str,
rule: impl Fn(&LimitRequest<'_>) -> Limit + Send + Sync + 'static,
) -> Self
pub fn rate_limiter( self, name: &str, rule: impl Fn(&LimitRequest<'_>) -> Limit + Send + Sync + 'static, ) -> Self
A named rate limit for Routes::throttle_by(name), whose rule
picks the limit for each request (by user, role, IP, API key…); see
crate::rate_limit::Limit.
Sourcepub fn report<F, Fut>(self, reporter: F) -> Self
pub fn report<F, Fut>(self, reporter: F) -> Self
Sends every error that needs a person (a 500, a job that failed for
good, a failed scheduled task) to reporter, e.g. an error tracker;
see crate::report.
Sourcepub fn channel<F, Fut>(self, name: &str, send: F) -> Self
pub fn channel<F, Fut>(self, name: &str, send: F) -> Self
Adds a notification channel (WhatsApp, SMS, Slack…); see
Registry::channel.
Also sends the CSRF token as an XSRF-TOKEN cookie that scripts can
read, and accepts it back in an X-XSRF-TOKEN header (Laravel’s
behaviour), for a JavaScript client on the same site: axios sends
it by itself. Forms and htmx don’t need it; they send _token or
X-CSRF-Token.
Sourcepub fn mailer(
self,
name: &str,
settings: impl Fn(&Config) -> Result<MailConfig> + Send + Sync + 'static,
) -> Self
pub fn mailer( self, name: &str, settings: impl Fn(&Config) -> Result<MailConfig> + Send + Sync + 'static, ) -> Self
Adds a mailer named name, e.g. a provider for newsletters or a
second SMTP account, used with state.mailer_named(name) or, by
listing it in MAIL_FAILOVER, when the default mailer fails.
settings runs once the configuration is loaded;
MailConfig::from_env reads <PREFIX>_MAILER, _HOST, …
use renox::mail::MailConfig;
// BACKUP_HOST=smtp.other-provider.com, BACKUP_USERNAME=…; MAIL_FAILOVER=backup
App::new().mailer("backup", |config| MailConfig::from_env(config, "BACKUP"))Sourcepub fn disk(
self,
name: &str,
settings: impl Fn(&Config) -> Result<StorageConfig> + Send + Sync + 'static,
) -> Self
pub fn disk( self, name: &str, settings: impl Fn(&Config) -> Result<StorageConfig> + Send + Sync + 'static, ) -> Self
Adds a storage disk named name (letters, digits, -, _), e.g.
backups on another bucket, read with state.disk_named(name). settings
runs once the configuration is loaded; StorageConfig::from_env
reads <PREFIX>_DISK, <PREFIX>_BUCKET, … A local disk keeps its
files in STORAGE_PATH/<name> unless its settings say otherwise.
use renox::storage::StorageConfig;
App::new()
.disk("backups", |config| StorageConfig::from_env(config, "BACKUPS"))
.disk("exports", |_| Ok(StorageConfig::default())) // local: storage/exportsSourcepub fn provide<T: Send + Sync + 'static>(self, value: T) -> Self
pub fn provide<T: Send + Sync + 'static>(self, value: T) -> Self
Makes value available everywhere the app runs: Provided<T> in
handlers, state.provided::<T>() in jobs, listeners, commands and
scheduled tasks. One value per type; a second one replaces the first,
and it wins over a module’s (Registry::provide). See
crate::Provided.
Gives every view a value computed per request; see Registry::share.
Sourcepub fn templates(
self,
hook: impl Fn(&mut Environment<'static>) + Send + Sync + 'static,
) -> Self
pub fn templates( self, hook: impl Fn(&mut Environment<'static>) + Send + Sync + 'static, ) -> Self
Adds template functions, filters or globals; see Registry::templates.
Sourcepub async fn boot(self) -> Result<Kernel>
pub async fn boot(self) -> Result<Kernel>
Connects to the database and builds the router, without serving.
Sourcepub async fn into_router(self) -> Result<Router>
pub async fn into_router(self) -> Result<Router>
Builds the router without starting a server, e.g. for tests.
Sourcepub fn run(self) -> Result
pub fn run(self) -> Result
Runs the command given on the command line (serve by default) on a
new Tokio runtime.
Sourcepub async fn run_args(
self,
args: impl IntoIterator<Item = impl Into<String>>,
) -> Result
pub async fn run_args( self, args: impl IntoIterator<Item = impl Into<String>>, ) -> Result
Runs one command as the app binary would, e.g. from a test or a
program that drives the app: ["migrate:status"], ["down", "--secret", "abc"], or [] for serve. Output goes to stdout.
App::new().run_args(["migrate"]).await?;Trait Implementations§
Auto Trait Implementations§
impl !RefUnwindSafe for App
impl !UnwindSafe for App
impl Freeze for App
impl Send for App
impl Sync for App
impl Unpin for App
impl UnsafeUnpin for App
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<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
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