Admin setup
Step-by-step guide to enable the admin interface in an existing Runique project.
Prerequisites
- A working Runique project with a configured database
- A
usersmodel withis_staffandis_superuserfields (generated bymodel!) - The
runiquebinary installed (cargo install runiqueorcargo buildfrom the workspace)
Step 1 β Create `src/admin.rs`
This file declares administrable resources via the admin! macro:
// src/admin.rs
use crate::entities::{users, articles};
use crate::forms::{RegisterForm, ArticleForm};
admin! {
users: users::Model => RegisterForm {
title: "Users",
},
articles: articles::Model => ArticleForm {
title: "Articles",
}
}
Step 2 β Generate `src/admins/` with the daemon
runique start
The daemon reads src/admin.rs, generates src/admins/ and launches cargo run.
The src/admins/ folder is created automatically β do not edit it manually.
src/admins/
βββ README.md
βββ mod.rs
βββ admin_panel.rs
If
src/admins/already exists from a previous generation,runique startregenerates it.
Step 3 β Declare the module in `src/main.rs`
mod admin;
mod admins; // module generated by runique start
Step 4 β Wire `.with_admin()` in the builder
use runique::app::builder::RuniqueAppBuilder as builder;
RuniqueApp::builder(config)
.routes(url::routes())
.with_database(db)
.with_admin(|a| {
a.site_title("Administration")
.auth(RuniqueAdminAuth::new())
.routes(admins::routes("/admin"))
.with_state(admins::admin_state())
})
.build()
.await?
.run()
.await?;
| Method | Role |
|---|---|
.prefix("secret") | Segment mounted in front of the admin (default: none) β see below |
.site_title("β¦") | Title displayed in the interface |
.auth(RuniqueAdminAuth::new()) | Admin authentication (default) |
.routes(admins::routes("/admin")) | Mounts CRUD routes under /admin |
.with_state(β¦) | Shared state generated by the daemon |
.no_robots_txt() | Disables the automatic /robots.txt |
.extra_routes(vec![β¦]) | Custom routes protected by the admin middleware |
.routes() and .prefix() β two distinct settings
.routes(admins::routes("/site-admin")) sets where the admin lives.
.prefix("secret") sets what is mounted in front of it. They compose:
routes(...) | .prefix(...) | Admin URL |
|---|---|---|
/site-admin | β | /site-admin/β¦ |
/site-admin | secret | /secret/site-admin/β¦ |
| (not called) | secret | /secret/admin/β¦ (default path) |
Call order does not matter, and slashes are optional: secret, /secret and
/secret/ are equivalent.
Version note β
.prefix()used to replace the admin path, putting it in competition with.routes(): the last call won, and swapping the two lines silently changed the served URLs. It now prefixes, as its name promises. A project that used.prefix("/backoffice")to rename the admin should pass that path toadmins::routes("/backoffice")instead.
Automatic robots.txt β When the admin panel is active, Runique automatically serves a
/robots.txtroute (aSitemap:line when.sitemap(β¦)is set, plus theContent-Signaldirectives). Use.no_robots_txt()if you want to manage this file yourself.The admin prefix is deliberately absent from it.
robots.txtis a public file: writingDisallow: /my-prefix/would publish the very path a custom prefix is often meant to keep discreet. AndDisallowasks crawlers not to fetch, not to index β a URL known from elsewhere can still show up in results.Exclusion is handled by the
X-Robots-Tag: noindex, nofollowheader, which Runique sets on every admin response β login page included, as well as redirects, errors and JSON replies. It leaks no path and stays in place even if you override the admin templates.
Custom admin routes (`extra_routes`)
For business views that go beyond the generated CRUD (e.g. order detail with related lines,
custom dashboard), declare extra routes in url.rs and register them via .extra_routes().
These routes automatically inherit the admin middleware: authentication, AdminState,
PrototypeAdminState (sidebar), and CurrentUser.
url.rs
pub fn admin_extra_routes() -> Vec<(&'static str, runique::axum::routing::MethodRouter)> {
vec![
("/orders/{number}/detail", view!{ admin_order_detail }),
]
}
main.rs
.with_admin(|a| a
.routes(admins::routes("/admin"))
.extra_routes(url::admin_extra_routes())
)
Paths are relative to the admin prefix β /orders/{number}/detail becomes
/admin/orders/{number}/detail.
Handler β retrieve the extensions injected by the middleware:
use runique::prelude::*;
use runique::admin::{AdminState, PrototypeAdminState, inject_admin_prefix, insert_admin_messages};
pub async fn admin_order_detail(
Extension(admin): Extension<Arc<AdminState>>,
Extension(proto): Extension<Arc<PrototypeAdminState>>,
mut request: Request,
) -> AppResult<Response> {
inject_admin_prefix(&mut request, &admin.config.prefix);
insert_admin_messages(&mut request, proto.registry.all());
// β¦ business logic β¦
request.render("admin/order_detail.html")
}
The template must extend admin_base to inherit the CSS and sidebar:
{% extends "admin_base" %}
Step 5 β Create a superuser
runique create-superuser
Follows an interactive wizard to create the first admin account (is_superuser = true).
Accessing the interface
Once the server is running, the interface is available at:
http://localhost:{PORT}/admin/
The /admin/login page redirects to the dashboard if credentials are valid.