rustio-admin 0.18.4

Django Admin, but for Rust. A small, focused admin framework.
Documentation
# Architecture

Brief module map — read this before contributing or filing a structural bug. The full design rationale lives in [`STRATEGIC_RESET_PLAN.md`](./archive/STRATEGIC_RESET_PLAN.md).

## Workspace

```
rustio-admin/
├── crates/
│   ├── rustio-admin/          ← the library (everything project code consumes)
│   ├── rustio-admin-macros/   ← #[derive(RustioAdmin)] proc-macro
│   └── rustio-admin-cli/      ← `rustio` CLI binary (work in progress)
└── examples/
    └── minimal/               ← canonical project skeleton
```

Workspace deps (`tokio`, `hyper`, `sqlx`, `minijinja`, `chrono`, `argon2`, …) are pinned at the workspace level for one-place version control. Project-side code never reaches for them transitively — the framework re-exports the surface it cares about (`Db`, `Router`, `Server`, `Templates`, etc.).

## Module map (`rustio-admin`)

| Path | Responsibility |
|---|---|
| `error.rs`        | `Error` / `Result` + `From<sqlx::Error>` mapping (constraint violations → 409 Conflict). |
| `http.rs`         | `Request`, `Response`, `FormData`, typed `Context` for per-request middleware data. |
| `router.rs`       | Tiny middleware-aware router; `:param` captures; 404 vs 405 distinction. |
| `server.rs`       | hyper-based `Server` with graceful Ctrl-C shutdown + `serve_static`. |
| `orm.rs`          | `Db`, `DbOptions`, `Model`, `Value`, `Row`, generic CRUD helpers. Postgres-only. |
| `migrations.rs`   | Walks numerically prefixed `*.sql` files; transactional apply with a tracking table. |
| `templates.rs`    | minijinja loader with disk-override path; embedded admin templates baked via `include_str!`. |
| `background.rs`   | Tiny periodic task runner (currently only the session sweeper). |
| `middleware/`     | `logger`, `security_headers`, `rate_limit` (DashMap token bucket), `gzip`, `csrf_protect` (double-submit cookie). |
| `auth/`           | `Identity`, `Role` ladder, sessions, users (Argon2), per-model permissions with a 60-s cache. |
| `admin/`          | The whole admin panel — see below. |

### `admin/` submodules

| Path | Responsibility |
|---|---|
| `types.rs`        | `Admin` builder, `AdminEntry`, `AdminField`, `AdminModel` trait, `AdminTheme`, `SiteBranding`, `ListOpts`/`ListPage`, `AdminOps` trait + `CoreUserOps` stub for the synthetic User entry. |
| `modeladmin.rs`   | `ModelAdmin` trait + `Fieldset` + `SortDir` + `parse_order_spec`. |
| `ops.rs`          | `ConcreteOps<M>` — the single live `AdminOps` impl. Builds the SQL list-page query (WHERE / ORDER BY / LIMIT / OFFSET) with `M::COLUMNS` validation. |
| `routes.rs`       | `register_admin_routes`. Mounts every admin URL with permission/role guards. |
| `handlers.rs`     | `dashboard`, `list_model`, `show_*_form`, `do_create/update/delete`, `show_log_entries`, `show_password_change`, … one handler per URL. |
| `render.rs`       | Template context builders (`BaseContext`, `ListCtx`, `FormCtx`, …). All HTML stays in templates; this file produces only `serde::Serialize` data. |
| `builtin.rs`      | Bespoke pages for `/admin/users/*` and `/admin/groups/*` (richer than the generic CRUD — last-developer guards, perm-grid editor, profile-page tabs). |
| `audit.rs`        | `rustio_admin_actions` schema + `record` / `recent` / `for_object` queries. |
| `relations.rs`    | `RelationRegistry` built from `&[AdminEntry]` — drives the delete-confirm cascade list and FK widgets. |
| `filters.rs`      | `classify_field`, `field_ui_metadata`, `infer_filters` — pure helpers that turn schema metadata into UI hints. |
| `icons.rs`        | Inline SVG catalogue baked at compile time; exposed via the `icon()` minijinja function. |

## Templates

Located under `crates/rustio-admin/assets/templates/admin/`. Baked into the binary via `include_str!` in `templates.rs::EMBEDDED_TEMPLATES`. Disk overrides win at render time when a project sets `RUSTIO_TEMPLATE_DIR` (or passes a path to `Templates::new`).

