Public-API /v1/orders: Zeichen-Warnung + Ablehnung bei zu langem Text (A6)
Additiv, ohne Aenderung der bestehenden Antwortstruktur:
- Nicht erlaubte Zeichen werden wie bisher entfernt; zusaetzlich meldet
die Antwort ein optionales "warnings":[{code:"unsupported_characters",
characters:[...]}] (Brieftext + Empfaengerfelder).
- Nur EINSEITIGE Formate (A6 hoch/quer): passt der Brieftext nicht auf
eine Karte, wird der Auftrag mit HTTP 422 abgelehnt (Backend-Umbruch-
zaehlung /api/order/pagecount, kein Scriptalizer). A4 fliesst mehrseitig
und wird nie abgelehnt. Backend nicht erreichbar => fail-open.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -328,6 +328,53 @@ const handler = (router, { services, getSchema, logger }) => {
|
|||||||
const envFormat = envMode === 'none' ? null : (fmtKey === 'a4' ? 'dinlang' : 'c6');
|
const envFormat = envMode === 'none' ? null : (fmtKey === 'a4' ? 'dinlang' : 'c6');
|
||||||
const hatDateien = files.length > 0;
|
const hatDateien = files.length > 0;
|
||||||
|
|
||||||
|
// ── Zeichen prüfen (WARNEN, nicht ablehnen) ──────────────────────────────
|
||||||
|
// Nicht erlaubte Zeichen werden wie bisher entfernt; der Aufrufer bekommt
|
||||||
|
// zusätzlich eine Warnung, welche Zeichen entfernt wurden.
|
||||||
|
const unerlaubt = new Set();
|
||||||
|
const pruefeZeichen = (s) => {
|
||||||
|
for (const ch of new Set(String(s || ''))) if (clean(ch) === '') unerlaubt.add(ch);
|
||||||
|
};
|
||||||
|
if (!istDatei && !istKuvertOnly) pruefeZeichen(letter.text);
|
||||||
|
for (const e of recipients) {
|
||||||
|
for (const k of ['salutation', 'first_name', 'last_name', 'street', 'house_no', 'zip', 'city', 'country', 'free_text']) {
|
||||||
|
pruefeZeichen(e && e[k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const warnings = [];
|
||||||
|
if (unerlaubt.size) {
|
||||||
|
warnings.push({ code: 'unsupported_characters', characters: [...unerlaubt],
|
||||||
|
message: 'Some characters are not supported and were removed. See the allowed character set in the documentation.' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Textlänge prüfen (ABLEHNEN) – NUR einseitige Formate (A6) ────────────
|
||||||
|
// A4 fließt automatisch über mehrere Seiten und kann nicht „zu lang" sein;
|
||||||
|
// A6-Karten sind einseitig → passt der Text nicht auf eine Seite, wird der
|
||||||
|
// Auftrag abgelehnt (die reine Umbruch-Zählung des Backends, kein Scriptalizer).
|
||||||
|
if (!istDatei && !istKuvertOnly && (fmtKey === 'a6_hoch' || fmtKey === 'a6_quer')) {
|
||||||
|
const layoutKey = fmtKey === 'a6_hoch' ? 'a6p' : 'a6l';
|
||||||
|
try {
|
||||||
|
const r = await fetch(`${BACKEND_URL}/api/order/pagecount`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json', 'X-API-Token': BACKEND_TOKEN },
|
||||||
|
body: JSON.stringify({ text: clean(String(letter.text || '')), format: layoutKey }),
|
||||||
|
signal: AbortSignal.timeout(8000),
|
||||||
|
});
|
||||||
|
if (r.ok) {
|
||||||
|
const d = await r.json();
|
||||||
|
const pages = Math.max(1, ...((d.counts || []).map((c) => Number(c.pages) || 1)));
|
||||||
|
if (pages > 1) {
|
||||||
|
return fehler(res, 422, `letter.text is too long for a single ${String(body.document_format)} card (needs ${pages} pages). Please shorten it.`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.warn(`[skrift-api] Längenprüfung: pagecount HTTP ${r.status}`);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Backend nicht erreichbar → Auftrag NICHT blockieren (fail-open).
|
||||||
|
logger.warn(`[skrift-api] Längenprüfung übersprungen: ${e.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const font = ['tilda', 'alva', 'ellie'].includes(body.font) ? body.font : 'tilda';
|
const font = ['tilda', 'alva', 'ellie'].includes(body.font) ? body.font : 'tilda';
|
||||||
// Versandart nur Englisch: "single" (direkt an Empfänger) / "bulk" (Sammelversand an dich).
|
// Versandart nur Englisch: "single" (direkt an Empfänger) / "bulk" (Sammelversand an dich).
|
||||||
const shippingType = String(body.shipping_type || '').toLowerCase() === 'single' ? 'einzeln' : 'sammel';
|
const shippingType = String(body.shipping_type || '').toLowerCase() === 'single' ? 'einzeln' : 'sammel';
|
||||||
@@ -409,7 +456,7 @@ const handler = (router, { services, getSchema, logger }) => {
|
|||||||
body: JSON.stringify({ orderId }),
|
body: JSON.stringify({ orderId }),
|
||||||
}).catch((e) => logger.warn(`[skrift-api] Generierung nicht gestartet: ${e.message}`));
|
}).catch((e) => logger.warn(`[skrift-api] Generierung nicht gestartet: ${e.message}`));
|
||||||
|
|
||||||
return res.status(201).json({ order_number: orderNumber, status: 'pending' });
|
return res.status(201).json({ order_number: orderNumber, status: 'pending', ...(warnings.length ? { warnings } : {}) });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(`[skrift-api] orders: ${err.stack || err.message}`);
|
logger.error(`[skrift-api] orders: ${err.stack || err.message}`);
|
||||||
return fehler(res, 500, 'Order could not be created.');
|
return fehler(res, 500, 'Order could not be created.');
|
||||||
|
|||||||
Reference in New Issue
Block a user