Skip to content

Releases: oliverandrich/burrow

v0.13.1

15 Apr 21:16
ab94adb

Choose a tag to compare

Changed

  • Den upgraded to v0.7.0 — picks up PostgreSQL JSONB comparison fixes, GroupBy SQL pushdown, revision TOCTOU fix, and link validation enforcement

Fixed

  • Invalid den struct tags in test modelsden:"name,index" and den:"key,unique" incorrectly included field names in the den tag (field names come from the json tag). Den v0.7.0 now rejects unknown tag options, catching these at registration time

v0.13.0

15 Apr 19:18
898e7e9

Choose a tag to compare

Breaking Changes

  • Auth admin rewritten with hand-written handlerscontrib/auth no longer uses ModelAdmin for its admin views. User list, edit, delete, and invite management are now direct handlers with custom templates. The HasAdmin interface (AdminRoutes, AdminNavItems) is unchanged. Auth admin templates now use globally registered icon functions (iconSearch, iconPlus, iconPersonSlash, iconPersonCheck, iconTrash, iconXCircle).
  • Jobs admin rewritten with hand-written handlerscontrib/jobs no longer uses ModelAdmin. Job list, detail, delete, retry, and cancel are now direct handlers with custom templates including status filter pills and inline action buttons.
  • ModelAdmin package removedcontrib/admin/modeladmin has been deleted entirely. All admin views now use hand-written handlers. The admin coordinator (layout, sidebar, auth middleware, nav groups) is unchanged.

Added

  • User search in admin — the user admin list now supports search across username, name, and email fields.
  • Inline invite creation — the invite admin list now features an htmx-powered inline form that slides open on button click instead of navigating to a separate page.
  • burrow.DefineTask[P]() — type-safe generic task definitions for the jobs system. Wraps Queue.Handle and Queue.Enqueue with automatic JSON marshalling, ensuring compile-time agreement between producer and consumer payload types. Auth email jobs migrated as first consumer.
  • burrow.DefineResultTask[P, R]() — variant of DefineTask for handlers that return both a result and an error. Results are persisted as JSON on the job and visible in the admin detail view.
  • Job result persistence — completed jobs now store their handler's return value in a Result field (JSON). Failed jobs additionally record the Go error type in ErrorClass and the timestamp of the last handler invocation in LastAttemptedAt. All three fields are displayed in the admin detail view and cleared on retry.
  • Job priority — jobs now have a Priority field (default 0, higher = more urgent). Set per-type via burrow.WithPriority(n) at handler registration. The claim query picks highest-priority jobs first, with FIFO ordering within the same priority level.

Changed

  • Seed requires --seed flagSeedable.Seed() no longer runs unconditionally on every server start. Pass --seed (or set SEED=true) to run seed functions. This prevents non-idempotent seeders from creating duplicates on restart.
  • Auth handlers split into focused files — the monolithic handlers.go (714 lines, 25 functions) has been split into handlers_registration.go, handlers_login.go, handlers_credentials.go, handlers_recovery.go, handlers_email.go, and handlers_helpers.go. No API or behavior changes — pure file reorganization for better navigability.
  • Admin decoupled from auth packagecontrib/admin no longer imports contrib/auth directly. Instead, it discovers auth middleware via the new burrow.AdminAuth interface from the registry. contrib/auth implements AdminAuth automatically. Custom auth systems can provide their own implementation.
  • Config.IsHTTPS() helper — replaces duplicated strings.HasPrefix(baseURL, "https://") checks in csrf, session, and secure apps.
  • WebAuthn flag aliases removed — the legacy --webauthn-rp-id, --webauthn-rp-display-name, --webauthn-rp-origin aliases (without auth- prefix) have been removed. Use the canonical --auth-webauthn-* names.
  • burrow.Queue split into Enqueuer + Queue — new Enqueuer interface holds Enqueue, EnqueueAt, and Dequeue. Queue embeds Enqueuer and adds Handle. Code that only submits jobs can now accept Enqueuer instead of the full Queue. TaskDefinition and ResultTask store Enqueuer internally.
  • Form fields with nil pointers render as zero valuesforms.extractFields now returns the element type's zero value (e.g. "" for *string) instead of nil when a pointer field is nil. Templates can use {{ .Value }} on optional fields without special-casing nil.