```
assets/templates/admin/
├── _base.html                  ← shell with named blocks; inline theme bootstrap script
├── _topbar.html                ← partial: brand + identity + theme toggle
├── _sidebar.html               ← partial: nav with model entries + auth links
├── _theme.html                 ← partial: <style> tag injecting AdminTheme overrides
├── includes/
│   ├── _form_field.html        ← single render path for every widget
│   └── _field_errors.html
├── login.html
├── index.html                  ← dashboard
├── list.html                   ← changelist (toolbar + filters + sort + per-page + pills + numbered pagination + bulk select)
├── form.html                   ← generic create/edit form
├── confirm_delete.html
├── bulk_confirm_delete.html    ← built-in cascade-aware bulk delete confirm
├── bulk_confirm_action.html    ← generic confirm page for project-defined bulk actions
├── error.html / forbidden.html
├── object_history.html / log_entries.html
├── password_change.html
├── users_list.html / user_new.html / user_edit.html / user_view.html / user_confirm_delete.html
└── groups_list.html / group_new.html / group_edit.html / group_confirm_delete.html
```

## CSS + JS

Located under `crates/rustio-admin/assets/static/`. Single hand-written stylesheet (~1.9k LOC) and a minimal JS file (~160 LOC) for the sidebar drawer, dropdown wiring, bulk-select form helper, and FK autocomplete. **No build step.**

The stylesheet is the single source of truth for every design token — a single `:root` block defines the whole palette. The framework is **light-only**: there is no dark variant, the OS `prefers-color-scheme` preference is ignored, and no `data-rio-theme` attribute or toggle exists. The token surface is wider than 0.1.x:

- **Six override-point tokens** that `AdminTheme` can patch: `--rio-accent`, `--rio-bg`, `--rio-surface`, `--rio-text`, `--rio-text-muted`, `--rio-border`.
- **Surface ladder** (`--rio-surface`, `--rio-surface-2`, `--rio-surface-3`) for layered depth.
- **Text scale** (`--rio-text-strong`, `--rio-text`, `--rio-text-muted`, `--rio-text-subtle`) for clear hierarchy.
- **Border scale** (`--rio-border-soft`, `--rio-border`, `--rio-border-strong`) for hover / dividers / focus rings.
- **Typography scale** (sizes, line-heights, weights, family fallback chains).
- **Spacing scale** (`--rio-s1` through `--rio-s7`).
- **Three soft shadows** (xs / regular / lg) at `0.04–0.10` alpha.
- **Brand accent semantics** plus **success / warning / danger / info** with hue-tuned variants.

`_theme.html` is **purely an override patch** — when `AdminTheme` has no fields set, the partial emits no markup at all and `admin.css` is the only style source. When fields are set, the partial emits a `<style>` block *after* `<link rel="stylesheet" href="/static/admin.css">` in `_base.html` with a single `html { ... }` selector so an override wins cascade ties on source order without needing `!important`. Project re-skins are one `Admin::accent_color("…")` call away — tokens you didn't override fall through to the framework defaults.

Three responsive breakpoints (mobile-first):

| Breakpoint | Layout |
|---|---|
| `< 768px`  | Single column. Sidebar off-canvas behind a hamburger. Tables horizontally scrollable. |
| `≥ 768px`  | Two-column flex row. Sidebar `position: sticky` below the topbar with its own internal scroll. |
| `≥ 1280px` | Wider sidebar, more padding. Main content capped at 1280px so wide monitors don't sprawl table rows. |

## Public API surface

Stable export points (in import order of how a project usually meets them):

```rust
use rustio_admin::{
    // Core types
    Db, DbOptions, Model, Row, Value,
    Router, Server, Response, Request, FormData,
    Error, Result,

    // Auth
    Identity, Role,
    auth, background, middleware, migrations,

    // Admin
    Admin, AdminField, AdminModel, FieldType,
    BulkAction, Fieldset, ModelAdmin,
    register_admin_routes,

    // Macros
    RustioAdmin,
};

// Sub-namespaces a project may reach into:
use rustio_admin::admin::{AdminTheme, SiteBranding, UserProfileRow, UserProfileSection};
use rustio_admin::templates::Templates;
```

Everything else (`ConcreteOps`, `AdminOps`, `ListOpts`, `BaseContext`, …) is `pub(crate)` or `pub` within `admin::*` for testing only — projects never touch it directly.

## Hard architectural rules

The full list lives in [§8 of the strategic reset plan](./archive/STRATEGIC_RESET_PLAN.md#8-strict-architectural-rules). The CI pipeline enforces **rule #1** (no Tier 2 features) with a `git grep` guard on every PR:

```yaml
forbidden='HasSchema|ModelSchema|RustType|SchemaOps|from_schema|contract_validator|contract_doctor|RustioModel'
```

If you need any of those for a project, the right layer is a future `rustio-pro` crate — not this one.

Other rules summarised:

- **Postgres only.** No SQLite branches, no MySQL branches, no abstraction over databases.
- **No second runtime.** `ConcreteOps<M>` is the runtime. No bridges, no schema-driven sibling.
- **No magic.** Macros emit obvious code; `cargo expand` should always show the full picture.
- **Templates and CSS are hand-written.** No Tailwind, no PostCSS, no Sass, no build step.
- **Boring software.** Prefer obvious code over clever code. Prefer explicit over inferred. Prefer compile-time errors over runtime errors.
- **Every public type and method has a doc comment.** No silent contracts.