rustango 0.22.0

Django-shaped batteries-included web framework for Rust: ORM + migrations + auto-admin + multi-tenancy + audit log + auth (sessions, JWT, OAuth2/OIDC, HMAC) + APIs (ViewSet, OpenAPI auto-derive, JSON:API) + jobs (in-mem + Postgres) + email + media (S3 / R2 / B2 / MinIO + presigned uploads + collections + tags) + production middleware (CSRF, CSP, rate-limiting, compression, idempotency, etc.).
Documentation
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{% block title %}rustango admin{% endblock title %}</title>
<style>
* { box-sizing: border-box; }
body { font-family: -apple-system, system-ui, sans-serif; margin: 0; min-height: 100vh; background: #fafafa; color: #222; }
.layout { display: grid; grid-template-columns: 240px 1fr; min-height: 100vh; }
aside.sidebar { background: #f4eee7; border-right: 1px solid #e0d8cc; padding: 1.25rem 1rem; font-size: .9em; }
aside.sidebar h2 { margin: 0 0 .25rem 0; font-size: 1.1rem; color: #b03a2e; letter-spacing: .03em; }
aside.sidebar p.subtitle { margin: 0 0 1.25rem 0; font-size: .85em; color: #666; }
aside.sidebar h3 { margin: 1rem 0 .35rem 0; font-size: .8em; text-transform: uppercase; letter-spacing: .05em; color: #888; font-weight: 600; }
aside.sidebar ul { list-style: none; padding: 0; margin: 0; }
aside.sidebar li { margin: .15rem 0; }
aside.sidebar a { display: block; padding: .25rem .5rem; border-radius: 3px; color: #333; text-decoration: none; }
aside.sidebar a:hover { background: rgba(0,0,0,.05); }
aside.sidebar a.active { background: #fff; color: #b03a2e; font-weight: 600; box-shadow: 0 1px 2px rgba(0,0,0,.06); }
aside.sidebar .empty { color: #aaa; font-style: italic; font-size: .85em; }
main.content { padding: 1.5rem 2rem; }
nav.breadcrumb ol { list-style: none; padding: 0; margin: 0 0 1rem 0; display: flex; gap: .35rem; align-items: center; flex-wrap: wrap; font-size: .9em; color: #888; }
nav.breadcrumb li + li::before { content: "›"; margin-right: .35rem; color: #ccc; }
nav.breadcrumb a { color: #888; }
nav.breadcrumb a:hover { color: #b03a2e; text-decoration: none; }
nav.breadcrumb strong { color: #333; }
table { border-collapse: collapse; width: 100%; background: #fff; }
th, td { border: 1px solid #ddd; padding: .35rem .6rem; text-align: left; vertical-align: top; }
th { background: #f4f4f4; }
a { color: #0a4; text-decoration: none; }
a:hover { text-decoration: underline; }
small { color: #888; font-weight: normal; }
em { color: #888; }
input[type=text], input[type=number], input[type=date], input[type=datetime-local], input[type=search], textarea { width: 100%; box-sizing: border-box; padding: .25rem .4rem; font: inherit; }
textarea { min-height: 4rem; }
input:invalid, textarea:invalid { border-color: #e55; outline-color: #e55; }
input:invalid + small, textarea:invalid + small { color: #c00; }
.error { background: #fee; border: 1px solid #f88; padding: .5rem .75rem; border-radius: 4px; }
.muted { color: #aaa; }
.search { display: flex; gap: .5rem; align-items: center; margin: .5rem 0; }
.search input[type=search] { flex: 1; }
.active-filters { color: #666; font-size: .9em; margin: .5rem 0; }
.active-filters code { background: #eef; padding: .1rem .35rem; border-radius: 3px; }
.pager { margin-top: 1rem; }
button { padding: .4rem 1rem; font: inherit; cursor: pointer; }
dl { display: grid; grid-template-columns: max-content 1fr; gap: .25rem 1rem; }
dt { font-weight: bold; }
.audit-cleanup { background: #fff; border: 1px solid #e0d8cc; border-radius: 4px; padding: .5rem .75rem; margin: .75rem 0; display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; font-size: .9em; }
.audit-cleanup label { display: inline-flex; gap: .35rem; align-items: center; }
.audit-cleanup small.muted { font-size: .85em; }
.audit-trail { margin-top: 2rem; padding-top: 1rem; border-top: 1px solid #e0d8cc; }
.audit-trail h2 { font-size: 1rem; text-transform: uppercase; letter-spacing: .05em; color: #888; margin-bottom: .5rem; }
.audit-trail ol { padding-left: 0; list-style: none; }
.audit-trail li { background: #fff; border: 1px solid #e0d8cc; border-radius: 4px; padding: .5rem .75rem; margin-bottom: .5rem; }
.audit-op { display: inline-block; padding: .1rem .4rem; border-radius: 3px; font-size: .75em; text-transform: uppercase; letter-spacing: .04em; font-weight: 600; }
.audit-op-create { background: #e6f4ea; color: #1d6a30; }
.audit-op-update { background: #fff4e0; color: #8a5a00; }
.audit-op-delete { background: #fde7e7; color: #b03a2e; }
.audit-op-soft_delete { background: #fde7e7; color: #b03a2e; }
.audit-op-restore { background: #e6f4ea; color: #1d6a30; }
.audit-op-action { background: #e7eefb; color: #1f3c80; }
.audit-changes { font-family: ui-monospace, monospace; font-size: .8em; background: #f7f4ee; padding: .35rem .5rem; border-radius: 3px; margin: .35rem 0 0 0; white-space: pre-wrap; word-break: break-all; }
.form-fieldset { border: 1px solid #e0d8cc; border-radius: 4px; margin: 0 0 1rem 0; padding: .5rem 1rem 1rem; background: #fff; }
.form-fieldset legend { padding: 0 .5rem; font-weight: 600; color: #555; font-size: .9em; text-transform: uppercase; letter-spacing: .03em; }
.form-fieldset > table { background: transparent; }
.form-fieldset > table th, .form-fieldset > table td { border: none; border-bottom: 1px solid #f0ebe4; padding: .35rem .4rem; }
.list-form { margin: 0; }
.action-bar { display: flex; gap: .5rem; align-items: center; padding: .35rem .5rem; background: #f4eee7; border: 1px solid #e0d8cc; border-bottom: none; border-radius: 4px 4px 0 0; font-size: .9em; }
.action-bar select { padding: .25rem .35rem; font: inherit; }
.checkbox-col { width: 28px; text-align: center; padding: .15rem .25rem !important; }
.list-shell { display: grid; grid-template-columns: minmax(0, 1fr) 220px; gap: 1.5rem; align-items: start; }
.list-shell.no-rail { grid-template-columns: 1fr; }
.list-rail { font-size: .9em; }
.list-rail .facet { background: #fff; border: 1px solid #e0d8cc; border-radius: 4px; padding: .5rem .75rem; margin-bottom: .75rem; }
.list-rail .facet h4 { margin: 0 0 .35rem 0; font-size: .8em; text-transform: uppercase; letter-spacing: .03em; color: #888; font-weight: 600; }
.list-rail .facet ul { list-style: none; padding: 0; margin: 0; }
.list-rail .facet li { margin: .15rem 0; line-height: 1.4; }
.list-rail .facet a { color: #333; }
.list-rail .facet a.active { color: #b03a2e; font-weight: 600; }
.list-rail .facet .count { color: #888; font-size: .85em; }
.list-rail .facet .facet-more a { color: #888; font-style: italic; }
.list-rail .facet .facet-more a:hover { color: #b03a2e; }
@media (max-width: 1000px) {
  .list-shell { grid-template-columns: 1fr; }
}
@media (max-width: 720px) {
  .layout { grid-template-columns: 1fr; }
  aside.sidebar { border-right: none; border-bottom: 1px solid #e0d8cc; }
  main.content { padding: 1rem; }
}
</style>
</head>
<body>
<div class="layout">
  {% include "_sidebar.html" %}
  <main class="content">
{% block body %}{% endblock body %}
  </main>
</div>
</body>
</html>