Align agent with updated backend API

This commit is contained in:
2026-05-09 08:55:27 +02:00
parent 8d0bef38c0
commit 00483f7236
7 changed files with 242 additions and 115 deletions
+126 -99
View File
@@ -1,40 +1,89 @@
# Backend API für LabelPrintAgent
# Backend API LabelPrintAgent
Diese Datei beschreibt die Endpunkte, die der PaperlessManager bereitstellen muss, damit der LabelPrintAgent Etiketten abholen, drucken und das Ergebnis zurückmelden kann.
Diese Datei beschreibt die Endpunkte des PaperlessManager-Backends für den LabelPrintAgent.
Der LabelPrintAgent rendert keine Layouts selbst. Das Backend liefert ein fertiges Etikettbild.
Das Backend rendert das fertige Etikettbild (SVG -> PNG via resvg-js). Der Agent ist nur lokaler Druck-Connector.
## Authentifizierung
Alle Endpunkte sollten denselben Bearer Token akzeptieren:
Alle Endpunkte erfordern einen Bearer Token (JWT oder API-Key):
```http
Authorization: Bearer {apiToken}
Authorization: Bearer {token}
```
Der Token wird im Agent lokal verschlüsselt gespeichert.
`/jobs/next`, `/jobs/:id/image`, `/jobs/:id/printed` und `/jobs/:id/error` benötigen keine spezifische Permission, nur einen gültigen Token. `POST /jobs` und `POST /preview` erfordern `VIEW_SCANNER`.
## 1. Nächsten Druckjob abrufen
## 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}
```
Der Agent ruft diesen Endpunkt alle X Sekunden auf.
### Query-Parameter
| Name | Pflicht | Beschreibung |
| Parameter | Pflicht | Beschreibung |
| --- | --- | --- |
| `agentId` | ja | Eindeutige ID des Agents, z. B. Rechnername |
| `agentId` | nein | Eindeutige Agent-ID, z. B. Rechnername; Fallback: `unknown` |
### Antwort, wenn kein Job vorhanden ist
### Antwort kein Job vorhanden
```http
204 No Content
```
### Antwort mit Bild direkt im JSON
### Antwort Job vorhanden
```http
200 OK
@@ -43,7 +92,7 @@ Content-Type: application/json
```json
{
"jobId": "12345",
"jobId": "42",
"labelImageBase64": "iVBORw0KGgoAAAANSUhEUgAA...",
"labelImageContentType": "image/png",
"labelWidthMm": 57,
@@ -51,34 +100,18 @@ Content-Type: application/json
}
```
### Antwort mit separater Bild-URL
| 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 |
```json
{
"jobId": "12345",
"labelImageUrl": "/api/label-print-agent/jobs/12345/image",
"labelImageContentType": "image/png",
"labelWidthMm": 57,
"labelHeightMm": 32
}
```
Das Backend setzt beim Ausliefern einen Lock (5-Minuten-TTL). Jobs mit abgelaufenem Lock werden erneut angeboten.
### Felder
## 4. Etikettbild separat abrufen
| Feld | Pflicht | Beschreibung |
| --- | --- | --- |
| `jobId` | ja | Eindeutige Job-ID für Rückmeldungen |
| `labelImageBase64` | bedingt | Base64-kodiertes Etikettbild |
| `labelImageUrl` | bedingt | URL zum Nachladen des Etikettbilds |
| `labelImageContentType` | empfohlen | z. B. `image/png` |
| `labelWidthMm` | ja | Etikettenbreite in mm, z. B. `57` |
| `labelHeightMm` | ja | Etikettenhöhe in mm, z. B. `32` |
`labelImageBase64` oder `labelImageUrl` muss gesetzt sein.
## 2. Etikettbild nachladen
Nur erforderlich, wenn `labelImageUrl` verwendet wird.
Alternativ zum Base64-Feld in `jobs/next`.
```http
GET /api/label-print-agent/jobs/{jobId}/image
@@ -91,11 +124,15 @@ GET /api/label-print-agent/jobs/{jobId}/image
Content-Type: image/png
```
Body: Binärdaten des fertigen Etikettbilds.
Body: binäres PNG-Bild.
Empfehlung: PNG, schwarz/weiß, passend zum Etikettenformat, z. B. 57 x 32 mm bei 300 dpi.
```http
404 Not Found
```
## 3. Erfolgreichen Druck melden
Job oder Bild nicht vorhanden.
## 5. Erfolgreichen Druck melden
```http
POST /api/label-print-agent/jobs/{jobId}/printed
@@ -111,21 +148,21 @@ Content-Type: application/json
}
```
Alle Felder optional; Fallback jeweils `""` / `unknown`.
### Antwort
```http
200 OK
```
oder:
```http
204 No Content
```json
{ "ok": true }
```
Das Backend sollte den Job erst hier endgültig als gedruckt markieren.
Das Backend setzt den Job auf `printed`, speichert Zeitstempel und ruft die konfigurierte `LabelPrintedUrl` des Templates auf (`POST`).
## 4. Fehler melden
## 6. Druckfehler melden
```http
POST /api/label-print-agent/jobs/{jobId}/error
@@ -138,7 +175,7 @@ Content-Type: application/json
{
"agentId": "PC-BUERO",
"printerName": "DYMO LabelWriter 450",
"errorMessage": "Drucker ist nicht verfügbar."
"errorMessage": "Drucker nicht verfügbar."
}
```
@@ -148,63 +185,53 @@ Content-Type: application/json
200 OK
```
oder:
```http
204 No Content
```json
{ "ok": true }
```
Das Backend entscheidet danach, ob der Job erneut angeboten wird oder auf Fehler bleibt.
Das Backend setzt den Job auf `error` und ruft die konfigurierte `LabelReleaseUrl` des Templates auf (`POST`).
## Backend-Verhalten
## Job-Lebenszyklus
Empfohlener Ablauf im Backend:
```text
createJob()
|
v
pending ---- jobs/next ---- Lock (5 Min TTL)
|
Agent druckt
|
+-------+-------+
printed error
(PrintedUrl) (ReleaseUrl)
```
1. Job erstellen und serverseitig Layout, Nummern, QR-Code und Bild erzeugen.
2. Job bleibt wartend, bis ein Agent ihn abholt.
3. `jobs/next` liefert jeweils höchstens einen Job.
4. Backend reserviert oder lockt den Job beim Ausliefern, damit zwei Agents ihn nicht parallel drucken.
5. Agent druckt lokal.
6. Agent meldet `printed` oder `error`.
7. Backend setzt den finalen Status.
## 7. Server-Sent Events neue Druckaufträge (Push)
## Empfohlene Statuscodes
```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:
```text
data: {"type":"label-job-available"}
```
Der Agent ruft daraufhin sofort `GET /jobs/next` auf. Polling bleibt als Fallback sinnvoll, z. B. alle 30 Sekunden, 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 vorhanden | `200 OK` |
| Token fehlt/ungültig | `401 Unauthorized` |
| Agent darf nicht drucken | `403 Forbidden` |
| 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` |
## Server-Sent Events optional
Später kann das Backend zusätzlich einen Event-Endpunkt anbieten:
```http
GET /api/label-print-agent/events?agentId={agentId}
Accept: text/event-stream
```
Beispiel:
```text
event: label-job-available
data: {"count":1}
```
Der Agent könnte dann bei einem Event sofort `jobs/next` aufrufen. Polling bleibt trotzdem als Fallback sinnvoll.
## Wichtige Designentscheidung
Der Agent kennt keine fachlichen Layouts mehr:
- keine `layout_key`
- keine lokalen LabelTemplates
- keine MySQL-Verbindung
- keine Nummernreservierung
- kein QR-Code-Rendering
Das Backend liefert ein fertiges Bild. Der Agent ist nur noch lokaler Windows-Druck-Connector.