From cbbe6b99960f0f3445e911de7b9c83e4c628d508 Mon Sep 17 00:00:00 2001 From: rene Date: Sun, 17 May 2026 19:51:19 +0200 Subject: [PATCH] =?UTF-8?q?Security:=20Cloudflare=20Turnstile=20f=C3=BCr?= =?UTF-8?q?=20Kontaktformular,=20server-seitige=20Verifizierung?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 2 +- app/src/app.html | 1 + app/src/routes/+page.svelte | 24 ++++++++++----- app/src/routes/api/contact/+server.ts | 44 +++++++++++++++++++++++++++ docker-compose.yml | 1 + 5 files changed, 64 insertions(+), 8 deletions(-) create mode 100644 app/src/routes/api/contact/+server.ts diff --git a/Makefile b/Makefile index ecd70ec..9ce3d5b 100644 --- a/Makefile +++ b/Makefile @@ -64,7 +64,7 @@ deploy: check-ssh @COPYFILE_DISABLE=1 tar czf - $(TAR_EXCLUDE) . | ssh $(DS_HOST) "tar xzf - -C $(DS_PATH)/" @echo "→ .env auf DS aktualisieren..." @if [ -f .env ]; then \ - grep BREVO_KEY .env | ssh $(DS_HOST) "cat > $(DS_PATH)/.env"; \ + grep -E "BREVO_KEY|TURNSTILE_SECRET" .env | ssh $(DS_HOST) "cat > $(DS_PATH)/.env"; \ fi @echo "→ PocketBase Hooks synchronisieren..." @for f in $(HOOKS_SRC)/*.pb.js; do \ diff --git a/app/src/app.html b/app/src/app.html index 9f772c3..7f56a44 100644 --- a/app/src/app.html +++ b/app/src/app.html @@ -9,6 +9,7 @@ + diff --git a/app/src/routes/+page.svelte b/app/src/routes/+page.svelte index f53973e..9252a50 100644 --- a/app/src/routes/+page.svelte +++ b/app/src/routes/+page.svelte @@ -4,6 +4,7 @@ import { pb } from '$lib/pb'; import { onMount } from 'svelte'; import { browser } from '$app/environment'; + const PUBLIC_TURNSTILE_SITE_KEY = '0x4AAAAAADRLILtAf9XMk-g0'; const DEMO_TENANT = 'mengbzc3ajxpccz'; @@ -56,14 +57,22 @@ if (!formName.trim() || !formEmail.trim()) { formError = 'Bitte Name und E-Mail ausfüllen.'; return; } formSending = true; formError = ''; try { - await pb.collection('inquiries').create({ - name: formName.trim(), - company: formCompany.trim(), - email: formEmail.trim(), - phone: formPhone.trim(), - message: formMsg.trim(), - plan: modalPlan + const token = (window as any).turnstile?.getResponse() ?? ''; + const res = await fetch('/api/contact', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + token, name: formName.trim(), email: formEmail.trim(), + company: formCompany.trim(), phone: formPhone.trim(), + message: formMsg.trim(), plan: modalPlan + }) }); + if (!res.ok) { + const d = await res.json(); + formError = d.error ?? 'Fehler beim Senden.'; + (window as any).turnstile?.reset(); + return; + } formDone = true; formName = formCompany = formEmail = formPhone = formMsg = ''; } catch { @@ -438,6 +447,7 @@ +
{#if formError}{/if}