Preisregel im Brief, Preispruefung in Workflow 2

This commit is contained in:
2026-09-20 12:46:16 +02:00
parent 8ab9fe4be5
commit fe22f7b01d

View File

@@ -9,7 +9,7 @@
-460,
100
],
"id": "1672e3ce-2e45-48a3-bb31-dd70e8e01e66",
"id": "b6be91ee-7c33-4990-8f11-3a8a5d7378ba",
"name": "Manuell starten"
},
{
@@ -25,9 +25,9 @@
-460,
280
],
"id": "2f61cedf-09a4-4f7f-9c5d-6a85778f4206",
"id": "e8af0078-9f50-4318-9764-83c85099cfb8",
"name": "Webhook Start",
"webhookId": "6363e34d-b371-47ab-b48a-2b8fff7e3e1d"
"webhookId": "5292db0f-071e-42c6-893f-acfbd2e1d4d7"
},
{
"parameters": {
@@ -35,79 +35,79 @@
"assignments": {
"assignments": [
{
"id": "626dcb0d-2f72-4a9a-9a5d-d503f5282fb3",
"id": "8b6e2da3-da8b-40cc-9f52-33cd801a497b",
"name": "nw_base",
"value": "https://app.neuronwriter.com/neuron-api/0.5/writer",
"type": "string"
},
{
"id": "d5cd20b9-8924-45ec-8676-be4d6967d696",
"id": "3c92a320-321c-416b-a4f0-548e37a82d19",
"name": "gitea_base",
"value": "https://gitea.lucas-orth.de",
"value": "http://gitea:3000",
"type": "string"
},
{
"id": "4cf84d9a-b123-454b-997a-8a729c8e822f",
"id": "8f62200d-a0f9-4fa0-b182-becb6121a0ef",
"name": "gitea_owner",
"value": "lucas.orth",
"type": "string"
},
{
"id": "669399d0-fc3a-4732-9ca4-b54c3bb3cb99",
"id": "08a88597-c4bc-4aff-bac9-929f0e5ec617",
"name": "gitea_repo",
"value": "Skrift-Auto-Article",
"type": "string"
},
{
"id": "3fe40194-e6f0-4c2b-a2c2-09ce14e72628",
"id": "0a37c1bf-c4df-475a-bd0e-8e5799b7348c",
"name": "branch",
"value": "main",
"type": "string"
},
{
"id": "e192fbf8-a43f-425e-84fc-326e1adfd371",
"id": "66ce6e02-7b4c-4337-acc8-0497eca716b2",
"name": "artikel_ordner",
"value": "artikel",
"type": "string"
},
{
"id": "c05fc266-62af-40fa-af34-9ff78d737b7c",
"id": "f56d64e1-e73b-4aa8-b70f-a39b875bd41c",
"name": "brief_ordner",
"value": "briefe",
"type": "string"
},
{
"id": "7ba0d52a-562d-458e-8174-3668839dc51e",
"id": "7151540f-8cc2-4d3c-858d-68a92ffebda2",
"name": "nachbessern_ordner",
"value": "nachbessern",
"type": "string"
},
{
"id": "f0ed3f23-e06d-44f6-8552-a39e18f6c940",
"id": "2f1e3867-f450-4455-bb7f-4e739511a092",
"name": "status_pfad",
"value": "status-importe.json",
"type": "string"
},
{
"id": "1f58adf1-dd6d-41cf-a968-1fbd42a67d92",
"id": "26572729-abbc-4f56-8c4c-10d82053d209",
"name": "wp_base",
"value": "https://skrift.de",
"type": "string"
},
{
"id": "66ba99ca-640b-4c7f-a62a-10a156413eeb",
"id": "f79e769d-4349-4312-a771-75003e1dc06f",
"name": "score_ziel",
"value": 72,
"value": "={{ $json.body && $json.body.score_ziel ? Number($json.body.score_ziel) : 72 }}",
"type": "number"
},
{
"id": "ae433f97-9f29-45d1-b246-6c798e871818",
"id": "ab009d25-5581-4b97-9f62-2479ebbb4eb6",
"name": "pause_zwischen_artikeln",
"value": 5,
"type": "number"
},
{
"id": "99d6bffe-713d-485e-8f97-42a550d97705",
"id": "21d61ca0-8b9e-472b-9980-5f9c9c992333",
"name": "benachrichtigung_an",
"value": "privat@lucas-orth.de",
"type": "string"
@@ -122,7 +122,7 @@
-240,
190
],
"id": "24f0589f-6595-434f-887d-ea4f514fe2f2",
"id": "78ef6b35-5a81-4f5d-a6c5-8587d8f5011f",
"name": "Konfiguration"
},
{
@@ -147,7 +147,7 @@
-20,
190
],
"id": "9339e23d-346d-41ce-afd3-a54b156d45f4",
"id": "261803f2-492f-40d5-9025-dd8b4f4ed321",
"name": "Artikelordner lesen",
"credentials": {
"httpHeaderAuth": {
@@ -162,7 +162,7 @@
{
"parameters": {
"mode": "runOnceForAllItems",
"jsCode": "\nconst antwort = $input.first().json;\nif (antwort.statusCode === 404) {\n return [{ json: { leer: true, grund: 'Ordner \"artikel\" existiert noch nicht im Repo.' } }];\n}\nif (antwort.statusCode !== 200) {\n throw new Error('Artikelordner konnte nicht gelesen werden (HTTP ' + antwort.statusCode\n + '). Antwort: ' + JSON.stringify(antwort.body).slice(0, 400));\n}\nconst dateien = (antwort.body || []).filter(e =>\n e.type === 'file' && /^artikel-.+\\.json$/.test(e.name));\n\nif (!dateien.length) {\n return [{ json: { leer: true, grund: 'Keine Dateien nach Muster artikel-<slug>.json gefunden.' } }];\n}\nreturn dateien.map(d => ({ json: {\n name: d.name, pfad: d.path, sha: d.sha,\n slug: d.name.replace(/^artikel-/, '').replace(/\\.json$/, ''),\n}}));\n"
"jsCode": "\nconst antwort = $input.first().json;\n// n8n liefert den Koerper bei fullResponse je nach Version als body oder data.\nconst K = (r) => (r && r.body !== undefined) ? r.body : ((r || {}).data);\nconst ka = K(antwort);\nif (antwort.statusCode === 404) {\n return [{ json: { leer: true, grund: 'Ordner \"artikel\" existiert noch nicht im Repo.' } }];\n}\nif (antwort.statusCode !== 200) {\n throw new Error('Artikelordner konnte nicht gelesen werden (HTTP ' + antwort.statusCode\n + '). Antwort: ' + JSON.stringify(ka).slice(0, 400));\n}\nconst dateien = (ka || []).filter(e =>\n e.type === 'file' && /^artikel-.+\\.json$/.test(e.name));\n\nif (!dateien.length) {\n return [{ json: { leer: true, grund: 'Keine Dateien nach Muster artikel-<slug>.json gefunden.' } }];\n}\nreturn dateien.map(d => ({ json: {\n name: d.name, pfad: d.path, sha: d.sha,\n slug: d.name.replace(/^artikel-/, '').replace(/\\.json$/, ''),\n}}));\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
@@ -170,7 +170,7 @@
200,
190
],
"id": "09bd880f-76b6-4789-89ba-847550dfd368",
"id": "6ad02233-4cbb-48d3-be1a-0e97dcc65650",
"name": "Artikeldateien filtern"
},
{
@@ -184,7 +184,7 @@
},
"conditions": [
{
"id": "8ab83719-1d1d-45ac-baed-72435ed39667",
"id": "7f80b65e-4821-4071-a37f-3d47313c2d8f",
"leftValue": "={{ $json.leer === true }}",
"rightValue": "",
"operator": {
@@ -205,7 +205,7 @@
420,
190
],
"id": "73a94681-412a-4c21-8da2-4f57b69e1f66",
"id": "3f559f53-d0db-47a1-a093-19652fff8f5f",
"name": "Artikel vorhanden?"
},
{
@@ -216,7 +216,7 @@
640,
40
],
"id": "c3a5e351-bfc7-4071-abf1-955f6a06f5bb",
"id": "db75bced-d1c9-4656-95fb-13054ae70099",
"name": "Nichts zu tun"
},
{
@@ -232,7 +232,7 @@
640,
320
],
"id": "1ea30d47-559f-4acc-bc05-7545a71690a6",
"id": "c6453647-01d7-40cb-a987-832f64f88dcf",
"name": "Artikel nacheinander"
},
{
@@ -256,7 +256,7 @@
900,
420
],
"id": "8b6d1d9e-eaf9-411e-a440-6faedbd3e306",
"id": "99498cef-39e8-4072-ad37-935f88776a7f",
"name": "Artikeldatei laden",
"credentials": {
"httpHeaderAuth": {
@@ -280,7 +280,7 @@
900,
640
],
"id": "67a6dc51-5a69-4a7e-b2f7-4cb0f160bc7b",
"id": "ceeef477-d968-4734-a1e7-541063617fdd",
"name": "Fehler Artikeldatei",
"onError": "continueRegularOutput"
},
@@ -306,7 +306,7 @@
1120,
420
],
"id": "c03ef3a0-aa7b-49a6-8cd4-d0c61c4c7f6a",
"id": "4378f17c-fffb-4909-8a0c-329aab212806",
"name": "Brief laden",
"credentials": {
"httpHeaderAuth": {
@@ -318,7 +318,7 @@
{
"parameters": {
"mode": "runOnceForAllItems",
"jsCode": "\nconst meta = $('Artikel nacheinander').first().json;\nconst artRoh = $('Artikeldatei laden').first().json;\nconst briefAntwort = $input.first().json;\n\nlet artikel;\ntry { artikel = JSON.parse(Buffer.from(artRoh.content, 'base64').toString('utf8')); }\ncatch (e) { throw new Error('artikel-' + meta.slug + '.json ist kein gueltiges JSON: ' + e.message); }\n\nconst pflicht = ['slug', 'titel', 'query_id', 'kategorie', 'content_html'];\nconst fehlt = pflicht.filter(f => !artikel[f]);\nif (fehlt.length) {\n throw new Error('artikel-' + meta.slug + '.json fehlen Pflichtfelder: ' + fehlt.join(', ')\n + '. Erwartetes Format siehe README Abschnitt \"Vertrag Cowork -> Workflow 2\".');\n}\nif (artikel.slug !== meta.slug) {\n throw new Error('Slug im Dateinamen (' + meta.slug + ') und im JSON (' + artikel.slug + ') stimmen nicht ueberein.');\n}\n\nlet brief = null;\nif (briefAntwort.statusCode === 200 && briefAntwort.body && briefAntwort.body.content) {\n try { brief = JSON.parse(Buffer.from(briefAntwort.body.content, 'base64').toString('utf8')); }\n catch (e) { brief = null; }\n}\n\nreturn [{ json: { slug: meta.slug, artikel, brief, brief_vorhanden: !!brief } }];\n"
"jsCode": "\nconst meta = $('Artikel nacheinander').first().json;\nconst artRoh = $('Artikeldatei laden').first().json;\nconst briefAntwort = $input.first().json;\n// n8n liefert den Koerper bei fullResponse je nach Version als body oder data.\nconst K = (r) => (r && r.body !== undefined) ? r.body : ((r || {}).data);\nconst kb = K(briefAntwort);\n\nlet artikel;\ntry { artikel = JSON.parse(Buffer.from(artRoh.content, 'base64').toString('utf8')); }\ncatch (e) { throw new Error('artikel-' + meta.slug + '.json ist kein gueltiges JSON: ' + e.message); }\n\nconst pflicht = ['slug', 'titel', 'query_id', 'kategorie', 'content_html'];\nconst fehlt = pflicht.filter(f => !artikel[f]);\nif (fehlt.length) {\n throw new Error('artikel-' + meta.slug + '.json fehlen Pflichtfelder: ' + fehlt.join(', ')\n + '. Erwartetes Format siehe README Abschnitt \"Vertrag Cowork -> Workflow 2\".');\n}\nif (artikel.slug !== meta.slug) {\n throw new Error('Slug im Dateinamen (' + meta.slug + ') und im JSON (' + artikel.slug + ') stimmen nicht ueberein.');\n}\n\nlet brief = null;\nif (briefAntwort.statusCode === 200 && kb && kb.content) {\n try { brief = JSON.parse(Buffer.from(kb.content, 'base64').toString('utf8')); }\n catch (e) { brief = null; }\n}\n\n// Preise muessen als Shortcode stehen, nie ausgeschrieben.\nconst preisTreffer = [];\nconst re = /(\\d{1,3}(?:[.,]\\d{1,2})?)\\s*(?:€|EUR|Euro)/gi;\nlet m;\nwhile ((m = re.exec(String(artikel.content_html || ''))) !== null) {\n preisTreffer.push(m[0].trim());\n}\nconst shortcodes = (String(artikel.content_html || '').match(/\\[skrift_preis[^\\]]*\\]/g) || []);\n\nreturn [{ json: { slug: meta.slug, artikel, brief, brief_vorhanden: !!brief,\n preis_warnung: preisTreffer.length\n ? `${preisTreffer.length} ausgeschriebene(r) Preis(e) im Text: ${[...new Set(preisTreffer)].slice(0,5).join(', ')}. `\n + 'Bitte durch [skrift_preis key=\"...\"] ersetzen.'\n : null,\n preis_shortcodes: shortcodes.length } }];\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
@@ -326,7 +326,7 @@
1340,
420
],
"id": "bfb21a1d-46f8-4fd1-aa04-d99fa7760db1",
"id": "cd588edb-dccb-42b4-9e52-07ee612667e7",
"name": "Artikel pruefen"
},
{
@@ -356,7 +356,7 @@
1560,
420
],
"id": "496e71b7-c180-4672-a9d2-f07f41770d64",
"id": "5e57d3cd-065f-4371-ae2b-62bf0190d389",
"name": "Score ermitteln",
"credentials": {
"httpHeaderAuth": {
@@ -380,7 +380,7 @@
1560,
640
],
"id": "1d96b2cf-36d5-4297-b898-8e2228575372",
"id": "bc9cdc94-249f-43d6-bb73-3a232bdc4d3f",
"name": "Fehler Score",
"onError": "continueRegularOutput"
},
@@ -395,7 +395,7 @@
1780,
420
],
"id": "4cb5e1f7-2dce-4946-9a06-4f40599a26dd",
"id": "61d351aa-fae8-4b83-b383-e9e0eabe35ce",
"name": "Score auswerten"
},
{
@@ -409,7 +409,7 @@
},
"conditions": [
{
"id": "ab5cb32a-2031-4584-a8d7-651e68611b93",
"id": "5f9081ad-6a56-41cf-ac40-98df6e63d862",
"leftValue": "={{ $json.bestanden }}",
"rightValue": "",
"operator": {
@@ -430,7 +430,7 @@
2000,
420
],
"id": "b33eb7d4-7c48-4f5a-befe-323da4dbca8d",
"id": "e932d985-5556-452f-9315-56fbc87c9af9",
"name": "Score erreicht?"
},
{
@@ -444,7 +444,7 @@
2220,
640
],
"id": "00bcc501-e2f3-4902-ae1e-55d22c470b07",
"id": "71a32ae7-9fa4-4c5f-b01b-c7498c5fcb82",
"name": "Fehlende Begriffe ermitteln"
},
{
@@ -469,7 +469,7 @@
2440,
640
],
"id": "a192685f-1936-4bfa-944b-23f9a5eee52f",
"id": "6ab382ae-462a-4638-91ce-bb6eb581729c",
"name": "Nachbesserung vorhanden?",
"credentials": {
"httpHeaderAuth": {
@@ -481,7 +481,7 @@
{
"parameters": {
"mode": "runOnceForAllItems",
"jsCode": "\nconst n = $('Fehlende Begriffe ermitteln').first().json;\nconst p = $input.first().json;\nconst da = p.statusCode === 200 && p.body && p.body.sha;\nconst konf = $('Konfiguration').first().json;\nreturn [{ json: { ...n, methode: da ? 'PUT' : 'POST', commit_body: JSON.stringify({\n content: Buffer.from(JSON.stringify(n.bericht, null, 2), 'utf8').toString('base64'),\n message: `Nachbesserung ${n.slug}: Score ${n.score} unter Ziel`,\n branch: konf.branch, ...(da ? { sha: p.body.sha } : {}),\n})}}];\n"
"jsCode": "\nconst n = $('Fehlende Begriffe ermitteln').first().json;\nconst p = $input.first().json;\n// n8n liefert den Koerper bei fullResponse je nach Version als body oder data.\nconst K = (r) => (r && r.body !== undefined) ? r.body : ((r || {}).data);\nconst kp = K(p);\nconst da = p.statusCode === 200 && kp && kp.sha;\nconst konf = $('Konfiguration').first().json;\nreturn [{ json: { ...n, methode: da ? 'PUT' : 'POST', commit_body: JSON.stringify({\n content: Buffer.from(JSON.stringify(n.bericht, null, 2), 'utf8').toString('base64'),\n message: `Nachbesserung ${n.slug}: Score ${n.score} unter Ziel`,\n branch: konf.branch, ...(da ? { sha: kp.sha } : {}),\n})}}];\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
@@ -489,7 +489,7 @@
2660,
640
],
"id": "ab3c50cd-468d-42b6-bb76-99b0ad4dea68",
"id": "baa5802a-e510-4d0c-ad25-c41f6add1a41",
"name": "Nachbesserung vorbereiten"
},
{
@@ -519,7 +519,7 @@
2880,
640
],
"id": "c67daa49-e5a9-4651-8148-4d314360cb44",
"id": "fab0cf4b-3bfa-4999-922e-710eeeb4e3e5",
"name": "Nachbesserung committen",
"credentials": {
"httpHeaderAuth": {
@@ -543,14 +543,14 @@
2880,
860
],
"id": "66596b5c-ae26-4588-9559-6dab3042104c",
"id": "e996cf98-d502-445c-96f4-f10cd24bcb2d",
"name": "Fehler Nachbesserung",
"onError": "continueRegularOutput"
},
{
"parameters": {
"mode": "runOnceForAllItems",
"jsCode": "\nconst n = $('Fehlende Begriffe ermitteln').first().json;\nreturn [{ json: { slug: n.slug, ergebnis: 'nachbessern', score: n.score,\n score_ziel: n.bericht.score_ziel, pfad: n.pfad,\n anzahl_pflicht: n.anzahl_pflicht, anzahl_erweitert: n.anzahl_erweitert,\n wortzahl: n.bericht.wortzahl,\n meldung: `Score ${n.score} unter Ziel ${n.bericht.score_ziel}. `\n + `${n.anzahl_pflicht} Pflichtbegriffe fehlen, ${n.anzahl_erweitert} Zusatzbegriffe.` }}];\n"
"jsCode": "\nconst n = $('Fehlende Begriffe ermitteln').first().json;\nconst p = $('Artikel pruefen').first().json;\nreturn [{ json: { slug: n.slug, ergebnis: 'nachbessern', score: n.score,\n preis_warnung: p.preis_warnung || null,\n score_ziel: n.bericht.score_ziel, pfad: n.pfad,\n anzahl_pflicht: n.anzahl_pflicht, anzahl_erweitert: n.anzahl_erweitert,\n wortzahl: n.bericht.wortzahl,\n meldung: `Score ${n.score} unter Ziel ${n.bericht.score_ziel}. `\n + `${n.anzahl_pflicht} Pflichtbegriffe fehlen, ${n.anzahl_erweitert} Zusatzbegriffe.` }}];\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
@@ -558,7 +558,7 @@
3100,
640
],
"id": "54395aa6-a6d9-4d20-8fdd-2f979b1238ea",
"id": "b9804e4e-7d67-4a69-8eff-db2eef089d31",
"name": "Nachbesserung gemerkt"
},
{
@@ -588,7 +588,7 @@
2220,
240
],
"id": "9a8d7b0b-a088-4bad-85b5-1b58060dd76b",
"id": "6a9a0e6d-ac62-449e-9423-0b8cce15e63a",
"name": "Revision zu NeuronWriter",
"credentials": {
"httpHeaderAuth": {
@@ -612,7 +612,7 @@
2220,
60
],
"id": "8f65fab0-fde9-41a2-9af7-22c94311bd8c",
"id": "8164f9fa-b7a8-4054-9a2c-f858904e3c8e",
"name": "Fehler Revision",
"onError": "continueRegularOutput"
},
@@ -638,7 +638,7 @@
2440,
240
],
"id": "038f803f-0e6f-46f5-919c-2481224a8dc4",
"id": "dbc8958d-e00a-49ff-b2ca-0ba1b21f23c6",
"name": "Beitrag vorhanden?",
"credentials": {
"httpBasicAuth": {
@@ -672,7 +672,7 @@
2660,
240
],
"id": "73d6a0e6-7d2e-4a40-8115-769f2d25ed0e",
"id": "f577d6d9-6a14-4c17-8847-cd3cc5c27528",
"name": "Kategorie suchen",
"credentials": {
"httpBasicAuth": {
@@ -687,7 +687,7 @@
{
"parameters": {
"mode": "runOnceForAllItems",
"jsCode": "\nconst s = $('Score auswerten').first().json;\nconst gesucht = String(s.artikel.kategorie).trim();\nconst antwort = $input.first().json;\nconst treffer = (antwort.statusCode === 200 && Array.isArray(antwort.body))\n ? antwort.body.find(c => c.name.toLowerCase() === gesucht.toLowerCase()) : null;\nreturn [{ json: { ...s, kategorie_id: treffer ? treffer.id : null,\n kategorie_name: gesucht, kategorie_fehlt: !treffer } }];\n"
"jsCode": "\nconst s = $('Score auswerten').first().json;\nconst gesucht = String(s.artikel.kategorie).trim();\nconst antwort = $input.first().json;\n// n8n liefert den Koerper bei fullResponse je nach Version als body oder data.\nconst K = (r) => (r && r.body !== undefined) ? r.body : ((r || {}).data);\nconst ka = K(antwort);\nconst treffer = (antwort.statusCode === 200 && Array.isArray(ka))\n ? ka.find(c => c.name.toLowerCase() === gesucht.toLowerCase()) : null;\nreturn [{ json: { ...s, kategorie_id: treffer ? treffer.id : null,\n kategorie_name: gesucht, kategorie_fehlt: !treffer } }];\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
@@ -695,7 +695,7 @@
2880,
240
],
"id": "62618b8c-8712-4547-a27c-c041ae3497a1",
"id": "cf5e7318-28e2-4fef-8592-b6bc9e5569da",
"name": "Kategorie pruefen"
},
{
@@ -709,7 +709,7 @@
},
"conditions": [
{
"id": "3144ce37-9b76-4e0e-9d3e-07f4367a7f4d",
"id": "f9af79a7-5da4-4711-a15a-683d518e4256",
"leftValue": "={{ $json.kategorie_fehlt }}",
"rightValue": "",
"operator": {
@@ -730,7 +730,7 @@
3100,
240
],
"id": "89ff9876-f1c2-4497-a9d7-6b53323acc56",
"id": "e5896ca7-d4d5-4123-a98d-d9a15496187b",
"name": "Kategorie anlegen?"
},
{
@@ -760,7 +760,7 @@
3320,
140
],
"id": "d79e1c1f-3de8-4287-85ed-aa6c61a11f85",
"id": "970d521b-0500-47e2-b7da-ee40d672c324",
"name": "Kategorie anlegen",
"credentials": {
"httpBasicAuth": {
@@ -783,13 +783,13 @@
3540,
240
],
"id": "3e297808-4e25-4a32-96b8-bd85fd99195b",
"id": "db263677-f5a1-4a6a-a2ac-88667a2a1a5f",
"name": "Kategorie festlegen"
},
{
"parameters": {
"mode": "runOnceForAllItems",
"jsCode": "\nconst s = $input.first().json;\nconst dup = $('Beitrag vorhanden?').first().json;\nconst a = s.artikel;\n\n// Duplikatspruefung: exakter Slug-Treffer entscheidet ueber anlegen oder aktualisieren.\nconst treffer = (dup.statusCode === 200 && Array.isArray(dup.body))\n ? dup.body.find(p => p.slug === s.slug) : null;\n\nconst heute = new Date().toISOString().slice(0, 10);\n\nconst meta = {\n kurzantwort: a.kurzantwort || '',\n cta_ziel: a.cta_ziel || '',\n stand_datum: a.stand_datum || heute,\n faq: Array.isArray(a.faq)\n ? a.faq.filter(f => f && f.frage).map(f => ({ frage: String(f.frage), antwort: String(f.antwort || '') }))\n : [],\n _seopress_titles_title: a.seo_titel || a.titel,\n _seopress_titles_desc: a.seo_beschreibung || '',\n _seopress_analysis_target_kw: a.keyword || '',\n};\n\nconst body = {\n title: a.titel,\n slug: s.slug,\n content: a.content_html,\n status: 'draft', // harte Vorgabe: nie automatisch veroeffentlichen\n categories: [s.kategorie_id],\n meta,\n};\nif (a.auszug) body.excerpt = a.auszug;\n\nreturn [{ json: {\n ...s,\n vorhanden: !!treffer,\n post_id: treffer ? treffer.id : null,\n alter_status: treffer ? treffer.status : null,\n wp_url: treffer\n ? `${$('Konfiguration').first().json.wp_base}/wp-json/wp/v2/posts/${treffer.id}`\n : `${$('Konfiguration').first().json.wp_base}/wp-json/wp/v2/posts`,\n wp_body: JSON.stringify(body),\n}}];\n"
"jsCode": "\nconst s = $input.first().json;\nconst dup = $('Beitrag vorhanden?').first().json;\nconst a = s.artikel;\n\n// Duplikatspruefung: exakter Slug-Treffer entscheidet ueber anlegen oder aktualisieren.\n// n8n liefert den Koerper bei fullResponse je nach Version als body oder data.\nconst K = (r) => (r && r.body !== undefined) ? r.body : ((r || {}).data);\nconst kd = K(dup);\nconst treffer = (dup.statusCode === 200 && Array.isArray(kd))\n ? kd.find(p => p.slug === s.slug) : null;\n\nconst heute = new Date().toISOString().slice(0, 10);\n\nconst meta = {\n kurzantwort: a.kurzantwort || '',\n cta_ziel: a.cta_ziel || '',\n stand_datum: a.stand_datum || heute,\n faq: Array.isArray(a.faq)\n ? a.faq.filter(f => f && f.frage).map(f => ({ frage: String(f.frage), antwort: String(f.antwort || '') }))\n : [],\n _seopress_titles_title: a.seo_titel || a.titel,\n _seopress_titles_desc: a.seo_beschreibung || '',\n _seopress_analysis_target_kw: a.keyword || '',\n};\n\nconst body = {\n title: a.titel,\n slug: s.slug,\n content: a.content_html,\n status: 'draft', // harte Vorgabe: nie automatisch veroeffentlichen\n categories: [s.kategorie_id],\n meta,\n};\nif (a.auszug) body.excerpt = a.auszug;\n\nreturn [{ json: {\n ...s,\n vorhanden: !!treffer,\n post_id: treffer ? treffer.id : null,\n alter_status: treffer ? treffer.status : null,\n wp_url: treffer\n ? `${$('Konfiguration').first().json.wp_base}/wp-json/wp/v2/posts/${treffer.id}`\n : `${$('Konfiguration').first().json.wp_base}/wp-json/wp/v2/posts`,\n wp_body: JSON.stringify(body),\n}}];\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
@@ -797,7 +797,7 @@
3760,
240
],
"id": "13917c1b-ac7f-4c89-9758-1dd677a4607b",
"id": "c3163328-1964-44eb-ae44-1d32e6bad6e9",
"name": "WordPress-Body bauen"
},
{
@@ -827,7 +827,7 @@
3980,
240
],
"id": "5ced40d2-3597-4511-b744-f26b97a3631e",
"id": "5bd4153d-b67b-4c71-88d1-bbd365c0409c",
"name": "Beitrag anlegen oder aktualisieren",
"credentials": {
"httpBasicAuth": {
@@ -842,7 +842,7 @@
{
"parameters": {
"mode": "runOnceForAllItems",
"jsCode": "\nconst v = $('WordPress-Body bauen').first().json;\nconst p = $input.first().json;\n\nif (!p.id) {\n throw new Error('WordPress lieferte keine Beitrags-ID zurueck. Antwort: '\n + JSON.stringify(p).slice(0, 400));\n}\nif (p.status !== 'draft') {\n throw new Error('Beitrag ' + p.id + ' hat Status \"' + p.status\n + '\" statt \"draft\". Der Workflow darf nie veroeffentlichen. Bitte pruefen.');\n}\n\n// Gegenprobe: sind die Meta-Felder wirklich angekommen?\nconst m = p.meta || {};\nconst fehlend = ['kurzantwort', 'cta_ziel', 'stand_datum', 'faq']\n .filter(k => m[k] === undefined);\nconst faq_ok = Array.isArray(m.faq) ? m.faq.length : -1;\n\nconst konf = $('Konfiguration').first().json;\nreturn [{ json: {\n slug: v.slug, ergebnis: 'importiert', score: v.score, score_ziel: v.score_ziel,\n post_id: p.id, aktion: v.vorhanden ? 'aktualisiert' : 'neu angelegt',\n alter_status: v.alter_status, status: p.status,\n kategorie: v.kategorie_name, kategorie_id: v.kategorie_id,\n vorschau_url: `${konf.wp_base}/?p=${p.id}&preview=true`,\n bearbeiten_url: `${konf.wp_base}/wp-admin/post.php?post=${p.id}&action=edit`,\n meta_warnung: fehlend.length\n ? 'Diese Meta-Felder kamen nicht zurueck: ' + fehlend.join(', ')\n + '. Mu-Plugin skrift-ratgeber-felder.php installiert?'\n : null,\n faq_eintraege: faq_ok,\n seo_titel: (p.meta || {})._seopress_titles_title || null,\n}}];\n"
"jsCode": "\nconst v = $('WordPress-Body bauen').first().json;\nconst p = $input.first().json;\n\nif (!p.id) {\n throw new Error('WordPress lieferte keine Beitrags-ID zurueck. Antwort: '\n + JSON.stringify(p).slice(0, 400));\n}\nif (p.status !== 'draft') {\n throw new Error('Beitrag ' + p.id + ' hat Status \"' + p.status\n + '\" statt \"draft\". Der Workflow darf nie veroeffentlichen. Bitte pruefen.');\n}\n\n// Gegenprobe: sind die Meta-Felder wirklich angekommen?\nconst m = p.meta || {};\nconst fehlend = ['kurzantwort', 'cta_ziel', 'stand_datum', 'faq']\n .filter(k => m[k] === undefined);\nconst faq_ok = Array.isArray(m.faq) ? m.faq.length : -1;\n\nconst konf = $('Konfiguration').first().json;\nreturn [{ json: {\n slug: v.slug, ergebnis: 'importiert', score: v.score, score_ziel: v.score_ziel,\n post_id: p.id, aktion: v.vorhanden ? 'aktualisiert' : 'neu angelegt',\n alter_status: v.alter_status, status: p.status,\n kategorie: v.kategorie_name, kategorie_id: v.kategorie_id,\n vorschau_url: `${konf.wp_base}/?p=${p.id}&preview=true`,\n bearbeiten_url: `${konf.wp_base}/wp-admin/post.php?post=${p.id}&action=edit`,\n meta_warnung: fehlend.length\n ? 'Diese Meta-Felder kamen nicht zurueck: ' + fehlend.join(', ')\n + '. Mu-Plugin skrift-ratgeber-felder.php installiert?'\n : null,\n faq_eintraege: faq_ok,\n seo_titel: (p.meta || {})._seopress_titles_title || null,\n preis_warnung: v.preis_warnung || null,\n preis_shortcodes: v.preis_shortcodes || 0,\n}}];\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
@@ -850,7 +850,7 @@
4200,
240
],
"id": "a3cd590e-9170-4ff7-9b38-75a0ad5d662a",
"id": "f56e1f46-0255-4056-b8ef-b456d910b603",
"name": "Ergebnis pruefen"
},
{
@@ -881,7 +881,7 @@
4420,
240
],
"id": "6482a2c3-28f6-407a-8228-ca1fc2b56436",
"id": "ca4cbfd2-2dca-4179-9c3b-80eff70a2ee3",
"name": "Cache leeren",
"credentials": {
"httpBasicAuth": {
@@ -901,7 +901,7 @@
4640,
240
],
"id": "0976c724-324f-4969-9e37-bae30d40d852",
"id": "05502f6a-0303-40c2-9292-f363102b6ae6",
"name": "Importergebnis merken"
},
{
@@ -915,9 +915,9 @@
4860,
420
],
"id": "b643ef48-5c52-4524-96bf-d3d794fdcceb",
"id": "783cac50-2646-45fd-9884-30e3497686d1",
"name": "Pause Rate Limit",
"webhookId": "8ccb2087-3c7e-44bc-9d71-facd6a9569cb"
"webhookId": "308902ac-3e3c-429a-977e-bac53612e3a7"
},
{
"parameters": {
@@ -930,7 +930,7 @@
900,
80
],
"id": "d667118f-4d70-4d35-b448-522228a05542",
"id": "ea85b7b5-9ad0-44f9-a4ff-eea1d99a8a66",
"name": "Ergebnisse sammeln"
},
{
@@ -955,7 +955,7 @@
1120,
80
],
"id": "fb4d8e22-bfd5-41fe-8041-53b9ddffe489",
"id": "2e01f634-c7f4-477e-97c2-e2e460db0bb5",
"name": "Statustabelle lesen",
"credentials": {
"httpHeaderAuth": {
@@ -967,7 +967,7 @@
{
"parameters": {
"mode": "runOnceForAllItems",
"jsCode": "\nconst s = $('Ergebnisse sammeln').first().json;\nconst p = $input.first().json;\nconst konf = $('Konfiguration').first().json;\n\nlet bestand = [];\nlet sha = null;\nif (p.statusCode === 200 && p.body && p.body.content) {\n sha = p.body.sha;\n try { bestand = JSON.parse(Buffer.from(p.body.content, 'base64').toString('utf8')); }\n catch (e) { bestand = []; }\n if (!Array.isArray(bestand)) bestand = [];\n}\n\n// Je Slug nur die juengste Zeile behalten.\nconst neu = [...bestand];\ns.neue_zeilen.forEach(z => {\n const i = neu.findIndex(x => x.slug === z.slug);\n if (i >= 0) neu[i] = z; else neu.push(z);\n});\n\nreturn [{ json: { ueberspringen: s.neue_zeilen.length === 0, commit_body: JSON.stringify({\n content: Buffer.from(JSON.stringify(neu, null, 2), 'utf8').toString('base64'),\n message: `Import: ${s.anzahl_importiert} Beitrag/Beitraege als Entwurf`,\n branch: konf.branch, ...(sha ? { sha } : {}),\n}), methode: sha ? 'PUT' : 'POST' }}];\n"
"jsCode": "\nconst s = $('Ergebnisse sammeln').first().json;\nconst p = $input.first().json;\nconst konf = $('Konfiguration').first().json;\n\nlet bestand = [];\nlet sha = null;\n// n8n liefert den Koerper bei fullResponse je nach Version als body oder data.\nconst K = (r) => (r && r.body !== undefined) ? r.body : ((r || {}).data);\nconst kp = K(p);\nif (p.statusCode === 200 && kp && kp.content) {\n sha = kp.sha;\n try { bestand = JSON.parse(Buffer.from(kp.content, 'base64').toString('utf8')); }\n catch (e) { bestand = []; }\n if (!Array.isArray(bestand)) bestand = [];\n}\n\n// Je Slug nur die juengste Zeile behalten.\nconst neu = [...bestand];\ns.neue_zeilen.forEach(z => {\n const i = neu.findIndex(x => x.slug === z.slug);\n if (i >= 0) neu[i] = z; else neu.push(z);\n});\n\nreturn [{ json: { ueberspringen: s.neue_zeilen.length === 0, commit_body: JSON.stringify({\n content: Buffer.from(JSON.stringify(neu, null, 2), 'utf8').toString('base64'),\n message: `Import: ${s.anzahl_importiert} Beitrag/Beitraege als Entwurf`,\n branch: konf.branch, ...(sha ? { sha } : {}),\n}), methode: sha ? 'PUT' : 'POST' }}];\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
@@ -975,7 +975,7 @@
1340,
80
],
"id": "594685bf-86df-4d55-bd5f-beb7e1496d08",
"id": "d803d2e6-9508-40cc-b039-14caee2fe57a",
"name": "Statustabelle fortschreiben"
},
{
@@ -989,7 +989,7 @@
},
"conditions": [
{
"id": "b1e74a2a-5318-4b76-a2c4-60d4862daf23",
"id": "374ba8a9-06b2-4606-b926-690758c243c2",
"leftValue": "={{ $json.ueberspringen }}",
"rightValue": "",
"operator": {
@@ -1010,7 +1010,7 @@
1560,
80
],
"id": "5eb207fb-6109-47ff-9bad-0f4d66cb9b10",
"id": "19504889-db25-4c59-bdf5-e9867c76372c",
"name": "Gab es Importe?"
},
{
@@ -1040,7 +1040,7 @@
1780,
160
],
"id": "ba55974d-bc96-498e-aab2-129eff4f76eb",
"id": "35dd3192-8726-431a-a08d-c8248085604d",
"name": "Statustabelle schreiben",
"credentials": {
"httpHeaderAuth": {
@@ -1064,14 +1064,14 @@
1780,
340
],
"id": "d88cb84c-6b1b-4cef-ade5-8a4462928248",
"id": "1343e9d1-c1d8-4983-92c7-825b204601b4",
"name": "Fehler Statustabelle",
"onError": "continueRegularOutput"
},
{
"parameters": {
"mode": "runOnceForAllItems",
"jsCode": "\nconst s = $('Ergebnisse sammeln').first().json;\nconst z = [];\nz.push(`Importiert: ${s.anzahl_importiert} | Nachbessern: ${s.anzahl_nachbessern} | Fehler: ${s.anzahl_fehler}`);\nz.push('');\n\nif (s.importiert.length) {\n z.push('Als Entwurf in WordPress:');\n s.importiert.forEach(r => {\n z.push(` ${r.slug} (Score ${r.score}, ${r.aktion})`);\n z.push(` Vorschau: ${r.vorschau_url}`);\n z.push(` Bearbeiten: ${r.bearbeiten_url}`);\n if (r.meta_warnung) z.push(` ACHTUNG: ${r.meta_warnung}`);\n if (r.cache_hinweis) z.push(` Hinweis: ${r.cache_hinweis}`);\n });\n z.push('');\n}\nif (s.nachbessern.length) {\n z.push('Score zu niedrig, Nachbesserung liegt im Repo:');\n s.nachbessern.forEach(r => {\n z.push(` ${r.slug}: Score ${r.score} von ${r.score_ziel}`);\n z.push(` ${r.anzahl_pflicht} Pflichtbegriffe fehlen, ${r.anzahl_erweitert} Zusatzbegriffe`);\n if (r.wortzahl && r.wortzahl.differenz !== null) {\n z.push(` Wortzahl ${r.wortzahl.ist} von ${r.wortzahl.ziel} (${r.wortzahl.differenz > 0 ? '+' : ''}${r.wortzahl.differenz})`);\n }\n z.push(` Datei: ${r.pfad}`);\n });\n z.push('');\n}\nif (s.fehler.length) {\n z.push('Fehler:');\n s.fehler.forEach(r => z.push(` ${r.slug || '?'} [${r.stufe}]: ${r.meldung}`));\n z.push('');\n}\nz.push('Kein Beitrag wurde veroeffentlicht. Alle Importe stehen auf Entwurf.');\n\nreturn [{ json: {\n betreff: `Skrift Ratgeber: ${s.anzahl_importiert} Entwurf/Entwuerfe`\n + (s.anzahl_nachbessern ? `, ${s.anzahl_nachbessern} zum Nachbessern` : '')\n + (s.anzahl_fehler ? `, ${s.anzahl_fehler} Fehler` : ''),\n text: z.join('\\n') }}];\n"
"jsCode": "\nconst s = $('Ergebnisse sammeln').first().json;\nconst z = [];\nz.push(`Importiert: ${s.anzahl_importiert} | Nachbessern: ${s.anzahl_nachbessern} | Fehler: ${s.anzahl_fehler}`);\nz.push('');\n\nif (s.importiert.length) {\n z.push('Als Entwurf in WordPress:');\n s.importiert.forEach(r => {\n z.push(` ${r.slug} (Score ${r.score}, ${r.aktion})`);\n z.push(` Vorschau: ${r.vorschau_url}`);\n z.push(` Bearbeiten: ${r.bearbeiten_url}`);\n if (r.meta_warnung) z.push(` ACHTUNG: ${r.meta_warnung}`);\n if (r.preis_warnung) z.push(` PREISE: ${r.preis_warnung}`);\n if (r.cache_hinweis) z.push(` Hinweis: ${r.cache_hinweis}`);\n });\n z.push('');\n}\nif (s.nachbessern.length) {\n z.push('Score zu niedrig, Nachbesserung liegt im Repo:');\n s.nachbessern.forEach(r => {\n z.push(` ${r.slug}: Score ${r.score} von ${r.score_ziel}`);\n z.push(` ${r.anzahl_pflicht} Pflichtbegriffe fehlen, ${r.anzahl_erweitert} Zusatzbegriffe`);\n if (r.wortzahl && r.wortzahl.differenz !== null) {\n z.push(` Wortzahl ${r.wortzahl.ist} von ${r.wortzahl.ziel} (${r.wortzahl.differenz > 0 ? '+' : ''}${r.wortzahl.differenz})`);\n }\n z.push(` Datei: ${r.pfad}`);\n if (r.preis_warnung) z.push(` PREISE: ${r.preis_warnung}`);\n });\n z.push('');\n}\nif (s.fehler.length) {\n z.push('Fehler:');\n s.fehler.forEach(r => z.push(` ${r.slug || '?'} [${r.stufe}]: ${r.meldung}`));\n z.push('');\n}\nz.push('Kein Beitrag wurde veroeffentlicht. Alle Importe stehen auf Entwurf.');\n\nreturn [{ json: {\n betreff: `Skrift Ratgeber: ${s.anzahl_importiert} Entwurf/Entwuerfe`\n + (s.anzahl_nachbessern ? `, ${s.anzahl_nachbessern} zum Nachbessern` : '')\n + (s.anzahl_fehler ? `, ${s.anzahl_fehler} Fehler` : ''),\n text: z.join('\\n') }}];\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
@@ -1079,7 +1079,7 @@
2000,
80
],
"id": "a462adda-f0fa-4290-a9bc-e234cb02dc55",
"id": "f500deba-3ff8-4224-b4ba-1ceea76055b9",
"name": "Meldung formulieren"
},
{
@@ -1097,7 +1097,7 @@
2220,
80
],
"id": "005be690-894e-45cb-915e-c8c80e6c3214",
"id": "4c8c5d7b-967a-4a41-8c04-b07d8efe1e1a",
"name": "Benachrichtigung",
"disabled": true,
"notes": "SMTP-Credential zuweisen, Absender eintragen, dann aktivieren."