249 lines
4.7 KiB
Markdown
249 lines
4.7 KiB
Markdown
# Backend API – LabelPrintAgent
|
||
|
||
Diese Datei beschreibt die Endpunkte des PaperlessManager-Backends für den LabelPrintAgent.
|
||
|
||
Das Backend rendert das fertige Etikettbild (SVG → PNG via resvg-js). Der Agent ist nur lokaler Druck-Connector.
|
||
|
||
## Authentifizierung
|
||
|
||
Alle Endpunkte erfordern einen Bearer Token (JWT oder API-Key):
|
||
|
||
```http
|
||
Authorization: Bearer {token}
|
||
```
|
||
|
||
`/jobs/next`, `/jobs/:id/image`, `/jobs/:id/printed` und `/jobs/:id/error` benötigen **keine** spezifische Permission (nur gültigen Token). `POST /jobs` und `POST /preview` erfordern `VIEW_SCANNER`.
|
||
|
||
---
|
||
|
||
## 1. Job manuell anlegen (Frontend → Backend)
|
||
|
||
```http
|
||
POST /api/label-print-agent/jobs
|
||
Content-Type: application/json
|
||
```
|
||
|
||
### Request Body
|
||
|
||
```json
|
||
{
|
||
"templateId": 3,
|
||
"fieldValues": {
|
||
"datum": "2026-05-09"
|
||
}
|
||
}
|
||
```
|
||
|
||
| Feld | Pflicht | Beschreibung |
|
||
|---|---|---|
|
||
| `templateId` | ja | ID des BarcodeTemplates |
|
||
| `fieldValues` | nein | Feldwerte für Platzhalter; Datumsfelder im Format `YYYY-MM-DD` |
|
||
|
||
### Antwort
|
||
|
||
```http
|
||
201 Created
|
||
```
|
||
```json
|
||
{ "jobId": "42" }
|
||
```
|
||
|
||
---
|
||
|
||
## 2. Vorschau-Bild rendern (kein Job, keine Nummer-Reservierung)
|
||
|
||
```http
|
||
POST /api/label-print-agent/preview
|
||
Content-Type: application/json
|
||
```
|
||
|
||
### Request Body
|
||
|
||
Identisch mit `POST /jobs`. `{number}` wird immer als `1` gerendert — die GET-URL wird **nicht** aufgerufen.
|
||
|
||
### Antwort
|
||
|
||
```http
|
||
200 OK
|
||
Content-Type: image/png
|
||
```
|
||
|
||
Body: binäres PNG-Bild.
|
||
|
||
---
|
||
|
||
## 3. Nächsten Druckjob abrufen (Agent-Polling)
|
||
|
||
```http
|
||
GET /api/label-print-agent/jobs/next?agentId={agentId}
|
||
```
|
||
|
||
| Parameter | Pflicht | Beschreibung |
|
||
|---|---|---|
|
||
| `agentId` | nein | Eindeutige Agent-ID (z. B. Rechnername); Fallback: `"unknown"` |
|
||
|
||
### Antwort – kein Job vorhanden
|
||
|
||
```http
|
||
204 No Content
|
||
```
|
||
|
||
### Antwort – Job vorhanden
|
||
|
||
```http
|
||
200 OK
|
||
Content-Type: application/json
|
||
```
|
||
|
||
```json
|
||
{
|
||
"jobId": "42",
|
||
"labelImageBase64": "iVBORw0KGgoAAAANSUhEUgAA...",
|
||
"labelImageContentType": "image/png",
|
||
"labelWidthMm": 57,
|
||
"labelHeightMm": 32
|
||
}
|
||
```
|
||
|
||
| Feld | Beschreibung |
|
||
|---|---|
|
||
| `jobId` | Job-ID für Rückmeldungen |
|
||
| `labelImageBase64` | Base64-PNG; `null` wenn Rendering fehlgeschlagen |
|
||
| `labelImageContentType` | Immer `"image/png"` |
|
||
| `labelWidthMm` / `labelHeightMm` | Etikettenmaß in mm |
|
||
|
||
Das Backend setzt beim Ausliefern einen Lock (5-Minuten-TTL). Jobs mit abgelaufenem Lock werden erneut angeboten.
|
||
|
||
---
|
||
|
||
## 4. Etikettbild separat abrufen
|
||
|
||
Alternativ zum Base64-Feld in `jobs/next`.
|
||
|
||
```http
|
||
GET /api/label-print-agent/jobs/{jobId}/image
|
||
```
|
||
|
||
### Antwort
|
||
|
||
```http
|
||
200 OK
|
||
Content-Type: image/png
|
||
```
|
||
|
||
Body: binäres PNG-Bild.
|
||
|
||
```http
|
||
404 Not Found – Job oder Bild nicht vorhanden
|
||
```
|
||
|
||
---
|
||
|
||
## 5. Erfolgreichen Druck melden
|
||
|
||
```http
|
||
POST /api/label-print-agent/jobs/{jobId}/printed
|
||
Content-Type: application/json
|
||
```
|
||
|
||
### Request Body
|
||
|
||
```json
|
||
{
|
||
"agentId": "PC-BUERO",
|
||
"printerName": "DYMO LabelWriter 450"
|
||
}
|
||
```
|
||
|
||
Alle Felder optional; Fallback jeweils `""` / `"unknown"`.
|
||
|
||
### Antwort
|
||
|
||
```http
|
||
200 OK
|
||
```
|
||
```json
|
||
{ "ok": true }
|
||
```
|
||
|
||
Das Backend setzt den Job auf `printed`, speichert Zeitstempel und ruft die konfigurierte `LabelPrintedUrl` des Templates auf (`POST`).
|
||
|
||
---
|
||
|
||
## 6. Druckfehler melden
|
||
|
||
```http
|
||
POST /api/label-print-agent/jobs/{jobId}/error
|
||
Content-Type: application/json
|
||
```
|
||
|
||
### Request Body
|
||
|
||
```json
|
||
{
|
||
"agentId": "PC-BUERO",
|
||
"printerName": "DYMO LabelWriter 450",
|
||
"errorMessage": "Drucker nicht verfügbar."
|
||
}
|
||
```
|
||
|
||
### Antwort
|
||
|
||
```http
|
||
200 OK
|
||
```
|
||
```json
|
||
{ "ok": true }
|
||
```
|
||
|
||
Das Backend setzt den Job auf `error` und ruft die konfigurierte `LabelReleaseUrl` des Templates auf (`POST`).
|
||
|
||
---
|
||
|
||
## Job-Lebenszyklus
|
||
|
||
```
|
||
createJob()
|
||
│
|
||
▼
|
||
pending ──── jobs/next ──── Lock (5 Min TTL)
|
||
│
|
||
Agent druckt
|
||
│
|
||
┌───────┴───────┐
|
||
printed error
|
||
(PrintedUrl) (ReleaseUrl)
|
||
```
|
||
|
||
## 7. Server-Sent Events – neue Druckaufträge (Push)
|
||
|
||
```http
|
||
GET /api/label-print-agent/events
|
||
Authorization: Bearer {token}
|
||
Accept: text/event-stream
|
||
```
|
||
|
||
Der Agent verbindet sich einmalig. Sobald ein neuer Druckauftrag erstellt wird, sendet das Backend:
|
||
|
||
```
|
||
data: {"type":"label-job-available"}
|
||
```
|
||
|
||
Der Agent ruft daraufhin sofort `GET /jobs/next` auf. Polling bleibt als Fallback sinnvoll (z. B. alle 30 s), falls die SSE-Verbindung unterbrochen wurde.
|
||
|
||
Es werden keine agentId-Parameter ausgewertet – alle verbundenen Agents erhalten das Event.
|
||
|
||
---
|
||
|
||
## Statuscodes
|
||
|
||
| Situation | Status |
|
||
|---|---|
|
||
| Kein Job vorhanden | `204 No Content` |
|
||
| Job / Bild vorhanden | `200 OK` |
|
||
| Job erstellt | `201 Created` |
|
||
| Token fehlt / ungültig | `401 Unauthorized` |
|
||
| Fehlende Permission | `403 Forbidden` |
|
||
| Job-ID unbekannt | `404 Not Found` |
|
||
| Backend-Fehler | `500 Internal Server Error` |
|