Releases and security

2.1.8 - 2026-05-28

Fixed — runique (admin, bulk)

  • bulk_create violated UNIQUE constraint on re-submit: the generated create_fn performed a plain INSERT per value. Re-submitting the same days caused a UNIQUE violation that stopped the loop. The generator now emits an upsert: for each value, it checks whether a record with that value already exists (via Expr::cust(format!("CAST({field} AS TEXT) = '{}'"...))), then updates if found or inserts if not.
  • Edit view used multi-select form when bulk_create was declared: when bulk_create is declared without an explicit edit_form, the daemon now auto-generates an edit_form_builder using module::AdminForm (standard single-record form). The individual edit view no longer uses the multi-select create form.
  • Unique fields appeared in bulk edit form: bulk editing a resource with UNIQUE-constrained fields could produce a UNIQUE violation when the same value was applied to multiple records. The generator now emits UNIQUE_FIELDS per entity (from derive_form!{} unique constraints). These fields are automatically excluded from the bulk edit form (both GET rendering and POST update map).

Added — runique (middleware)

  • Anti-bot honeypot middleware: AntiBot::new("field_name") injects a hidden trap field into all forms on the protected scope. If the field is filled on POST, form.is_valid() returns false immediately without running field-level validation.
  • RateLimiter per-method configuration: rate_limit_get(), rate_limit_post(), rate_limit_put(), rate_limit_delete() allow setting independent limits per HTTP method in addition to the global rate_limit().

Added — runique (forms)

  • FormTracing structured tracing for all form pipeline stages: when RuniqueLog::forms is configured, each stage (field registration, set_value, validate, finalize, render) emits a structured tracing event at the configured level.
  • cleaned_enum<T>() on RuniqueForm: reads a validated field value and tries to convert it to a SeaORM ActiveEnum.
  • add_value() on RuniqueForm: forces a value on a named field, bypassing fill(). Useful for fields skipped by the form pipeline (e.g. password hash pre-computed before form processing).