Metadata-Version: 2.4
Name: worq-dashboard
Version: 0.5.0
Summary: Async job queue dashboard for Python — Sidekiq Web UI for ARQ
Project-URL: Homepage, https://github.com/nitinsingh/worq
Project-URL: Documentation, https://github.com/nitinsingh/worq#readme
Project-URL: Repository, https://github.com/nitinsingh/worq
Project-URL: Issues, https://github.com/nitinsingh/worq/issues
Author: Nitin Singh
License: MIT
License-File: LICENSE
Keywords: arq,async,background-jobs,dashboard,fastapi,job-queue,redis,sidekiq
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: AsyncIO
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: pydantic>=2.0.0
Requires-Dist: redis>=5.0.0
Requires-Dist: structlog>=24.0.0
Provides-Extra: alerts
Requires-Dist: httpx>=0.27.0; extra == 'alerts'
Provides-Extra: celery
Requires-Dist: celery>=5.3.0; extra == 'celery'
Provides-Extra: dev
Requires-Dist: aiosqlite>=0.19.0; extra == 'dev'
Requires-Dist: bandit>=1.7.0; extra == 'dev'
Requires-Dist: celery>=5.3.0; extra == 'dev'
Requires-Dist: httpx>=0.27.0; extra == 'dev'
Requires-Dist: hypothesis>=6.100.0; extra == 'dev'
Requires-Dist: mypy>=1.10.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Requires-Dist: rq>=1.16.0; extra == 'dev'
Requires-Dist: ruff>=0.4.0; extra == 'dev'
Requires-Dist: testcontainers[redis]>=4.0.0; extra == 'dev'
Provides-Extra: metrics
Requires-Dist: aiosqlite>=0.19.0; extra == 'metrics'
Provides-Extra: rq
Requires-Dist: rq>=1.16.0; extra == 'rq'
Provides-Extra: ui
Requires-Dist: fastapi>=0.115.0; extra == 'ui'
Requires-Dist: jinja2>=3.1.0; extra == 'ui'
Requires-Dist: uvicorn>=0.30.0; extra == 'ui'
Requires-Dist: websockets>=13.0; extra == 'ui'
Description-Content-Type: text/markdown

# Worq

**The Sidekiq Web UI for Python job queues.**

Mount a production-grade dashboard inside your FastAPI app in two lines. Monitor jobs, retry failures, search history, track metrics — all from a single interface.

> **ARQ** — fully tested in production. **Celery** and **RQ** adapters included in beta (community testing welcome).

![Worq Dashboard](docs/images/main.png)

<details>
<summary><strong>More screenshots</strong></summary>

| Queues | Scheduled Jobs |
|--------|----------------|
| ![Queues](docs/images/queues.png) | ![Scheduled](docs/images/scheduled.png) |

| Failed Jobs | Workers & Active Jobs |
|-------------|----------------------|
| ![Failed](docs/images/failed.png) | ![Workers](docs/images/workers.png) |

| Cron Jobs |
|-----------|
| ![Cron](docs/images/cron.png) |

</details>

```python
from worq import create_dashboard
from worq.adapters.arq import ARQAdapter

app.mount("/worq", create_dashboard(ARQAdapter(redis_url="redis://localhost")))
```

No separate process. No external service. Your dashboard lives inside your app.

---

## Adapter Status

| Adapter | Status | Tested With |
|---------|--------|-------------|
| **ARQ** | **Stable** — production-tested | Real ARQ workers, multiple queues, 10K+ jobs |
| **Celery** | **Beta** — code complete, needs real-world validation | Seeded Redis data matching Celery format |
| **RQ** | **Beta** — code complete, needs real-world validation | Seeded Redis data matching RQ format |
| **Custom** | **SDK available** — `AdapterBase` class + helpers | Compliance test harness included |

Celery and RQ adapters are architecturally sound (read the correct Redis keys, handle all statuses) but haven't been tested against real Celery/RQ workers in production. **Community testing and bug reports welcome.**

---

## The Problem

Every Python job queue has the same gap: **no management UI.**

When a production job fails at 2 AM:

| Rails developer | Python developer |
|----------------|-----------------|
| Opens Sidekiq UI | SSHes into server |
| Sees the failed job | Opens Redis CLI |
| Reads the traceback | Searches for the key |
| Clicks Retry | Writes a retry script |
| Done in 30 seconds | Hopes it works |

Flower requires a separate deployment. rq-dashboard is Flask-only and stale. ARQ has nothing.

**Worq closes this gap.**

---

## What You Get

### Dashboard Overview
- **5 stat cards** — Enqueued, Active, Scheduled, Failed, Complete (live-updating)
- **Active jobs** — Currently executing jobs with function name, queue, running duration
- **Throughput metrics** — Processed count, failure rate, average duration (last 24h)
- **Recent failures** — Quick retry buttons for the last 5 failed jobs