Fixed

  • Job retry backoff capped at 1 hour — exponential backoff (baseDelay * 2^(attempts-1)) now caps at 1 hour. Previously, high attempt counts could overflow time.Duration and produce negative or astronomically large delays.
  • Session flush logs encoding errorsstate.flush() now logs via slog.Error when cookie encoding fails instead of silently swallowing the error.
  • Admin rejects duplicate AdminAuth providersadmin.Configure() returns an error if multiple apps implement AdminAuth, instead of silently using the first one.
  • Session deferredWriter supports http.Flusher — the session middleware's response wrapper now implements Flush(), fixing SSE and streaming handlers that use direct w.(http.Flusher) type assertions.
  • Session cookies written once per requestsession.Set(), Delete(), and Save() no longer write the Set-Cookie header immediately. Instead, the session middleware defers the write until the response is sent, producing exactly one Set-Cookie header regardless of how many session mutations occur. Previously, each Set() call wrote a separate header, and only the last one survived to the browser.
  • Jobs recover from handler panics — worker goroutines now recover from panics in job handlers, converting them into failures with a stack trace. The worker stays alive and continues processing other jobs.
  • RenderError falls back to plaintext — when both error/{code} and error/default templates are missing, RenderError now writes a plaintext HTTP error instead of a blank response.
  • NavItem.LabelKey now translated in navLinksbuildNavLinks now translates LabelKey via i18n.T at render time, falling back to Label when no translation is found. Previously LabelKey was silently dropped.
  • Uploads no longer buffer entire file in memoryLocalStorage.Store now streams uploads directly to a temp file while computing the SHA-256 hash, instead of reading the entire file into []byte. Only the first 512 bytes are buffered for MIME detection. MIME validation happens before reading the body, so rejected files are discarded early.
  • Jobs admin UI re-enabledcontrib/jobs AdminRoutes and AdminNavItems were stubbed out during the Den migration. The ModelAdmin integration is now wired up again, restoring the list/detail/retry/cancel/delete views and the sidebar nav entry.
  • Auth redirects use SmartRedirectLogout, RecoveryCodesPage, and AcknowledgeRecoveryCodes now use htmx.SmartRedirect instead of http.Redirect, fixing redirect behavior when triggered via htmx.
  • Invite creation uses SmartRedirecthandleCreateInvite now uses htmx.SmartRedirect instead of http.Redirect, fixing redirect behavior when the form is submitted via htmx.

v0.12.0

08 Apr 19:38
151da5a

Choose a tag to compare

Breaking Changes

  • Den struct-tag validation is now enabled by defaultburrow.OpenDB() now enables Den's validate.WithValidation() automatically, so any document field tagged with validate:"..." (e.g., validate:"required", validate:"email", validate:"oneof=...") is enforced before every Insert and Update. Violations return an error wrapping den.ErrValidation.

    Projects that had validate: tags on Den documents where the tags previously had no effect will now see those constraints enforced. If the data layer needs to stay lax temporarily during migration, use the new burrow.OpenDBWithoutValidation() escape hatch. Remove it once the data is clean.

    burrow.TestDB() and authtest.NewDB() also enable validation so test code runs with the same constraints as production.

  • Upgraded to Den v0.6.0 — Den now runs mutating hooks (BeforeInsert, BeforeUpdate, BeforeSave) before both struct-tag validation and the Validator.Validate() interface. This lets a BeforeInsert hook populate a default value for a field that validation then requires, matching the ActiveRecord/Django/SQLAlchemy pattern. See the Den changelog for details. If you had custom validation that depended on running before the hooks (unusual), move it into BeforeInsert itself.

Added

  • burrow.OpenDBWithoutValidation(dsn) — opens a database with struct-tag validation disabled. Intended only as a migration escape hatch when moving a project from pre-v0.12.0 behavior.

v0.11.4

06 Apr 19:51
6dc14d8

Choose a tag to compare

Changed

  • Den updated to v0.5.0 — adds support for composite indexes via index_together and unique_together struct tags, and fixes Settings.Indexes application during Register()
  • Composite indexes for Job model — added index_together:claim (RunAt, Status, WorkerID) and index_together:stale (Status, LockedAt) to optimize worker claim and stale-job-rescue queries
  • Composite index for RecoveryCode model — added index_together:recovery_status (UserID, Used) to optimize unused recovery code lookups

v0.11.3

06 Apr 18:26
08fc0f8

Choose a tag to compare

Changed

  • Den updated to v0.4.2 — adds PostgreSQL version check (requires PostgreSQL 13+) and LLM documentation

v0.11.2

05 Apr 18:35
0cc74cc

Choose a tag to compare

Changed

  • License page — include full MIT license text, link to third-party licenses
  • Release workflow — release now waits for CI success before creating a GitHub release

v0.11.1

05 Apr 17:12
0e8319d

Choose a tag to compare

Fixed

  • Social card URLs — updated og:image and twitter:image meta tags to point to readthedocs.io

v0.11.0

05 Apr 16:08
1bbf8c4

Choose a tag to compare

