rustango 0.31.1

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" data-theme="{{ theme_mode | default(value='auto') }}">
    <head>
        <meta charset="utf-8">
        <title>Change password — {{ brand_name | default(value=tenant_name) }}</title>
        {% if brand_favicon_url %}
            <link rel="icon" href="{{ brand_favicon_url | safe }}">
        {% else %}
            <link rel="icon"
                  href="{{ static_url | default(value='/__static__') }}/rustango.png">
        {% endif %}
        <script>
  (function(){try{var t=localStorage.getItem('rustango-theme');if(t==='light'||t==='dark'||t==='auto'){document.documentElement.dataset.theme=t;}}catch(e){}})();
        </script>
        {% include "_theme_tokens.html" %}
        {% if brand_css %}<style>:root { {{ brand_css | safe }} }</style>{% endif %}
        <style>
    body {
      margin: 0;
      font: 14px/1.45 var(--font-family-base);
      background: var(--color-bg);
      color: var(--color-fg);
      display: flex;
      min-height: 100vh;
      align-items: center;
      justify-content: center;
    }
    .card {
      width: 380px;
      padding: var(--space-5);
      border: 1px solid var(--color-border);
      border-radius: var(--radius-md);
      background: var(--color-bg-surface);
      box-shadow: var(--shadow-md);
    }
    .brand {
      display: flex;
      align-items: center;
      gap: var(--space-2);
      margin-bottom: var(--space-2);
    }
    .brand img.brand-logo {
      height: 36px;
      width: auto;
      max-width: 100%;
      object-fit: contain;
    }
    .brand h1 {
      color: var(--color-accent);
      font-size: 1.4rem;
      margin: 0;
    }
    p.sub {
      color: var(--color-fg-muted);
      margin: 0 0 var(--space-5) 0;
    }
    p.sub strong { color: var(--color-fg); }
    label {
      display: block;
      font-size: var(--font-size-xs);
      color: var(--color-fg-muted);
      margin-top: var(--space-3);
      text-transform: uppercase;
      letter-spacing: 0.05em;
    }
    input[type=password] {
      width: 100%;
      padding: 0.5rem 0.6rem;
      border: 1px solid var(--color-border);
      border-radius: var(--radius-sm);
      font: inherit;
      margin-top: var(--space-1);
      box-sizing: border-box;
      background: var(--color-bg-input);
      color: var(--color-fg);
    }
    input:focus { outline: 2px solid var(--color-accent); outline-offset: 1px; }
    button {
      margin-top: var(--space-5);
      width: 100%;
      padding: 0.6rem;
      background: var(--color-accent);
      color: var(--color-fg-on-accent);
      border: 0;
      border-radius: var(--radius-sm);
      font: inherit;
      font-weight: var(--font-weight-bold);
      cursor: pointer;
    }
    button:hover { background: var(--color-accent-hover); }
    .error {
      color: var(--color-error-fg);
      background: var(--color-error-bg);
      padding: 0.5rem 0.75rem;
      border-radius: var(--radius-sm);
      margin: var(--space-3) 0 0 0;
      font-size: 13px;
    }
    .success {
      color: var(--color-success-fg);
      background: var(--color-success-bg);
      padding: 0.5rem 0.75rem;
      border-radius: var(--radius-sm);
      margin: var(--space-3) 0 0 0;
      font-size: 13px;
    }
    .nav {
      margin-top: var(--space-4);
      font-size: var(--font-size-sm);
      color: var(--color-fg-muted);
      text-align: center;
    }
    .nav a { color: var(--color-accent); text-decoration: none; }
    .nav a:hover { text-decoration: underline; }
        </style>
    </head>
    <body>
        <form class="card"
              method="post"
              action="{{ change_password_url | default(value='/__change-password') }}">
            <div class="brand">
                {% if brand_logo_url %}
                    <img class="brand-logo" src="{{ brand_logo_url | safe }}" alt="">
                {% else %}
                    <img class="brand-logo"
                         src="{{ static_url | default(value='/__static__') }}/rustango.png"
                         alt="">
                {% endif %}
                <h1>{{ brand_name | default(value=tenant_name) }}</h1>
            </div>
            <p class="sub">
                Change password for <strong>{{ tenant_slug }}</strong>
            </p>
            {% if error %}<div class="error">{{ error }}</div>{% endif %}
            {% if success %}<div class="success">{{ success }}</div>{% endif %}
            <label for="current_password">Current password</label>
            <input type="password"
                   id="current_password"
                   name="current_password"
                   autocomplete="current-password"
                   autofocus
                   required>
            <label for="new_password">New password</label>
            <input type="password"
                   id="new_password"
                   name="new_password"
                   autocomplete="new-password"
                   minlength="8"
                   required>
            <label for="confirm_password">Confirm new password</label>
            <input type="password"
                   id="confirm_password"
                   name="confirm_password"
                   autocomplete="new-password"
                   minlength="8"
                   required>
            <button type="submit">Change password</button>
            <div class="nav">
                <a href="{{ admin_url | default(value='/__admin') }}/">&larr; Back to admin</a>
            </div>
        </form>
    </body>
</html>