forge-jobs-api 0.3.1

Axum HTTP transport for forge-jobs. Pure async handlers over the storage trait surface (shared with in-process IPC bindings) + JSON DTOs + a reference jobs-server binary.
Documentation
# forge-jobs-api

[![crates.io](https://img.shields.io/crates/v/forge-jobs-api.svg)](https://crates.io/crates/forge-jobs-api)
[![docs.rs](https://img.shields.io/docsrs/forge-jobs-api)](https://docs.rs/forge-jobs-api)
[![license](https://img.shields.io/crates/l/forge-jobs-api.svg)](https://github.com/dandush03/forge#license)

HTTP transport for [`forge-jobs`](../forge-jobs/) — Axum routes over
the same handler bodies the in-process Tauri / desktop binding would
call. Lets ops poke a deployed queue without an in-process Rust
binding and gives sidecar tooling a stable JSON contract.

## Install

```toml
[dependencies]
forge-jobs     = "0.1"
forge-jobs-api = "0.1"

# For Postgres-backed deploys:
forge-jobs     = { version = "0.1", features = ["postgres"] }
forge-jobs-api = { version = "0.1", features = ["postgres"] }
```

## Use

The crate exposes:

- `forge_jobs_api::router()` — Axum `Router` you mount under your
  service. Routes are versioned-flat (no `/v1/` prefix today; the
  internal API is still settling).
- `forge_jobs_api::handlers::*` — pure async functions over
  `&Storage`. Same bodies the Tauri plugin's IPC commands call, so
  the two transports can't drift apart. Test against an in-memory
  SQLite — no router or HTTP container needed.
- `forge_jobs_api::dto::*` — request/response shapes shared between
  HTTP and IPC consumers.
- `forge_jobs_api::Error` — handler error type. Implements
  `axum::response::IntoResponse` so each variant maps to a sensible
  HTTP status code (400 / 404 / 409 / 429 / 500).

### ⚠️ Security — the routes are unauthenticated

`router()` returns an Axum `Router` with **no auth, no rate
limiting, no body-size limit, no CORS**. Some routes mutate state
(`POST /queue/:name/backoff`). You MUST either:

1. **Bind to loopback** (`127.0.0.1`) so only same-host
   processes can hit it, or
2. **Mount behind your own middleware** that enforces auth before
   the request reaches `router()`:
   ```rust,ignore
   use axum::Router;
   let app: Router = Router::new()
       .nest("/api/queue", forge_jobs_api::router(storage))
       .layer(my_auth_middleware);
   ```

A future minor release will add an optional `auth` feature with a
pluggable token check; today, that's on you.

### Wiring it up

```rust,ignore
use std::sync::Arc;
use forge_jobs::storage::{DatabaseConfig, QueuePaths};
use forge_jobs::{HandlerRegistry, QueueRuntime};
use forge_jobs_api::router;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let paths = /* your QueuePaths impl */;
    let storage = DatabaseConfig::load(&paths)?.open_storage(&paths).await?;

    // Spawn workers (queue background). `with_queues` declares which
    // queues this worker consumes — required; read from the env in prod
    // via `forge_jobs::queues_from_env()`.
    let runtime = QueueRuntime::new(storage.clone(), HandlerRegistry::new(), Arc::new(forge_jobs::DefaultRouter))
        .with_queues(["default".to_owned()]);
    runtime.ensure_queue("default", 4).await?;
    let _handle = runtime.start().await?;

    // Mount the HTTP API. Bound to loopback because routes are
    // unauthenticated — see the Security section above before
    // changing to 0.0.0.0.
    let app = router(storage);
    let listener = tokio::net::TcpListener::bind("127.0.0.1:8080").await?;
    axum::serve(listener, app).await?;
    Ok(())
}
```

### `jobs-server` binary

The crate also ships a small reference binary:

```bash
# Default: SQLite via $JOBS_DATA_DIR / $JOBS_CONFIG_DIR env vars.
cargo run -p forge-jobs-api --bin jobs-server

# Postgres:
cargo run -p forge-jobs-api --bin jobs-server --features postgres
```

Bind address and worker count are env-configured — see
`src/bin/jobs-server.rs`.

## Routes

| Method | Path | Description |
|---|---|---|
| `GET` | `/queue/overview` | All queues' status counts + live workers + retention settings |
| `POST` | `/queue/:name/backoff` | Set per-queue exponential backoff curve |
| `GET` | `/storage/info` | Backend identifier + boot-time facts |
| `GET` | `/metrics` | Prometheus exposition (queue depth, latency percentiles) |

More routes land as the in-process IPC surface formalizes. The
Tauri-plugin's commands and these handlers share the same DTOs, so
divergence is structurally impossible.

## Status

`0.1` — tracks `forge-jobs` major version. The handler bodies are
stable; route shapes may evolve before `1.0` as we tighten the
externally-facing contract.

## License

Dual-licensed under either of

- Apache License, Version 2.0 ([LICENSE-APACHE]LICENSE-APACHE or
  <http://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT]LICENSE-MIT or
  <http://opensource.org/licenses/MIT>)

at your option. Contributions intentionally submitted for inclusion
in this crate shall be dual-licensed as above, without any additional
terms or conditions.