### Per-Queue Breakdown
- Each queue listed with its own enqueued/active/scheduled/failed/complete counts
- Total row at the bottom
- See which queue is backed up at a glance

### Job Management
- **Search** by function name, status, queue, time range
- **Retry** failed jobs with one click
- **Delete** jobs from any state
- **Run Now** — execute scheduled jobs immediately
- **Bulk operations** — select up to 500 jobs, retry or delete all at once
- **Paginated** — handles 100K+ jobs without performance degradation

### Worker Health & Active Jobs
- Per-queue health status (ALIVE / NO WORKERS) from ARQ health-check keys
- Active jobs table with function, queue, and running duration
- Color-coded badges: >60s yellow, >300s red

### Cron Jobs
- View configured cron schedules with last run and result
- Trigger manually from the dashboard
- Validates function names — no orphan jobs

### Alerting
```python
from worq.alerts import AlertRule, SlackTarget

dashboard = create_dashboard(
    adapter,
    alerts=[
        AlertRule(
            name="high-failure-rate",
            condition="failure_rate > 5",
            targets=[SlackTarget(webhook_url="https://hooks.slack.com/...")],
            cooldown_minutes=30,
        ),
    ],
)
```
Safe condition DSL — no `eval()`, no `exec()`. Whitelist parser only.

### Audit Log
```python
from worq.audit import AuditLog

dashboard = create_dashboard(
    adapter,
    allow_write=True,
    audit_log=AuditLog(redis_url="redis://localhost"),
)
```
Records every retry, delete, and enqueue with user identity, timestamp, and job IDs.

### Authentication
```python
from worq.auth.basic import BasicAuth, hash_password

dashboard = create_dashboard(
    adapter,
    auth=BasicAuth("admin", hash_password("secret")),
)
```
Or bring your own: any `async (request) -> bool` callable works.

---

## Worq vs Alternatives

| Feature | Worq | Flower (Celery) | rq-dashboard |
|---------|------|-----------------|--------------|
| Mountable in FastAPI | **2 lines** | Separate process | Separate process |
| ARQ + Celery + RQ | **ARQ stable, Celery/RQ beta** | Celery only | RQ only |
| Live updates (WebSocket) | **Yes** (WS → SSE → polling) | Polling only | Polling only |
| Job search & filtering | **Full** | Limited | Limited |
| Bulk retry/delete | **Yes (500/batch)** | No | No |
| Per-queue breakdown | **Yes** | No | Basic |
| Throughput metrics | **Yes** | No | No |
| Cron management | **Yes** | No | No |
| Alerting (Slack/webhook) | **Yes** | No | No |
| Audit log | **Yes** | No | No |
| Dark mode | **Yes** | No | No |
| Mobile responsive | **Yes** | Partial | No |
| Pagination | **Yes** | Limited | No |
| JSON only (no pickle) | **Yes** | Pickle default | Pickle default |
| Security headers | **Yes** | No | No |

---

## Install

```bash
pip install worq-dashboard[ui]              # Dashboard + ARQ (production-ready)
pip install worq-dashboard[ui,celery]       # + Celery adapter (beta)
pip install worq-dashboard[ui,rq]           # + RQ adapter (beta)
```

> **Note:** The PyPI package is `worq-dashboard` but you import as `from worq import ...`

**Requires:** Python 3.12+, Redis

---

## Quick Start

### ARQ (most common)

```python
from fastapi import FastAPI
from worq import create_dashboard
from worq.adapters.arq import ARQAdapter

app = FastAPI()
adapter = ARQAdapter(redis_url="redis://localhost:6379")
app.mount("/worq", create_dashboard(adapter))
```

> **Important:** ARQ uses pickle by default. Worq requires JSON:
> ```python
> import json
> class WorkerSettings:
>     job_serializer = json.dumps
>     job_deserializer = json.loads
> ```

### Celery

```python
from worq.adapters.celery import CeleryAdapter
adapter = CeleryAdapter(redis_url="redis://localhost:6379")
app.mount("/worq", create_dashboard(adapter))
```

### RQ

```python
from worq.adapters.rq import RQAdapter
adapter = RQAdapter(redis_url="redis://localhost:6379")
app.mount("/worq", create_dashboard(adapter))
```

Open `http://localhost:8000/worq` — that's it.

---

## Multi-Queue Setup

```python
adapter = ARQAdapter(
    redis_url="redis://localhost:6379",
    queue_names=["arq:queue:critical", "arq:queue:high", "arq:queue:default", "arq:queue:low"],
    allow_write=True,
)
```

The Queues page shows each queue separately. See which one is backed up.

---

## Multi-Backend

Monitor ARQ and Celery from one dashboard:

```python
dashboard = create_dashboard(
    adapters={
        "arq": ARQAdapter(redis_url="redis://localhost"),
        "celery": CeleryAdapter(redis_url="redis://localhost:6380"),
    }
)
```

