{
  "name": "Kontaktformular (alpin-code)",
  "nodes": [
    {
      "id": "kontaktformular",
      "name": "Kontaktformular",
      "type": "n8n-nodes-base.formTrigger",
      "typeVersion": 2.2,
      "position": [
        0,
        0
      ],
      "parameters": {
        "formTitle": "Ihre Anfrage",
        "formDescription": "Schreiben Sie uns kurz, worum es geht. Wir melden uns persönlich.",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Name",
              "requiredField": true
            },
            {
              "fieldLabel": "E-Mail",
              "fieldType": "email",
              "requiredField": true
            },
            {
              "fieldLabel": "Telefon (optional)"
            },
            {
              "fieldLabel": "Ihr Anliegen",
              "fieldType": "textarea",
              "requiredField": true
            },
            {
              "fieldLabel": "Datenschutz",
              "fieldType": "checkbox",
              "requiredField": true,
              "fieldOptions": {
                "values": [
                  {
                    "option": "Ich bin einverstanden, dass meine Angaben zur Bearbeitung meiner Anfrage gespeichert werden."
                  }
                ]
              }
            },
            {
              "fieldType": "hiddenField",
              "fieldName": "website"
            }
          ]
        },
        "options": {
          "appendAttribution": false,
          "buttonLabel": "Anfrage senden",
          "respondWithOptions": {
            "values": {
              "respondWith": "text",
              "formSubmittedText": "Danke, Ihre Anfrage ist angekommen. Wir melden uns persönlich bei Ihnen."
            }
          }
        }
      },
      "webhookId": "kontaktformular"
    },
    {
      "id": "anfrage-pr-fen",
      "name": "Anfrage prüfen",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        220,
        0
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// Prüft die Anfrage. Spam oder ungültige Eingaben: kein Weiterlauf, keine Mail.\nconst f = $input.first().json;\nconst text = (v, max) => String(v ?? '').trim().slice(0, max);\nconst name = text(f['Name'], 120);\nconst email = text(f['E-Mail'], 200).toLowerCase();\nconst telefon = text(f['Telefon (optional)'], 40);\nconst anliegen = text(f['Ihr Anliegen'], 4000);\nconst einwilligung = Array.isArray(f['Datenschutz']) ? f['Datenschutz'].length > 0 : Boolean(f['Datenschutz']);\nconst fangfeld = text(f['website'], 200);\nconst gruende = [];\nif (fangfeld) gruende.push('Fangfeld ausgefüllt (Bot)');\nif (!name) gruende.push('Name fehlt');\n// Strenge Form: keine Kommas, Klammern, Anführungszeichen, sonst wären mehrere Empfänger einschleusbar\nif (!/^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,}$/.test(email)) gruende.push('E-Mail ungültig');\nif (anliegen.length < 5) gruende.push('Anliegen zu kurz');\nif (!einwilligung) gruende.push('keine Einwilligung');\nif (gruende.length) return [{ json: { spam: true, gruende } }];\n// Bestätigung drosseln: je Adresse 1 pro Tag, insgesamt 20 pro Stunde und 100 pro Tag (gegen Missbrauch als Mail-Schleuder)\n// shortcut: Workflow-Daten sind nicht atomar, gleichzeitige Läufe können die Grenze knapp überschreiten; bei Bot-Last Drossel je IP am Reverse-Proxy\nconst merker = $getWorkflowStaticData('global'); const jetzt = Date.now();\nconst [lokal, domain] = email.split('@');\nconst schluessel = (domain === 'gmail.com' || domain === 'googlemail.com' ? lokal.split('+')[0].replace(/\\./g, '') + '@gmail.com' : lokal.split('+')[0] + '@' + domain);\nmerker.adressen = Object.fromEntries(Object.entries(merker.adressen || {}).filter(([, t]) => jetzt - t < 864e5));\nmerker.tag = (merker.tag || []).filter(t => jetzt - t < 864e5);\nconst stunde = merker.tag.filter(t => jetzt - t < 36e5).length;\nconst bestaetigen = !merker.adressen[schluessel] && stunde < 20 && merker.tag.length < 100;\nif (bestaetigen) { merker.adressen[schluessel] = jetzt; merker.tag.push(jetzt); }\nreturn [{ json: { spam: false, bestaetigen, name, email, telefon, anliegen, einwilligung: true, eingegangen_am: new Date().toISOString() } }];"
      }
    },
    {
      "id": "echte-anfrage-",
      "name": "Echte Anfrage?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        440,
        0
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "echt",
              "leftValue": "={{ $json.spam }}",
              "rightValue": false,
              "operator": {
                "type": "boolean",
                "operation": "false",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      }
    },
    {
      "id": "als-datei-vorbereiten",
      "name": "Als Datei vorbereiten",
      "type": "n8n-nodes-base.convertToFile",
      "typeVersion": 1.1,
      "position": [
        660,
        -100
      ],
      "parameters": {
        "operation": "toJson",
        "mode": "each",
        "options": {
          "fileName": "={{ $now.toFormat(\"yyyyMMdd-HHmmss\") }}.json"
        }
      }
    },
    {
      "id": "anfrage-ablegen",
      "name": "Anfrage ablegen",
      "type": "n8n-nodes-base.readWriteFile",
      "typeVersion": 1,
      "position": [
        880,
        -100
      ],
      "parameters": {
        "operation": "write",
        "fileName": "=/PFAD/ZU/IHREM/ORDNER/anfrage-{{ $now.toFormat('yyyyMMdd-HHmmss') }}.json",
        "dataPropertyName": "data",
        "options": {}
      }
    },
    {
      "id": "mail-an-den-betrieb",
      "name": "Mail an den Betrieb",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        1100,
        -100
      ],
      "parameters": {
        "fromEmail": "noreply@beispielbetrieb.de",
        "toEmail": "anfragen@beispielbetrieb.de",
        "subject": "=Neue Anfrage von {{ $(\"Anfrage prüfen\").item.json.name }}",
        "emailFormat": "text",
        "text": "=Neue Anfrage über das Kontaktformular\n\nName: {{ $(\"Anfrage prüfen\").item.json.name }}\nE-Mail: {{ $(\"Anfrage prüfen\").item.json.email }}\nTelefon: {{ $(\"Anfrage prüfen\").item.json.telefon || \"–\" }}\nEingegangen: {{ $(\"Anfrage prüfen\").item.json.eingegangen_am }}\nEinwilligung Datenschutz: ja\n\nAnliegen:\n{{ $(\"Anfrage prüfen\").item.json.anliegen }}\n\nAntworten Sie direkt auf diese Mail, sie geht an die anfragende Person.",
        "options": {
          "replyTo": "={{ $(\"Anfrage prüfen\").item.json.email }}",
          "appendAttribution": false
        }
      },
      "credentials": {
        "smtp": {
          "id": "SMTP_ID",
          "name": "SMTP Versand"
        }
      }
    },
    {
      "id": "best-tigung-erlaubt-",
      "name": "Bestätigung erlaubt?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1320,
        -100
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "bestaetigen",
              "leftValue": "={{ $(\"Anfrage prüfen\").item.json.bestaetigen }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      }
    },
    {
      "id": "best-tigung-an-den-kunden",
      "name": "Bestätigung an den Kunden",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        1540,
        -160
      ],
      "parameters": {
        "fromEmail": "noreply@beispielbetrieb.de",
        "toEmail": "={{ $(\"Anfrage prüfen\").item.json.email }}",
        "subject": "Ihre Anfrage ist angekommen",
        "emailFormat": "text",
        "text": "Guten Tag,\n\nvielen Dank, Ihre Anfrage ist bei uns angekommen. Wir melden uns persönlich bei Ihnen.\n\nDiese Nachricht wurde automatisch versendet.",
        "options": {
          "appendAttribution": false
        }
      },
      "credentials": {
        "smtp": {
          "id": "SMTP_ID",
          "name": "SMTP Versand"
        }
      }
    },
    {
      "id": "aussortiert-kein-versand-",
      "name": "Aussortiert (kein Versand)",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        660,
        120
      ],
      "parameters": {}
    }
  ],
  "connections": {
    "Kontaktformular": {
      "main": [
        [
          {
            "node": "Anfrage prüfen",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Anfrage prüfen": {
      "main": [
        [
          {
            "node": "Echte Anfrage?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Echte Anfrage?": {
      "main": [
        [
          {
            "node": "Als Datei vorbereiten",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Aussortiert (kein Versand)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Als Datei vorbereiten": {
      "main": [
        [
          {
            "node": "Anfrage ablegen",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Anfrage ablegen": {
      "main": [
        [
          {
            "node": "Mail an den Betrieb",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mail an den Betrieb": {
      "main": [
        [
          {
            "node": "Bestätigung erlaubt?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Bestätigung erlaubt?": {
      "main": [
        [
          {
            "node": "Bestätigung an den Kunden",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "saveDataSuccessExecution": "none"
  },
  "pinData": {}
}
