Oban Web

repository·main·Indexed 41 days ago

https://github.com/oban-bg/oban_web

A real-time, distributed dashboard for monitoring and managing Oban background jobs. It can be embedded into Phoenix applications via LiveView or deployed as a standalone Docker container. Includes a comprehensive design system for high-density data display, a specific state color palette for job monitoring, and support for Python development workers.

Tokens
16.9K
Snippets
65
Records
88
Agent score
49%

What's inside Oban Web

  1. What is Oban Web?

    main

    Oban Web is a realtime dashboard for the Oban job orchestration framework. It is designed to be hosted directly inside your Phoenix application, providing a distributed and lightweight view of background job activity.

    Key capabilities include:

    • Inspecting Jobs: Quickly find and investigate failed or retryable jobs, including their arguments and error history.
    • Queue Management: Monitor throughput and queue health, and perform actions like scaling, pausing, resuming, or stopping queues.
    • Cron Monitoring: Visualize and manage scheduled cron jobs.
    • Workflow Visualization: For users with Oban Pro, the dashboard provides a dedicated Workflows page to visualize Pro-specific workflows.

    Because it is powered by Oban Met and Phoenix LiveView, the dashboard provides fully realtime updates without requiring external services or agents.

  2. Configure Typography and the Tabular Rule

    main

    Oban Web uses a single typeface for hierarchy, relying on weight, size, and case rather than multiple font families.

    Font Families:

    • Body/Headlines/Labels: Inter var, sans-serif (self-hosted variable woff2).
    • Mono: Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace (used for machine-truth content like job args, JSON, and identifiers).

    Hierarchy:

    • Headline: 600 weight, 1rem.
    • Body: 400–500 weight, 0.875rem.
    • Label: 500 weight, 0.75rem, uppercase, 0.05em letter spacing (used for table headers and sidebar sections).
    • Mono: 400 weight, 0.75rem (used for inline metadata).

    The Tabular Rule: To prevent layout jitter when live numbers (counts, timestamps, durations) update, all numerals must use the .tabular utility:

    font-variant-numeric: slashed-zero tabular-nums;
  3. Configure access levels in Oban Web

    main

    The resolve_access/1 callback in your Oban.Web.Resolver can return one of four access levels:

    • Full Access (:all): Grants permission for all dashboard operations.
    • Read Only (:read_only): Allows viewing jobs, queues, and metrics, but prevents any modifications (e.g., pausing queues or canceling jobs).
    • Fine-Grained Control (Keyword List): Enables specific actions while disabling others. Use a keyword list where keys are the action name and the value is true.
    • Forbidden Access ({:forbidden, path}): Denies access entirely and redirects the user to the specified path. The user will see a flash message indicating they lack permission.
    # Full Access
    def resolve_access(%{admin?: true}), do: :all
    
    # Read Only
    def resolve_access(_user), do: :read_only
    
    # Fine-Grained Control
    def resolve_access(user) do
      if user.can_manage_jobs? do
        [cancel_jobs: true, delete_jobs: true, retry_jobs: true]
      else
        :read_only
      end
    end
    
    # Forbidden Access
    def resolve_access(nil), do: {:forbidden, "/login"}
  4. Apply the 400 Rule for State Colors

    main

    To maintain visual consistency and accessibility, follow the 400 Rule when using state colors:

    1. State Weight: State colors must always appear at the 400 level for the hue itself (e.g., border-{hue}-400, bg-{hue}-400/10, or chart/sparkline bars).
    2. Text Contrast: For text, shift to the 600 level in light mode and back to 400 in dark mode.
      • Exception: Yellow, cyan, and emerald text require 700 in light mode to meet 4.5:1 contrast requirements.
    3. Stroke Contrast: 2px chart line strokes should be 600 in light mode (yellow to 700) to clear 3:1 against white, and return to 400 in dark mode.
  5. Implement the Search Bar Signature

    main

    The search bar is a recessed well using shadow-inner and an inset ring. It contains:

    • A magnifying-glass glyph that transitions to a violet-filled spinner during loading.
    • Inline filter chips (removable tokens).
    • A borderless, transparent input field.
    • A popover panel for suggestions containing name, description, and example rows.
  6. Understand Layout and Elevation

    main

    The layout is a full-viewport control room designed for high density.

    Layout Structure:

    • Main Container: Uses p-4 (16px padding) with a flex column containing a header, content, and footer.
    • Two-Column Pages: Consists of a resizable sidebar (default 320px, width persisted to localStorage via --sidebar-width) and a flex-grow content panel.
    • Density: Rows are compact (~10px vertical padding) and separated by hairline dividers (divide-y) rather than boxes.

    Elevation & Depth: Depth is communicated through structural shadows and tonal stepping rather than decoration:

    • Panel: Uses shadow-lg on rounded-md surfaces (e.g., jobs table, detail views).
    • Popover: Uses shadow-lg + ring-1 ring-black/5 (e.g., dropdowns, toasts).
    • Overlay hint: Uses shadow-md (e.g., tooltips, legends).
    • Well: Uses shadow-inner + ring-1 ring-inset (e.g., search input).
  7. How Oban Web metrics work

    main

    Oban Web's real-time updates, counts, and charts are powered by the Oban.Met package. Met provides monitoring and introspection via an automatically managed supervisor. It handles:

    • Telemetry-powered metric tracking and aggregation with compaction.
    • Periodic queue checking and reporting (replacing the Gossip plugin).
    • Periodic counting and reporting with backoff (replacing the Stats plugin).
    • Leader-backed distributed metric sharing with handoff between nodes.

    Note: A running Met instance is required for the Oban Web dashboard to load and function.

  8. Configure Sidebar Filter Rows (Exclusive vs Additive)

    main

    The sidebar uses two distinct grammars for filtering depending on the semantic meaning of the row:

    Exclusive Rows (e.g., Job States)

    Used when only one state can be active at a time. These are rendered as links.

    • Active state: Lifts onto a white (light mode) or gray-800 (dark mode) fill with rounded-md and bolder text.
    • Hover state: Previews the active fill using gray-100.

    Additive Rows (e.g., Nodes, Queues)

    Used when multiple items can be selected. These are rendered as toggle buttons (aria-pressed).

    • Active state: A 4px transparent left border snaps to violet-500 and the text becomes bold.
    • Hover state: Previews the active border using violet-400.

    Common Elements:

    • Gutter Glyph: A state dot (dims to gray at zero), a node icon, or a queue-details link.
    • Counts: Right-aligned in .tabular gray text. Use a visually hidden column label for accessibility (e.g., "limit 30, executing 12").
  9. Understand the Oban Web Design System

    main

    Oban Web follows a 'Control Room' design philosophy: a calm, neutral gray architecture where color is strictly reserved for job states and intent. The interface is designed for high-density, real-time data display, prioritizing legibility and luminosity.

    Key Principles:

    • Neutral Architecture: The canvas and panels use composed grays to ensure that the eight luminous job-state colors are the primary signals.
    • Luminous Signals: Job states use vibrant 400-level hues that stand out against the neutral background.
    • Quiet-until-touched: Controls remain as quiet gray glyphs and reveal their full intent (color/labels) only on hover.
    • Full Parity: Every surface supports complete light and dark mode parity via the .dark class.
    • Desktop-First: The tool is optimized for high-density desktop use; mobile/tablet are not primary optimization targets.
  10. Apply the Series Palette Rule for Charts

    main

    When grouping charts by criteria other than job state (e.g., queue, node, worker), use the Series Palette Rule to prevent confusion with job states:

    1. Distinct Hues: Use hues that are not in the state palette (e.g., orange, teal, fuchsia, lime, sky, pink, amber, red). These are retrieved via Oban.Web.Colors.series_colors/1.
    2. Weight Differentiation: Series must use a different weight than the state palette. Use 600 in light mode and 300 in dark mode (never 400). This ensures that even neighboring hues (like sky vs. cyan) are distinguishable by weight.
    3. Stability: Each label hashes to a stable hue so colors persist across refreshes. Series are stacked and listed alphabetically.
    4. Overflow: Anything beyond the top seven series is grouped into a gray other series.
  11. How access control works in Oban Web

    main

    Oban Web manages access through the Oban.Web.Resolver behaviour. Access control is a two-step process triggered when the dashboard mounts:

    1. Extract User: The c:Oban.Web.Resolver.resolve_user/1 callback is called to extract the current user from the Plug.Conn. This is optional if you do not need user-specific permissions.
    2. Resolve Access: The c:Oban.Web.Resolver.resolve_access/1 callback determines the user's permissions (e.g., :all, :read_only, or specific actions) based on the user object returned in step 1.

    To implement this, create a module that implements the Oban.Web.Resolver behaviour and pass it to oban_dashboard/2 in your router.

    defmodule MyApp.Resolver do
      @behaviour Oban.Web.Resolver
    
      @impl true
      def resolve_user(conn) do
        conn.assigns[:current_user]
      end
    
      @impl true
      def resolve_access(user) do
        case user do
          %{role: :admin} -> :all
          %{role: :viewer} -> :read_only
          _ -> {:forbidden, "/"}
        end
      end
    end
    
    # In your router
    scope "/" do
      pipe_through :browser
    
      oban_dashboard "/oban", resolver: MyApp.Resolver
    end