Skrift Backend - Handwritten Document Generator
Docker-basiertes Backend für die Generierung von handschriftlichen Dokumenten (Briefe, Postkarten, Umschläge) mit SVG-Output.
Features
- Preview-System: Batch-Generierung von Vorschauen mit Caching (30 Briefe pro Batch)
- Scriptalizer Integration: Nutzt externe API für natürliche Handschrift-Variationen
- SVG-Generierung: Eigene Font-Engine für hochqualitative SVG-Ausgabe
- Multi-Format Support: A4, A6 (Hoch-/Querformat), C6, DIN Lang Umschläge
- Platzhalter-System: Automatische Ersetzung von
[[Platzhalter]]mit CSV-Export - Rate Limiting: Schutz vor API-Spam (konfigurierbar)
- Docker-Ready: Vollständig containerisiert mit docker-compose
Quick Start
1. Konfiguration
cp .env.example .env
# .env bearbeiten und Scriptalizer License Key eintragen
2. Lokal testen (ohne Docker)
npm install
npm start
Server läuft auf: http://localhost:4000
3. Mit Docker deployen
docker-compose up -d
API-Endpunkte
Health Check
GET /health
Response:
{
"status": "healthy",
"timestamp": "2026-01-15T12:00:00Z",
"uptime": 12345,
"scriptalizer": "configured",
"storage": {
"cache": true,
"output": true
}
}
Preview Batch Generierung
POST /api/preview/batch
Request Body:
{
"sessionId": "uuid-abc-123",
"batchIndex": 0,
"forceRegenerate": false,
"config": {
"font": "tilda",
"letters": [
{
"index": 0,
"format": "a4",
"text": "Hallo [[Vorname]], dein Code ist [[Gutscheincode]]...",
"placeholders": {
"Vorname": "Max",
"Nachname": "Mustermann",
"Strasse": "Hauptstr. 1",
"PLZ": "10115",
"Ort": "Berlin",
"Gutscheincode": "SAVE20"
}
}
],
"envelopes": [
{
"index": 0,
"format": "c6",
"type": "recipient",
"data": {
"Vorname": "Max",
"Nachname": "Mustermann",
"Strasse": "Hauptstr. 1",
"PLZ": "10115",
"Ort": "Berlin"
}
}
]
}
}
Response:
{
"sessionId": "uuid-abc-123",
"files": [
{
"type": "letter",
"index": 0,
"url": "/api/preview/uuid-abc-123/letter_000.svg"
},
{
"type": "envelope",
"index": 0,
"url": "/api/preview/uuid-abc-123/envelope_000.svg"
}
],
"csvUrl": "/api/preview/uuid-abc-123/platzhalter.csv",
"expiresAt": "2026-01-15T14:00:00Z"
}
Rate Limit: 2 Requests/Minute pro sessionId
Preview-Datei abrufen
GET /api/preview/:sessionId/:filename
Beispiel:
GET /api/preview/uuid-abc-123/letter_000.svg
Response: SVG-Datei (Content-Type: image/svg+xml)
Bestellung finalisieren (aus Cache)
POST /api/order/finalize
Request Body:
{
"sessionId": "uuid-abc-123",
"orderNumber": "SK-2026-01-15-001"
}
Response:
{
"orderNumber": "SK-2026-01-15-001",
"outputPath": "/app/output/SK-2026-01-15-001",
"files": {
"letters": 100,
"envelopes": 100,
"csv": "platzhalter.csv"
},
"timestamp": "2026-01-15T12:30:00Z"
}
Bestellung neu generieren (ohne Cache)
POST /api/order/generate
Request Body:
{
"orderNumber": "SK-2026-01-15-002",
"config": {
"font": "tilda",
"letters": [...],
"envelopes": [...]
}
}
Response: Gleich wie /api/order/finalize
Formate
Schriftstücke (Letters)
a4- A4 Hochformat (210 × 297 mm)a6p- A6 Hochformat (105 × 148 mm)a6l- A6 Querformat (148 × 105 mm)
Umschläge (Envelopes)
c6- C6 Umschlag (162 × 114 mm)din_lang- DIN Lang Umschlag (220 × 110 mm)
Fonts
tilda- PremiumUltra79alva- PremiumUltra23ellie- PremiumUltra39
Umschlag-Typen
Empfänger-Adresse (type: "recipient")
Adresse wird unten links positioniert (kein Sichtfenster).
{
"type": "recipient",
"data": {
"Vorname": "Max",
"Nachname": "Mustermann",
"Strasse": "Hauptstr. 1",
"PLZ": "10115",
"Ort": "Berlin"
}
}
Individueller Text (type: "custom")
Text wird mittig zentriert positioniert. Max. 150 Zeichen.
{
"type": "custom",
"data": {
"customText": "Für meine großartige Freundin Caro"
}
}
Verzeichnisstruktur
/app/
├── cache/
│ └── previews/
│ └── {sessionId}/
│ ├── letter_000.svg
│ ├── envelope_000.svg
│ ├── platzhalter.csv
│ └── metadata.json
│
├── output/
│ └── {orderNumber}/
│ ├── schriftstuecke/
│ │ ├── brief_000.svg
│ │ └── ...
│ ├── umschlaege/
│ │ ├── umschlag_000.svg
│ │ └── ...
│ ├── platzhalter.csv
│ └── order-metadata.json
│
└── fonts/
├── tilda.svg
├── alva.svg
└── ellie.svg
Umgebungsvariablen
# Node Environment
NODE_ENV=production
# Scriptalizer API
SCRIPTALIZER_LICENSE_KEY=your-key-here
SCRIPTALIZER_ERR_FREQUENCY=10
# Preview System
BATCH_SIZE=30
CACHE_LIFETIME_HOURS=2
RATE_LIMIT_PER_MINUTE=2
# Server
PORT=4000
CORS_ORIGIN=*
Deployment
Auf Server (mit Docker)
# .env Datei erstellen mit production values
docker-compose up -d
# Logs ansehen
docker-compose logs -f
# Stoppen
docker-compose down
Nginx Proxy Manager Setup
- Proxy Host erstellen
- Domain:
api.skrift.de(oder deine Domain) - Forward Hostname/IP:
localhost - Forward Port:
4000 - SSL Zertifikat über NPM erstellen
Entwicklung
Lokales Testen
npm run dev # Mit nodemon
Scriptalizer Separator Test
npm run test:separator
Logs
# Docker logs
docker-compose logs -f skrift-backend
# Lokale logs
# Output in console
Integration mit N8N
N8N kann direkt auf den /app/output/{orderNumber}/ Ordner zugreifen:
// N8N Workflow (Beispiel)
const fs = require('fs');
const orderPath = '/var/skrift-output/SK-2026-01-15-001';
// Lese alle SVGs
const letters = fs.readdirSync(`${orderPath}/schriftstuecke`);
// Sende an Plotter
for (const file of letters) {
await sendToPlotter(`${orderPath}/schriftstuecke/${file}`);
}
Fehlerbehandlung
HTTP Status Codes
200- Success400- Bad Request (z.B. ungültige Parameter)404- Not Found (z.B. Session nicht gefunden)410- Gone (z.B. Cache abgelaufen)429- Too Many Requests (Rate Limit)500- Internal Server Error503- Service Unavailable (z.B. Scriptalizer down)
Typische Fehler
Rate Limit überschritten:
{
"error": "Zu viele Vorschau-Anfragen. Bitte warten Sie.",
"retryAfter": 45,
"message": "Limit: 2 Anfragen pro Minute"
}
Scriptalizer Fehler:
{
"error": "Scriptalizer request failed: timeout"
}
Cache abgelaufen:
{
"error": "Preview-Session abgelaufen. Bitte neu generieren."
}
Limits
- Scriptalizer API: 10.000 Calls/Tag
- Batch Size: 30 Briefe pro Request
- Input Size: 48KB pro Scriptalizer Call
- Rate Limit: 2 Preview-Requests/Minute
- Cache Lifetime: 2 Stunden
Troubleshooting
Fonts nicht gefunden
# Fonts kopieren
cp /path/to/fonts/*.svg ./fonts/
Scriptalizer API Fehler
# License Key prüfen
cat .env | grep SCRIPTALIZER_LICENSE_KEY
# Test-Script ausführen
npm run test:separator
Permissions Fehler
# Cache/Output Ordner Permissions
chmod -R 755 cache output
Weitere Infos
- Scriptalizer API: www.scriptalizer.co.uk
- Support: Siehe Issues in Repository
Version: 1.0.0 Last Updated: 2026-01-01