Files
paperlessmanager/docs/BACKEND_API.md
T
bjoernpoettkerandClaude Opus 5.5 3f98dfd98f fix(security): Rechteprüfung deny by default, API-Keys nur auf freigegebenen Routen
- PermissionsGuard lehnt Routen ohne @RequirePermissions/@AllowAnyAuthenticated
  ab. API-Keys umgehen keine Rechteprüfung mehr, sondern gelten nur auf
  Routen mit @AllowApiKey (Paperless-Webhook, externer Upload, checksum,
  Agent-Routen des Etikettendrucks).
- Rechte für 24 bisher offene Routen ergänzt, u.a. paperless/documents
  (nur Admin), paperless/documents/:id und Kontonummern (Posteingang/
  manuell), webhook/status (Admin). routen-rechte.spec.ts prüft alle Routen.
- Etiketten-Agent: printed/error nur für abgeholte, offene Jobs (bedingtes
  Update, sonst 409) – ein gedruckter Job kann seine Nummer nicht mehr
  freigeben. Feldwerte nur aus den Eingabefeldern der Vorlage ({number}
  nicht vorgebbar) und URL-kodiert in GET-/Callback-URLs.
- PUT paperless/inbox/:id ignoriert Freigabe (CF15) und Zahlung (CF16)
  ohne die jeweilige Rolle (Funktionstrennung).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:46:43 +01:00

5.1 KiB
Raw Blame History

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):

Authorization: Bearer {token}

/events, /jobs/next, /jobs/:id/image, /jobs/:id/printed und /jobs/:id/error sind die Agent-Routen: Sie nehmen einen API-Key an (X-API-Key, ?apiKey= oder Authorization: Bearer pm_…); Benutzer mit JWT brauchen dort MANAGE_SETTINGS. POST /jobs und POST /preview erfordern VIEW_SCANNER und nehmen keinen API-Key an.

printed und error werden nur für vom Agenten abgeholte, noch offene Jobs angenommen, sonst 409 Conflict. Ein wiederholtes printed bzw. error für einen bereits so abgeschlossenen Job ist ein No-op (kein zweiter Callback).


1. Job manuell anlegen (Frontend → Backend)

POST /api/label-print-agent/jobs
Content-Type: application/json

Request Body

{
  "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

201 Created
{ "jobId": "42" }

2. Vorschau-Bild rendern (kein Job, keine Nummer-Reservierung)

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

200 OK
Content-Type: image/png

Body: binäres PNG-Bild.


3. Nächsten Druckjob abrufen (Agent-Polling)

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

204 No Content

Antwort – Job vorhanden

200 OK
Content-Type: application/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.

GET /api/label-print-agent/jobs/{jobId}/image

Antwort

200 OK
Content-Type: image/png

Body: binäres PNG-Bild.

404 Not Found  – Job oder Bild nicht vorhanden

5. Erfolgreichen Druck melden

POST /api/label-print-agent/jobs/{jobId}/printed
Content-Type: application/json

Request Body

{
  "agentId": "PC-BUERO",
  "printerName": "DYMO LabelWriter 450"
}

Alle Felder optional; Fallback jeweils "" / "unknown".

Antwort

200 OK
{ "ok": true }

Das Backend setzt den Job auf printed, speichert Zeitstempel und ruft die konfigurierte LabelPrintedUrl des Templates auf (POST).


6. Druckfehler melden

POST /api/label-print-agent/jobs/{jobId}/error
Content-Type: application/json

Request Body

{
  "agentId": "PC-BUERO",
  "printerName": "DYMO LabelWriter 450",
  "errorMessage": "Drucker nicht verfügbar."
}

Antwort

200 OK
{ "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)

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