Initial commit
This commit is contained in:
461
Docker Backend/README.md
Normal file
461
Docker Backend/README.md
Normal file
@@ -0,0 +1,461 @@
|
||||
# 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
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
# .env bearbeiten und Scriptalizer License Key eintragen
|
||||
```
|
||||
|
||||
### 2. Lokal testen (ohne Docker)
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
|
||||
Server läuft auf: `http://localhost:4000`
|
||||
|
||||
### 3. Mit Docker deployen
|
||||
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## API-Endpunkte
|
||||
|
||||
### Health Check
|
||||
|
||||
```
|
||||
GET /health
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"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:**
|
||||
```json
|
||||
{
|
||||
"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:**
|
||||
```json
|
||||
{
|
||||
"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:**
|
||||
```json
|
||||
{
|
||||
"sessionId": "uuid-abc-123",
|
||||
"orderNumber": "SK-2026-01-15-001"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"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:**
|
||||
```json
|
||||
{
|
||||
"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` - PremiumUltra79
|
||||
- `alva` - PremiumUltra23
|
||||
- `ellie` - PremiumUltra39
|
||||
|
||||
---
|
||||
|
||||
## Umschlag-Typen
|
||||
|
||||
### Empfänger-Adresse (type: "recipient")
|
||||
Adresse wird **unten links** positioniert (kein Sichtfenster).
|
||||
|
||||
```json
|
||||
{
|
||||
"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.
|
||||
|
||||
```json
|
||||
{
|
||||
"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
|
||||
|
||||
```bash
|
||||
# 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)
|
||||
|
||||
```bash
|
||||
# .env Datei erstellen mit production values
|
||||
docker-compose up -d
|
||||
|
||||
# Logs ansehen
|
||||
docker-compose logs -f
|
||||
|
||||
# Stoppen
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
### Nginx Proxy Manager Setup
|
||||
|
||||
1. Proxy Host erstellen
|
||||
2. Domain: `api.skrift.de` (oder deine Domain)
|
||||
3. Forward Hostname/IP: `localhost`
|
||||
4. Forward Port: `4000`
|
||||
5. SSL Zertifikat über NPM erstellen
|
||||
|
||||
---
|
||||
|
||||
## Entwicklung
|
||||
|
||||
### Lokales Testen
|
||||
|
||||
```bash
|
||||
npm run dev # Mit nodemon
|
||||
```
|
||||
|
||||
### Scriptalizer Separator Test
|
||||
|
||||
```bash
|
||||
npm run test:separator
|
||||
```
|
||||
|
||||
### Logs
|
||||
|
||||
```bash
|
||||
# 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:
|
||||
|
||||
```javascript
|
||||
// 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` - Success
|
||||
- `400` - 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 Error
|
||||
- `503` - Service Unavailable (z.B. Scriptalizer down)
|
||||
|
||||
### Typische Fehler
|
||||
|
||||
**Rate Limit überschritten:**
|
||||
```json
|
||||
{
|
||||
"error": "Zu viele Vorschau-Anfragen. Bitte warten Sie.",
|
||||
"retryAfter": 45,
|
||||
"message": "Limit: 2 Anfragen pro Minute"
|
||||
}
|
||||
```
|
||||
|
||||
**Scriptalizer Fehler:**
|
||||
```json
|
||||
{
|
||||
"error": "Scriptalizer request failed: timeout"
|
||||
}
|
||||
```
|
||||
|
||||
**Cache abgelaufen:**
|
||||
```json
|
||||
{
|
||||
"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
|
||||
|
||||
```bash
|
||||
# Fonts kopieren
|
||||
cp /path/to/fonts/*.svg ./fonts/
|
||||
```
|
||||
|
||||
### Scriptalizer API Fehler
|
||||
|
||||
```bash
|
||||
# License Key prüfen
|
||||
cat .env | grep SCRIPTALIZER_LICENSE_KEY
|
||||
|
||||
# Test-Script ausführen
|
||||
npm run test:separator
|
||||
```
|
||||
|
||||
### Permissions Fehler
|
||||
|
||||
```bash
|
||||
# Cache/Output Ordner Permissions
|
||||
chmod -R 755 cache output
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Weitere Infos
|
||||
|
||||
- **Scriptalizer API**: [www.scriptalizer.co.uk](https://www.scriptalizer.co.uk)
|
||||
- **Support**: Siehe Issues in Repository
|
||||
|
||||
---
|
||||
|
||||
**Version**: 1.0.0
|
||||
**Last Updated**: 2026-01-01
|
||||
Reference in New Issue
Block a user