Breaking Changes

  • Database layer replaced: Bun → Den — the entire database layer has been replaced with Den, an object-document mapper (ODM) for Go. Same API for SQLite and PostgreSQL via URL-based DSN.
  • All IDs changed from int64 to string — documents now use ULID-based string IDs via document.Base
  • Migratable interface replaced by HasDocuments — apps declare document types instead of providing SQL migration files. Den creates tables and indexes automatically from struct tags.
  • DSN requires URL scheme--database-dsn sqlite:///app.db (default) or --database-dsn postgres://host/db. Plain file paths no longer accepted.
  • --jobs-database renamed to --jobs-database-dsn — env var JOBS_DATABASE_DSN, TOML key jobs.database_dsn
  • SQL migration files removed — schema is managed automatically from document struct definitions
  • License changed from EUPL-1.2 to MIT

Added

  • PostgreSQL support — switch between SQLite and PostgreSQL with a single flag, same code, same API
  • New contrib app: humanize — i18n-aware template functions for human-friendly display of times, numbers, and file sizes, inspired by Django's django.contrib.humanize

Changed

  • Handler pattern simplified — handlers are now methods on *App instead of a separate Handlers struct
  • Bun dependency removed — replaced by github.com/oliverandrich/den v0.4.0
  • Job queue: ownership guard — workers stamp worker_id on claimed jobs; Complete/Fail verify ownership via guarded FindOneAndUpdate, preventing double processing under concurrent workers
  • Job queue: Complete/Fail take *Job — no longer reload from DB, eliminating redundant reads and stale attempt counts

Fixed

  • Admin HTMX navigation — boosted requests with a custom hx-target (e.g. #main) no longer wrap content in the layout, fixing the doubled sidebar
  • itoa template function removed — was a no-op after int64→string migration; templates use .ID directly
  • Dead code removedURLParamInt64, MustURLParamInt64, stale bun: tags in tests

v0.10.0

29 Mar 08:43
fd733a6

Choose a tag to compare

Breaking Changes

  • App interface simplified: App now only requires Name() stringRegister(cfg *AppConfig) error has been removed. All setup logic moves into Configure(cfg *AppConfig, cmd *cli.Command) error via the Configurable interface.
  • Configurable signature changed: Configure(cmd *cli.Command) errorConfigure(cfg *AppConfig, cmd *cli.Command) error — update all implementations to accept the new cfg parameter
  • PostConfigurable signature changed: PostConfigure(cmd *cli.Command) errorPostConfigure(cfg *AppConfig, cmd *cli.Command) error
  • HasFlags extracted: Flags() is no longer part of Configurable — it is now a standalone HasFlags interface
  • Registry.RegisterAll removed: Use Registry.ConfigureAll(cfg *AppConfig) instead (calls Configure(cfg, nil) on each Configurable app)

v0.9.0

28 Mar 14:03
e3eb518

Choose a tag to compare

Breaking Changes

  • HasRequestFuncMap: Changed signature from RequestFuncMap(*http.Request) to RequestFuncMap(context.Context) — update all implementations by replacing r *http.Request with ctx context.Context and r.Context() with ctx (#5)
  • TemplateExecutor: Changed signature from func(*http.Request, string, map[string]any) to func(context.Context, string, map[string]any) (#5)

Added

  • Startable lifecycle interface — counterpart to HasShutdown, called after the full boot sequence (templates built, middleware and routes registered); receives *Server so apps can access server resources like TemplateExecutor() (#11)
  • jobs: automatic TemplateExecutor injection — the jobs app now implements Startable and injects the TemplateExecutor into every job handler's context, enabling RenderFragment in background jobs without manual setup (#11)
  • Added RenderFragment() for rendering templates outside HTTP handlers (background jobs, SSE, CLI) (#5)
  • Added Server.TemplateExecutor() accessor to obtain the template executor after boot (#5)
  • Added WithRequestPath()/RequestPath() context helpers for request path propagation (#5)
  • htmx: Added smart response helpers SmartRedirect and RenderOrRedirect that handle htmx/non-htmx branching, plus Reselect header setter and StatusStopPolling constant (#9)
  • Added URLParamInt64() and MustURLParamInt64() helpers for parsing numeric URL parameters (#8)
  • auth: Added MustCurrentUser() helper that returns the authenticated user or panics — for use behind RequireAuth middleware (#7)
  • sse: Added BrokerFromRegistry() for package-level access to the SSE broker without type assertions (#6)
  • csrfHxHeaders template function — renders hx-headers='{"X-CSRF-Token":"..."}' as an HTML attribute when the csrf app is registered, or nothing when it is not. Use <body {{ csrfHxHeaders }}> for automatic CSRF protection on all htmx requests.
  • csrfToken / csrfField / csrfHxHeaders fallbacks — these template functions are always available (return empty values when the csrf app is not registered), so layouts can reference them unconditionally.

Fixed

  • Bootstrap/admin layouts: automatic CSRF token for htmxbootstrap/layout, bootstrap/nav_layout, and admin/layout now use {{ csrfHxHeaders }} on the <body> tag, so all htmx requests include the CSRF token automatically.