Runique Admin

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 users model with is_staff and is_superuser fields (generated by model!)
  • The runique binary installed (cargo install runique or cargo build from 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 start regenerates 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?;
MethodRole
.prefix("/admin")Admin route prefix (default: /admin)
.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

Automatic robots.txt β€” When the admin panel is active, Runique automatically serves a /robots.txt route containing Disallow: /admin/ to exclude the interface from search engines. The prefix configured via .prefix() is respected. Use .no_robots_txt() if you want to manage this file yourself.


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.