---

## Multi-Tenant

Separate staging and production:

```python
dashboard = create_dashboard(
    tenants={
        "production": ARQAdapter(redis_url="redis://prod:6379"),
        "staging": ARQAdapter(redis_url="redis://staging:6379"),
    }
)
```

---

## Production Ready

Worq is designed for production from day one:

- **Stateless** — pure Redis reads, no in-process state. Run 100 dashboard instances behind a load balancer.
- **O(1) page loads** — atomic Redis counters for complete/failed counts. No scanning at page-load time.
- **2-second cache** — in-process cache prevents redundant Redis queries from concurrent users.
- **Security headers** — X-Frame-Options, X-Content-Type-Options, Referrer-Policy on every response.
- **Read-only by default** — write actions require explicit `allow_write=True`.
- **XSS-safe** — all dynamic HTML is escaped. No `eval()`, no `exec()`, no pickle.

---

## API

Full REST API for programmatic access. Every dashboard feature is available via API.

| Endpoint | Description |
|----------|-------------|
| `GET /api/queues` | Queue stats |
| `GET /api/queues/{queue}/jobs` | Job listing with status filter |
| `GET /api/jobs/{id}` | Job detail |
| `GET /api/search` | Search with filters |
| `GET /api/workers` | Worker status |
| `GET /api/scheduled` | Scheduled jobs |
| `GET /api/failed` | Failed jobs |
| `GET /api/metrics` | Throughput metrics |
| `GET /api/cron` | Cron job list |
| `GET /api/alerts` | Alert rules |
| `GET /api/audit` | Audit log |
| `POST /api/jobs/{id}/retry` | Retry a job |
| `DELETE /api/jobs/{id}` | Delete a job |
| `POST /api/scheduled/{id}/enqueue` | Run scheduled job now |
| `POST /api/bulk/retry` | Bulk retry (up to 500) |
| `POST /api/bulk/delete` | Bulk delete (up to 500) |
| `POST /api/cron/{name}/trigger` | Trigger cron job |
| `WS /ws/stats` | Live stats stream |
| `GET /api/sse/stats` | SSE stats fallback |

Full schemas: [API Reference](docs/api-reference.md)

---

## Documentation

| Guide | What you'll learn |
|-------|-------------------|
| [Getting Started](docs/getting-started.md) | Install, configure, mount — 5 minutes to first dashboard |
| [Configuration](docs/configuration.md) | Every `create_dashboard()` parameter explained |
| [Adapters](docs/adapters.md) | ARQ, Celery, RQ deep-dive + how to write custom adapters |
| [Features](docs/features.md) | Search, bulk ops, metrics, live updates, pagination |
| [Authentication](docs/authentication.md) | Basic auth, custom auth, session integration |
| [Alerting](docs/alerting.md) | Condition DSL, webhook/Slack targets, cooldown |
| [Audit Log](docs/audit-log.md) | Write action tracking with user attribution |
| [Cron Jobs](docs/cron-jobs.md) | View schedules, trigger manually |
| [Multi-Backend & Multi-Tenant](docs/multi-backend.md) | Multiple queues, multiple environments |
| [ARQ Production Guide](docs/arq-production-guide.md) | Recommended ARQ settings for production |
| [Deployment](docs/deployment.md) | Docker, Kubernetes, nginx, scaling |
| [Troubleshooting](docs/troubleshooting.md) | Common issues and fixes |
| [API Reference](docs/api-reference.md) | All endpoints with request/response examples |
| [UI Guide](docs/ui-guide.md) | Dark mode, mobile, accessibility, HTMX |

---

## Tech Stack

| Layer | Choice | Why |
|-------|--------|-----|
| Language | Python 3.12+ | Modern typing, `mypy --strict` |
| Framework | FastAPI / Starlette | Mountable sub-app, async native |
| Data | Redis (async redis-py) | Same store ARQ/Celery/RQ use |
| Models | Pydantic v2 | Validation + serialization |
| Frontend | Jinja2 + HTMX | Server-rendered, no JS build step |
| Logging | structlog | Structured JSON |
| Security | JSON only | No pickle, no eval, no exec |

---

## Quality

| Metric | Value |
|--------|-------|
| Tests | **482** (real Redis via testcontainers) |
| Coverage | **100%** line coverage |
| Type safety | **mypy --strict**, zero errors |
| Lint | **ruff**, zero warnings |
| Accessibility | **WCAG 2.1 AA** |
| License | **MIT** |

---

## Contributing

Worq is open source under the MIT license. Issues, feature requests, and pull requests welcome.

```bash
git clone https://github.com/NitinSingh8/worq.git
cd worq
uv sync --all-extras      # Install everything
uv run pytest tests/ -v   # Run tests (needs Docker for Redis)
uv run mypy src/worq --strict
```

---

## License

MIT — same as FastAPI, ARQ, and Pydantic.
