alejolcc

alejolcc

Oban Chore - A LiveView dashboard and plugin for managing Oban tasks

Oban Chore provides an Oban plugin to generate a dashboard to run Oban jobs

Motivation

In previous jobs, I often had to run Oban jobs manually for things like backfills or specific support actions. Doing this meant SSH-ing into production and manually typing commands to run the job, which carries inherent security risks.

Additionally, direct access to the Elixir node isn’t always available. While we could use the Oban dashboard, we often don’t want to give full access to this dashboard to non-technical staff. This unfortunately turns developers into a bottleneck for customer support and operations teams.

Oban Chore solves this by giving you a visual interface to handle these tasks directly from the browser, empowering non-technical users to safely run jobs.

Highlights

Zero-Boilerplate UI Generation: Define input schemas directly inside your worker, and let ObanChore generate the form components automatically.
Live Execution Streaming: Leveraging Phoenix PubSub and Telemetry, the dashboard streams logs and status updates from the process directly to the user’s browser in real-time.
Idempotency Check: Automatically detects if a job with the same arguments is already running.
Unique Execution Toggle: Manually enforce single-job execution via the dashboard UI. (you can override this from the job definition)
Validation: Full Ecto-backed validation for all chore arguments.

:rocket: Quick Example

  1. Configure Oban
    # config/config.exs                                                                                                                  
    config :my_app, Oban,                                                                                                                
      repo: MyApp.Repo,                                                                                                                  
      plugins: [                                                                                                                         
        {ObanChore.Plugin, otp_app: :my_app, pubsub_server: MyApp.PubSub}                                                                
      ],                                                                                                                                 
      queues: [default: 10]
  1. Define your Chore Worker:
defmodule MyApp.Chores.UserBackfill do                                                                                               
  use ObanChore.Worker,                                                                                                              
    name: "User Data Backfill",                                                                                                      
    description: "Backfills historical data for a specific user.",                                                                   
    fields: [                                                                                                                        
      user_id: [type: :integer, required: true, label: "Target User ID"],                                                            
      reason: [type: :string, default: "Manual correction", label: "Reason"]                                                         
    ]                                                                                                                                
                                                                                                                                     
  @impl Oban.Worker                                                                                                                  
  def perform(%Oban.Job{args: %{"user_id" => user_id, "reason" => reason}} = job) do                                                       
    ObanChore.log(job, "Starting user backfill for user #{user_id}")                                                                                 
    # Your execution logic here...                                                                                                   
    ObanChore.log(job, "Done!")                                                                                                      
    :ok                                                                                                                              
  end                                                                                                                                
                                                                                                                                     
  # Optional: Customize changeset validation                                                                                         
  @impl ObanChore.Worker                                                                                                             
  def custom_changeset(changeset) do                                                                                                 
    Ecto.Changeset.validate_number(changeset, :user_id, greater_than: 0)                                                             
  end                                                                                                                                
end                                                                                                                                  
  1. Mount the Dashboard:
# lib/my_app_web/router.ex                                                                                                           
defmodule MyAppWeb.Router do                                                                                                         
  use MyAppWeb, :router                                                                                                              
  import ObanChore.Router                                                                                                            
                                                                                                                                     
  scope "/" do                                                                                                                       
    pipe_through :browser                                                                                                            
    oban_chore_dashboard "/ops/chores"                                                                                               
  end                                                                                                                                
end                                                                                                                                  

Status & Feedback

Please note that this library is currently under active development. As this is an early release, any feedback, architecture suggestions, or ideas for new use cases are welcome!

Links

• Hex Package: oban_chore | Hex
• Documentation: ObanChore 🎭 — ObanChore v0.4.0

Most Liked

sorenone

sorenone

Oban Core Team

Currently released versions, no.
On main and because you mentioned it..is now true and very much the case. :magic_wand:

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

Dude that’s awesome. Presumably if you define a new/2 function on the worker the you can do validations and stuff on the args?

webofbits

webofbits

hey, nice work. i’m trying to understand the intended boundary here.

if I am not mistaken Oban Web has authorization/access control via Oban.Web.Resolver.

from the README/thread, it looks like Oban Chore is more about exposing a curated set of predefined workers as validated forms for support/ops users: backfills, reruns, manual syncs, etc. that makes sense as a different use case from Oban Web, which already has authorization but is still primarily a full job/queue inspection and management dashboard.

a few things i’m curious about:

1. how do you see authorization being handled per chore/worker?
2. is there an audit trail for who ran what, with which args, and why?
3. do you expect chores to be idempotent by convention, or does the library enforce enough around duplicate execution?
4. would you recommend using this alongside Oban Web, with Oban Web reserved for developers/admins and Oban Chore for support/ops?

Where Next?

Popular in Announcing Top

danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 29603 241
New
deadtrickster
I’ve just released stable versions of my Prometheus Elixir libs: Elixir client [docs]; Ecto collector [docs]; Plugs instrumenter/Export...
New
OvermindDL1
I created a new library (rather I pulled out a couple files from my big project), it manages an operating system PID file for the BEAM. ...
New
pkrawat1
Presenting Aviacommerce, open source e-commerce platform in Elixir Aviacommerce is an open source e-commerce platform in Elixir. We at...
New
aditya7iyengar
Rummage.Ecto and Rummage.Phoenix provide ways to perform Searching, Sorting and Pagination over Ecto queries and Phoenix collections. Fo...
New
achempion
Hi, I would like to tell about my initiative to further maintain and develop Waffle project which is the fork of Arc library. The progre...
New
alisinabh
Hey everyone i’ve developed a library for Jalaali calendar for elixir which supports converting Gregorian dates to Jalaali and vice vers...
New
kevinlang
Hey all, We have made an Ecto3 Adapter for SQLite3, ecto_sqlite3! We have successfully on-boarded the full suite of integration tests (...
New
wfgilman
I’ve cleaned up and open sourced three financial libraries I was using for my company. They are bindings for the APIs of these three comp...
New
trisolaran
Hi! :waving_hand: I would like to present LiveSelect, a little library that I wrote to easily add a dynamic selection input to your LV f...
198 10954 107
New

Other popular topics Top

mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement