Alle 30 Artikel, WF2 veroeffentlicht direkt und schuetzt veroeffentlichte Beitraege

This commit is contained in:
2026-09-22 18:33:54 +02:00
parent 0c0fbe6208
commit 05d8488e4b
5 changed files with 275 additions and 98 deletions

View File

@@ -71,6 +71,10 @@ KONF = node("Konfiguration", "n8n-nodes-base.set", 3.4, [-240, 190], {
{"id": nid(), "name": "wp_base", "value": "https://skrift.de", "type": "string"},
{"id": nid(), "name": "score_ziel", "value": "={{ $json.body && $json.body.score_ziel ? Number($json.body.score_ziel) : 65 }}", "type": "number"},
{"id": nid(), "name": "pause_zwischen_artikeln", "value": 5, "type": "number"},
# Zielstatus fuer neue Beitraege und fuer Entwuerfe: publish (Vorgabe seit 22.09.2026) oder draft
{"id": nid(), "name": "ziel_status", "value": "={{ $json.body && ['draft','publish'].includes($json.body.status) ? $json.body.status : 'publish' }}", "type": "string"},
# Veroeffentlichte Beitraege werden nie angefasst, ausser ausdruecklich per Webhook erlaubt
{"id": nid(), "name": "veroeffentlichte_ueberschreiben", "value": "={{ !!($json.body && $json.body.veroeffentlichte_ueberschreiben === true) }}", "type": "boolean"},
{"id": nid(), "name": "elementor_ausgabe", "value": False, "type": "boolean"},
{"id": nid(), "name": "bild_aktiv", "value": True, "type": "boolean"},
{"id": nid(), "name": "bild_modell", "value": "gpt-image-2", "type": "string"},
@@ -383,7 +387,30 @@ KAT_SUCHE = http("Kategorie suchen", [2660, 240], "GET",
"={{ $('Konfiguration').first().json.wp_base }}/wp-json/wp/v2/categories?search={{ encodeURIComponent($('Score auswerten').first().json.artikel.kategorie) }}&per_page=100",
CRED_WP, auth_typ="httpBasicAuth", error_out=False, retry=True,
extra={"options": {"response": {"response": {"neverError": True, "fullResponse": True}}, "timeout": 60000}})
link(DUP, KAT_SUCHE)
# Schutz: Ein bereits veroeffentlichter Beitrag wird weder ueberschrieben noch auf Entwurf gesetzt.
SCHUTZ = code("Veroeffentlicht pruefen", [2440, 440], r"""
const s = $('Score auswerten').first().json;
const dup = $input.first().json;
const K = (r) => (r && r.body !== undefined) ? r.body : ((r || {}).data);
const kd = K(dup);
const treffer = (dup.statusCode === 200 && Array.isArray(kd)) ? kd.find(p => p.slug === s.slug) : null;
const konf = $('Konfiguration').first().json;
const geschuetzt = !!treffer && treffer.status !== 'draft' && konf.veroeffentlichte_ueberschreiben !== true;
return [{ json: { slug: s.slug, score: s.score, geschuetzt,
post_id: treffer ? treffer.id : null, alter_status: treffer ? treffer.status : null } }];
""")
link(DUP, SCHUTZ)
SCHUTZ_IF = iff("Schon veroeffentlicht?", [2660, 440], "={{ $json.geschuetzt }}", "boolean", "true")
link(SCHUTZ, SCHUTZ_IF)
SCHUTZ_NOTIZ = code("Uebersprungen merken", [2880, 540], """
const v = $input.first().json;
return [{ json: { slug: v.slug, ergebnis: 'uebersprungen', score: v.score, post_id: v.post_id,
status: v.alter_status,
meldung: 'Beitrag ' + v.post_id + ' ist bereits ' + v.alter_status + ' und bleibt unveraendert.' } }];
""")
link(SCHUTZ_IF, SCHUTZ_NOTIZ, out=0)
link(SCHUTZ_IF, KAT_SUCHE, out=1)
KAT_PRUEF = code("Kategorie pruefen", [2880, 240], """
const s = $('Score auswerten').first().json;
@@ -429,6 +456,7 @@ WP_VOR = code("WordPress-Body bauen", [3760, 240], r"""
const s = $input.first().json;
const dup = $('Beitrag vorhanden?').first().json;
const a = s.artikel;
const zielStatus = $('Konfiguration').first().json.ziel_status === 'draft' ? 'draft' : 'publish';
// Duplikatspruefung: exakter Slug-Treffer entscheidet ueber anlegen oder aktualisieren.
// n8n liefert den Koerper bei fullResponse je nach Version als body oder data.
@@ -522,7 +550,7 @@ const body = {
title: a.titel,
slug: s.slug,
content: a.content_html,
status: 'draft', // harte Vorgabe: nie automatisch veroeffentlichen
status: zielStatus, // publish seit 22.09.2026, per Webhook {"status":"draft"} umschaltbar
categories: [s.kategorie_id],
meta,
};
@@ -536,6 +564,7 @@ return [{ json: {
wp_url: treffer
? `${$('Konfiguration').first().json.wp_base}/wp-json/wp/v2/posts/${treffer.id}`
: `${$('Konfiguration').first().json.wp_base}/wp-json/wp/v2/posts`,
ziel_status: zielStatus,
wp_body: JSON.stringify(body),
}}];
""")
@@ -555,9 +584,9 @@ if (!p.id) {
throw new Error('WordPress lieferte keine Beitrags-ID zurueck. Antwort: '
+ JSON.stringify(p).slice(0, 400));
}
if (p.status !== 'draft') {
if (p.status !== v.ziel_status) {
throw new Error('Beitrag ' + p.id + ' hat Status "' + p.status
+ '" statt "draft". Der Workflow darf nie veroeffentlichen. Bitte pruefen.');
+ '" statt "' + v.ziel_status + '". Bitte Rechte des Benutzers pruefen.');
}
// Gegenprobe: sind die Meta-Felder wirklich angekommen?
@@ -612,7 +641,7 @@ link(CACHE, CACHE_NOTIZ)
PAUSE = node("Pause Rate Limit", "n8n-nodes-base.wait", 1.1, [4860, 420],
{"amount": "={{ $('Konfiguration').first().json.pause_zwischen_artikeln }}", "unit": "seconds"},
webhookId=str(uuid.uuid4()))
for q in [NACH_OK, NACH_FEHLER, FEHLER_ART, FEHLER_EVAL]:
for q in [NACH_OK, NACH_FEHLER, FEHLER_ART, FEHLER_EVAL, SCHUTZ_NOTIZ]:
link(q, PAUSE)
link(PAUSE, LOOP)
@@ -750,6 +779,7 @@ const alle = $input.all().map(i => i.json);
const importiert = alle.filter(r => r.ergebnis === 'importiert');
const nachbessern = alle.filter(r => r.ergebnis === 'nachbessern');
const fehler = alle.filter(r => r.ergebnis === 'fehler');
const uebersprungen = alle.filter(r => r.ergebnis === 'uebersprungen');
const zeilen = importiert.map(r => ({
slug: r.slug, score: r.score, datum: new Date().toISOString(),
@@ -757,11 +787,11 @@ const zeilen = importiert.map(r => ({
aktion: r.aktion, kategorie: r.kategorie,
}));
return [{ json: { alle, importiert, nachbessern, fehler,
return [{ json: { alle, importiert, nachbessern, fehler, uebersprungen,
neue_zeilen: zeilen,
anzahl_importiert: importiert.length,
anzahl_nachbessern: nachbessern.length,
anzahl_fehler: fehler.length }}];
anzahl_fehler: fehler.length, anzahl_uebersprungen: uebersprungen.length }}];
""")
link(LOOP, SAMMELN, out=0)
@@ -820,13 +850,13 @@ link(ST_SCHREIB, ST_FEHLER, out=1)
MAIL = code("Meldung formulieren", [2000, 80], r"""
const s = $('Ergebnisse sammeln').first().json;
const z = [];
z.push(`Importiert: ${s.anzahl_importiert} | Nachbessern: ${s.anzahl_nachbessern} | Fehler: ${s.anzahl_fehler}`);
z.push(`Importiert: ${s.anzahl_importiert} | Unveraendert (veroeffentlicht): ${s.anzahl_uebersprungen} | Nachbessern: ${s.anzahl_nachbessern} | Fehler: ${s.anzahl_fehler}`);
z.push('');
if (s.importiert.length) {
z.push('Als Entwurf in WordPress:');
z.push('In WordPress (Status je Beitrag):');
s.importiert.forEach(r => {
z.push(` ${r.slug} (Score ${r.score}, ${r.aktion})`);
z.push(` ${r.slug} (Score ${r.score}, ${r.aktion}, ${r.status})`);
z.push(` Vorschau: ${r.vorschau_url}`);
z.push(` Bearbeiten: ${r.bearbeiten_url}`);
if (r.elementor) z.push(` Layout: ${r.elementor}`);
@@ -867,10 +897,11 @@ if (s.fehler.length) {
s.fehler.forEach(r => z.push(` ${r.slug || '?'} [${r.stufe}]: ${r.meldung}`));
z.push('');
}
z.push('Kein Beitrag wurde veroeffentlicht. Alle Importe stehen auf Entwurf.');
if (s.uebersprungen.length) { z.push('Bereits veroeffentlicht, nicht angefasst:'); s.uebersprungen.forEach(r => z.push(` ${r.slug} (Beitrag ${r.post_id})`)); z.push(''); }
z.push('Veroeffentlichte Beitraege werden nie ueberschrieben oder auf Entwurf gesetzt.');
return [{ json: {
betreff: `Skrift Ratgeber: ${s.anzahl_importiert} Entwurf/Entwuerfe`
betreff: `Skrift Ratgeber: ${s.anzahl_importiert} importiert`
+ (s.anzahl_nachbessern ? `, ${s.anzahl_nachbessern} zum Nachbessern` : '')
+ (s.anzahl_fehler ? `, ${s.anzahl_fehler} Fehler` : ''),
text: z.join('\n') }}];