Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@ All notable changes to Workbench will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.8.0] - 2026-05-30

### Added

- **`@getworkbench/core`** Self-hosted alerting: BullMQ `QueueEvents` listeners, Slack/webhook presets, dashboard **Alerts** page, and Redis-backed persistence for contact points and rules (keys under `{prefix}:workbench:alerts:*`). Alerts are enabled by default; configure destinations and rules in the dashboard to start receiving notifications.
- **`@getworkbench/core`** Slack incoming webhook validation, Block Kit payload formatting (HTTPS dashboard buttons, HTTP dev links), and in-dialog Slack setup instructions.

### Changed

- All 14 publishable adapter packages (`@getworkbench/core`, `hono`, `elysia`, `express`, `fastify`, `nestjs`, `next`, `koa`, `astro`, `nuxt`, `bun`, `h3`, `adonis`, `tanstack-start`) bump together to `0.8.0`.

## [0.7.1] - 2026-05-28

### Changed
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,11 @@ Visit `http://localhost:PORT/jobs`.
| `basePath` | `string` | Override base path detection. Required for `@getworkbench/elysia`, `@getworkbench/koa`, `@getworkbench/next`, `@getworkbench/tanstack-start`, `@getworkbench/astro`, `@getworkbench/nuxt`, and `@getworkbench/h3`. |
| `readonly` | `boolean` | Disable actions (retry, remove, promote). |
| `tags` | `string[]` | Fields from `job.data` to extract as filterable tags. |
| `alerts` | `AlertsOptions` | Self-hosted Slack/webhook alerting via BullMQ QueueEvents. |

### Alerts

Alerts are on by default. Configure Slack or webhook **contact points** and **rules** in the dashboard **Alerts** page — notifications are only sent after you set that up. The model follows Grafana-style contact points + rules (unlike Bull Board, which has no built-in alerting). See [`@getworkbench/core` README](./packages/core/README.md) for Slack setup steps.

## Packages

Expand Down
13 changes: 10 additions & 3 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ packages must bump together** because they share `@getworkbench/core` via
# 1. Bump core + every adapter in lockstep. `npm version` does not handle
# `workspace:*` deps, so edit the `"version"` field in each package.json
# directly (or use a tiny sed script).
for p in core express fastify next nestjs elysia hono; do
sed -i.bak -E "s/(\"version\": )\"[^\"]+\"/\\1\"0.3.0\"/" "packages/$p/package.json"
for p in core express fastify hono elysia next nestjs koa astro nuxt bun h3 adonis tanstack-start; do
sed -i.bak -E "s/(\"version\": )\"[^\"]+\"/\\1\"0.8.0\"/" "packages/$p/package.json"
rm "packages/$p/package.json.bak"
done

Expand All @@ -76,8 +76,15 @@ bun run typecheck
(cd packages/fastify && bun publish --access public)
(cd packages/hono && bun publish --access public)
(cd packages/elysia && bun publish --access public)
(cd packages/koa && bun publish --access public)
(cd packages/next && bun publish --access public)
(cd packages/nestjs && bun publish --access public) # depends on express + fastify, publish last
(cd packages/nestjs && bun publish --access public)
(cd packages/astro && bun publish --access public)
(cd packages/nuxt && bun publish --access public)
(cd packages/bun && bun publish --access public)
(cd packages/h3 && bun publish --access public)
(cd packages/adonis && bun publish --access public)
(cd packages/tanstack-start && bun publish --access public)
# (cd packages/cli && bun publish --access public) # only if you bumped it

