rustango 0.23.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
{% extends "base.html" %}
{% block title %}Activity — rustango admin{% endblock title %}
{% block body %}
<nav class="breadcrumb" aria-label="breadcrumb">
  <ol>
    <li><a href="/__admin/">{{ admin_title }}</a></li>
    <li><strong>Activity</strong></li>
  </ol>
</nav>
<h1>Activity</h1>
<p>{{ total }} audit entr{% if total == 1 %}y{% else %}ies{% endif %} across every model. Newest first.</p>

{% if active_filters | length > 0 %}
<p class="active-filters">filtered by:
  {% for f in active_filters %}<code>{{ f.key }}={{ f.value }}</code> {% endfor %}
  &middot; <a href="/__audit">clear</a>
</p>
{% endif %}

<form method="post" action="/__audit/cleanup" class="audit-cleanup" onsubmit="return confirm('Apply audit cleanup? This is irreversible.');">
  <label>
    <input type="radio" name="mode" value="older_than" checked>
    older than
    <input type="number" name="days" value="90" min="0" step="1" style="width: 5rem;">
    days
  </label>
  <label>
    <input type="radio" name="mode" value="keep_last">
    keep last
    <input type="number" name="keep" value="50" min="0" step="1" style="width: 5rem;">
    per row
  </label>
  <button type="submit">Apply cleanup</button>
  <small class="muted">Per-tenant. The cleanup itself is recorded as an audit entry.</small>
</form>

<div class="list-shell">
<div class="list-main">
{% if entries | length == 0 %}
<p><em>No audit entries match the current filter.</em></p>
{% else %}
<section class="audit-trail">
<ol>
  {% for e in entries %}
  <li>
    {% if e.action_name %}
      <span class="audit-op audit-op-action">action: {{ e.action_name }}</span>
    {% else %}
      <span class="audit-op audit-op-{{ e.operation }}">{{ e.operation }}</span>
    {% endif %}
    <a href="{{ e.detail_url | safe }}"><strong>{{ e.entity_table }}#{{ e.entity_pk }}</strong></a>
    by <strong>{{ e.source }}</strong>
    <small>at {{ e.occurred_at }}</small>
    <pre class="audit-changes">{{ e.changes }}</pre>
  </li>
  {% endfor %}
</ol>
</section>
{% endif %}

{% if last_page > 1 %}
<p class="pager">
  {% if page > 1 %}
    <a href="/__audit?page={{ page - 1 }}{{ pager_extras | safe }}">&larr; prev</a>
  {% else %}
    <span class="muted">&larr; prev</span>
  {% endif %}
  &middot; page {{ page }} of {{ last_page }} &middot;
  {% if page < last_page %}
    <a href="/__audit?page={{ page + 1 }}{{ pager_extras | safe }}">next &rarr;</a>
  {% else %}
    <span class="muted">next &rarr;</span>
  {% endif %}
</p>
{% endif %}
</div>{# /list-main #}
{% if facets and facets | length > 0 %}
<aside class="list-rail">
  {% for facet in facets %}
  <section class="facet">
    <h4>By {{ facet.field }}</h4>
    <ul>
    {% for v in facet.values %}
      <li>
        <a href="{{ v.toggle_url | safe }}"{% if v.active %} class="active"{% endif %}>{{ v.display | safe }}</a>
        <span class="count">({{ v.count }})</span>
      </li>
    {% endfor %}
    {% if facet.more_count and facet.more_count > 0 %}
      <li class="facet-more"><a href="{{ facet.show_all_url | safe }}">+{{ facet.more_count }} more&hellip;</a></li>
    {% endif %}
    </ul>
  </section>
  {% endfor %}
</aside>
{% endif %}
</div>{# /list-shell #}
{% endblock body %}