https://github.com/pfa230/labeler
Self-hosted REST service that renders labels from declarative YAML templates (Rust/axum + Typst)
https://github.com/pfa230/labeler
homelab label-printing labels qr-code rust self-hosted typst
Last synced: 6 days ago
JSON representation
Self-hosted REST service that renders labels from declarative YAML templates (Rust/axum + Typst)
- Host: GitHub
- URL: https://github.com/pfa230/labeler
- Owner: pfa230
- License: mit
- Created: 2025-12-24T04:26:56.000Z (6 months ago)
- Default Branch: main
- Last Pushed: 2026-06-22T16:38:22.000Z (6 days ago)
- Last Synced: 2026-06-22T18:23:20.823Z (6 days ago)
- Topics: homelab, label-printing, labels, qr-code, rust, self-hosted, typst
- Language: Rust
- Size: 1.51 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 12
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
# Labeler
A REST service that renders labels from declarative YAML templates. It produces a single label as PNG
(for continuous-roll printers) or a sheet of labels as PDF (for pre-cut label sheets), by generating
[Typst](https://typst.app/) source on the fly and compiling it in-process.
## Quickstart (Docker)
```bash
docker run -p 8080:8080 ghcr.io/pfa230/labeler:edge
```
## Run
```bash
cargo run # serves on 0.0.0.0:$PORT (default 8080)
```
## Web UI
A React + TypeScript SPA in `ui/` (Vite, Tailwind). The backend serves its build at `/`.
```bash
npm --prefix ui install # once
npm --prefix ui run dev # Vite dev server (proxies /api to cargo run on :8080)
npm --prefix ui run build # build to ui/dist (then `cargo run` serves it at /)
```
In production the binary serves `ui/dist`; the Docker multi-stage build bundles the UI (see Deployment
below).
## Deployment
Run the whole thing with Docker:
```bash
docker compose up -d --build # serves on http://localhost:${HOST_PORT:-8080}
```
See [`docs/DEPLOY.md`](docs/DEPLOY.md) for configuration, persistent volumes and backups, and CUPS/IPP
printing setup.
YAML templates are loaded from `templates/` at startup; an invalid template stops the service from
starting. Starter templates: `avery5163` (US Letter sheet); the Brother continuous-tape set
`brother_12mm` / `brother_18mm` / `brother_24mm` (text only) plus `brother_18mm_qr` / `brother_24mm_qr`
(QR + text); and `homebox-qr`, a Homebox asset label whose QR links to an item, demonstrating variable
interpolation.
## Endpoints
All routes are under `/api` (the root is reserved for the web UI); unknown `/api/*` → `404 NotFound`.
- `GET /api/health` → `{ "status": "ok" }`
- `GET /api/templates` → list of template summaries
- `GET /api/templates/{id}` → detailed template schema
- `GET /api/templates/{id}/source` → raw stored template YAML
- `POST /api/render/label` → rendered PNG/PDF for a single template (preview / one-off)
- `POST /api/batch` → render/print a batch (single → ZIP or per-label jobs, sheet → paginated PDF or job)
- `GET /api/openapi.json` → OpenAPI document
- `GET /api/docs/` → Swagger UI
`scripts/render_avery_horizontal.sh` posts a sample request to a running server and writes a PDF. All
`/api` routes require authentication (ADR-0017), so export `LABELER_API_TOKEN` (create one in the UI
under Settings) before running it; the script sends it as `Authorization: Bearer $LABELER_API_TOKEN`.
## Error model
All errors are JSON with a stable schema:
```json
{
"error": {
"code": "TemplateNotFound",
"message": "No template with id 'xyz' was found",
"details": { "template": "xyz" }
}
}
```
## Development
```bash
cargo fmt
cargo clippy --all-targets --all-features
cargo test
```
[`CONTRIBUTING.md`](CONTRIBUTING.md) has the contributor workflow. The full API and template spec is in
[`docs/SPEC.md`](docs/SPEC.md); design decisions are recorded as [ADRs](docs/adr/).