# 5. Commit, tag, push.
Expand Down
60 changes: 0 additions & 60 deletions apps/web/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ import {
Twitter,
} from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import { ActionButton } from "../components/action-button";
import { CompareTable } from "../components/blog/compare-table";
import { CopyCommand } from "../components/copy-command";
import {
AdonisLogo,
Expand All @@ -43,7 +41,6 @@ import {
TestMockup,
} from "../components/mockups";
import { ThemeToggle } from "../components/theme-toggle";
import { COMPARISON_ROWS } from "../lib/blog/comparison";

/** Update once the desktop app has a tagged GitHub Release. */
const MAC_DOWNLOAD_URL =
Expand All @@ -58,7 +55,6 @@ const DOCKER_RUN_COMMAND = `docker run --rm -p 3000:3000 \\
const SPONSORS_URL = "https://github.com/sponsors/pontusab";
const TWITTER_URL = "https://x.com/pontusab";
const DOCS_URL = "https://github.com/pontusab/workbench#readme";
const COMPARISON_URL = "/blog/workbench-vs-bull-board";
const INSTALL_COMMAND = "npx @getworkbench/cli init";

/**
Expand Down Expand Up @@ -277,8 +273,6 @@ export default function Page() {

<BuiltForDevs />

<ComparisonSection />

<InstallSection />

<Footer />
Expand Down Expand Up @@ -313,12 +307,6 @@ function Nav() {
>
Blog
</a>
<Link
href={COMPARISON_URL}
className="hidden transition hover:text-[color:var(--color-foreground)] md:inline"
>
vs Bull Board
</Link>
<a
href={DOCS_URL}
target="_blank"
Expand Down Expand Up @@ -635,48 +623,6 @@ function BuiltForDevs() {
);
}

/* -------------------------------------------------------------------------- */
/* bull-board comparison */
/* -------------------------------------------------------------------------- */

function ComparisonSection() {
return (
<section
id="compare"
className="scroll-mt-16 border-t border-[color:var(--color-border)]/60 px-6 py-20 md:px-10 md:py-28"
>
<div className="mx-auto max-w-6xl">
<div className="max-w-2xl">
<div className="font-mono text-[11px] uppercase tracking-[0.2em] text-[color:var(--color-muted-foreground)]">
vs Bull Board
</div>
<h2 className="mt-3 text-balance text-3xl font-semibold tracking-tight md:text-4xl">
The modern bull-board alternative for BullMQ.
</h2>
<p className="mt-4 text-balance text-base leading-relaxed text-[color:var(--color-muted-foreground)]">
Bull Board is the dashboard everyone already knows. Workbench covers
the same Redis queues with thirteen first-party adapters, FlowProducer
DAGs, error triage, and a keyboard-driven UI — plus a native macOS app
and standalone Docker image.
</p>
<p className="mt-4">
<Link
href={COMPARISON_URL}
className="text-sm font-medium text-[color:var(--color-foreground)] underline decoration-[color:var(--color-border)] underline-offset-4 transition hover:decoration-[color:var(--color-foreground)]"
>
Read the full comparison →
</Link>
</p>
</div>

<div className="mt-10">
<CompareTable rows={COMPARISON_ROWS} compact />
</div>
</div>
</section>
);
}

/* -------------------------------------------------------------------------- */
/* Install / final CTA */
/* -------------------------------------------------------------------------- */
Expand Down Expand Up @@ -803,12 +749,6 @@ function Footer() {
>
Docs
</a>
<Link
href={COMPARISON_URL}
className="transition hover:text-[color:var(--color-foreground)]"
>
vs Bull Board
</Link>
<a
href={SPONSORS_URL}
target="_blank"
Expand Down
7 changes: 0 additions & 7 deletions apps/web/src/components/blog/blog-chrome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ interface BlogChromeProps {

const GITHUB_URL = "https://github.com/pontusab/workbench";
const SPONSORS_URL = "https://github.com/sponsors/pontusab";
const COMPARISON_URL = "/blog/workbench-vs-bull-board";

/**
* Shared header + footer used by both the blog index and individual post
Expand Down Expand Up @@ -51,12 +50,6 @@ export function BlogChrome({ children }: BlogChromeProps) {
>
Blog
</Link>
<Link
href={COMPARISON_URL}
className="hidden transition hover:text-[color:var(--color-foreground)] md:inline"
>
vs Bull Board
</Link>
<a
href={GITHUB_URL}
target="_blank"
Expand Down
3 changes: 3 additions & 0 deletions examples/with-elysia/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ new Elysia()
title: "Elysia · Workbench",
basePath: "/jobs",
auth,
alerts: {
dashboardUrl: `http://localhost:${port}/jobs`,
},
}),
)
.listen(port, ({ port }) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/adonis/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@getworkbench/adonis",
"version": "0.7.0",
"version": "0.8.0",
"description": "AdonisJS adapter for Workbench — modern BullMQ dashboard with broader framework coverage than bull-board.",
"type": "module",
"main": "./dist/index.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/astro/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@getworkbench/astro",
"version": "0.7.0",
"version": "0.8.0",
"description": "Astro adapter for Workbench — modern BullMQ dashboard with broader framework coverage than bull-board.",
"type": "module",
"main": "./dist/index.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/bun/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@getworkbench/bun",
"version": "0.7.0",
"version": "0.8.0",
"description": "Bun.serve adapter for Workbench — modern BullMQ dashboard with broader framework coverage than bull-board.",
"type": "module",
"main": "./dist/index.js",
Expand Down
64 changes: 64 additions & 0 deletions packages/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,70 @@ Shared by every adapter and by `createFetchHandler` / `WorkbenchCore`:
| `basePath` | `string` | Override base path detection. Required for some adapters (Elysia, Koa, Next.js, TanStack Start, Astro, Nuxt, h3, Adonis). |
| `readonly` | `boolean` | Disable actions (retry, remove, promote). |
| `tags` | `string[]` | Fields from `job.data` to extract as filterable tags. |
| `alerts` | `AlertsOptions` | Self-hosted alerting via BullMQ `QueueEvents`, Slack/webhook contact points, and dashboard-managed rules. |

## Alerts

Alerts are enabled by default. Configure contact points and rules from the dashboard **Alerts** page — nothing is sent until you add destinations and matching rules.

Optional seed data and settings in code:

```ts
workbench({
queues: [emailQueue],
basePath: "/jobs",
alerts: {
dashboardUrl: "https://jobs.example.com/jobs",
contactPoints: [
{
id: "slack-prod",
name: "Slack #ops",
preset: "slack",
url: process.env.SLACK_WEBHOOK_URL!,
enabled: true,
createdAt: Date.now(),
updatedAt: Date.now(),
},
],
rules: [
{
id: "failed-jobs",
name: "Job failed",
enabled: true,
trigger: "job_failed",
severity: "warning",
contactPointIds: ["slack-prod"],
createdAt: Date.now(),
updatedAt: Date.now(),
},
],
},
});
```

Supported triggers: `job_failed`, `job_stalled`, `retries_exhausted`, `failed_backlog`, `no_workers_with_backlog`.

Dashboard-managed config is **persisted in Redis** at `{prefix}:workbench:alerts:*` using the same connection as your queues. Code-defined `contactPoints` / `rules` seed Redis once when the store is empty.

Set `alerts.persistence: "memory"` to keep config in-process only (resets on restart). Set `alerts.enabled: false` to disable alerting entirely.

### How this compares

| Tool | Alert setup |
| --- | --- |
| **Bull Board** | No built-in alerting — you wire Slack/webhooks in worker code |
| **Sidekiq** | Code hooks (`death_handlers`) in your initializer |
| **Grafana** | UI contact points + alert rules + optional routing tree |
| **Workbench** | Grafana-style **contact points** and **rules** in the dashboard; event-driven via BullMQ `QueueEvents` |

### Slack setup

1. Create a Slack app at [api.slack.com/apps](https://api.slack.com/apps)
2. Enable **Incoming Webhooks** → **Add New Webhook to Workspace** → pick a channel
3. Copy the webhook URL (`https://hooks.slack.com/services/...`)
4. In Workbench **Alerts** → **Add contact point** → preset **Slack** → paste URL → **Test**

Slack preset sends Block Kit messages with queue, job, and severity details. Set `alerts.dashboardUrl` so notifications include an **Open in Workbench** link (HTTPS links render as buttons; HTTP dev URLs use a text link). Generic webhook preset sends a stable JSON payload for automation.

## Requirements

Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@getworkbench/core",
"version": "0.7.1",
"version": "0.8.0",
"description": "Core of Workbench — QueueManager, API router, and bundled UI. Modern alternative to `@bull-board/api`.",
"type": "module",
"main": "./dist/index.js",
Expand Down
Loading
Loading