Formulär
På Zkrinet: lägg till blocket under Byggblock i panelen — ett klick, din egen kod rörs inte och en ögonblicksbild tas först. Installationsstegen nedan gäller om du kör PocketBase själv, till exempel efter en export.
Kontakt- och leadformulär för landningssidor. Besökaren skickar utan att logga in, inlämningen sparas i databasen och mejlas till dig. Med botskydd (honungsfält + minsta ifyllnadstid alltid på, captcha valbart), rate limit per IP och skydd mot öppen redirect.
Kräver Mejl-pluginet. Testat mot PocketBase 0.40.4.
#Installation
- Installera Mejl först (och ställ in SMTP), kopiera sedan
pb_hooks/ochpb_migrations/till din PocketBase och starta om. - Skapa ett formulär i
zk_formulari adminpanelen:
| Fält | |
|---|---|
namn | Används i adressen: kontakt → /api/zk/formular/kontakt (a–z, 0–9, -, _) |
rubrik | Visas i mejlet till dig, t.ex. "Offertförfrågan" |
falt | Vilka fält formuläret har (se nedan) |
mottagare | Vart inlämningarna mejlas |
aktiv | Av = formuläret svarar 404 |
tack_url | Valfri. Dit vanliga formulär skickas efter inlämning. Måste ligga på appens adress eller i tillatna_adresser. |
captcha | ingen, friendly, turnstile eller hcaptcha |
kvitto | Skicka ett kort "tack, vi har fått ditt meddelande" till avsändaren (första fältet av typen epost) |
max_per_timme | Inlämningar per IP och timme. Tomt = 10 |
falt är en JSON-lista:
[
{ "namn": "namn", "etikett": "Namn", "typ": "text", "obligatorisk": true, "max": 100 },
{ "namn": "epost", "etikett": "E-post", "typ": "epost", "obligatorisk": true },
{ "namn": "telefon", "etikett": "Telefon", "typ": "text" },
{ "namn": "meddelande", "etikett": "Meddelande", "typ": "lang", "obligatorisk": true, "max": 2000 }
]
Typer: text (en rad), lang (flera rader), epost. max är antal tecken (standard 200 / 5000 / 254, högst 20 000). Högst 30 fält. Fältnamn får inte börja med zk_. Schemat kontrolleras när du sparar formuläret. Fält som inte står i schemat sparas aldrig, oavsett vad som skickas.
- Inställningar:
zk_formular_installningarhar redan en rad. Där finns captcha-nycklarna (se nedan),tillatna_adresser(kommaseparerade adresser för din landningssida om den ligger på en annan domän än PocketBase, t.ex.https://min-sajt.se, https://www.min-sajt.se),minsta_sekunder(standard 3) ochhemlighet(skapas automatiskt — rör den inte).
- Ligger PocketBase bakom en proxy (Cloudflare, nginx, Caddy)? Ställ in Settings → Trusted proxy så att PocketBase ser besökarens riktiga IP. Annars delar alla besökare proxyns IP — och därmed samma gräns per timme.
#Användning
Varje formulär behöver en färsk starttid (zk_start) från servern. Den bevisar hur länge formuläret varit öppet; robotar som skickar direkt nekas.
#Med JavaScript (fetch)
const PB = "https://din-pb.se"
const f = await fetch(`${PB}/api/zk/formular/kontakt`).then(r => r.json())
// f = { namn, rubrik, falt: [...], captcha: "friendly", sitekey: "…", start: "1762…", honungsfalt: "zk_webbplats" }
const r = await fetch(`${PB}/api/zk/formular/kontakt`, {
method: "POST", headers: { "Content-Type": "application/json" },
body: JSON.stringify({ namn: "Anna", epost: "anna@exempel.se", meddelande: "Hej!",
zk_start: f.start, zk_webbplats: "",
"frc-captcha-response": tokenFranWidgeten }),
})
// 200 { ok: true } · 400 { fel, falt: { epost: "ogiltig e-postadress" } } · 404 · 429 · 503
#Vanligt HTML-formulär
<form id="kontakt" method="post" action="https://din-pb.se/api/zk/formular/kontakt">
<input name="namn" required>
<input name="epost" type="email" required>
<textarea name="meddelande" required></textarea>
<!-- Honungsfält: ska vara tomt. Göm med CSS, INTE type="hidden" (det hoppar robotar över). -->
<div style="position:absolute;left:-9999px" aria-hidden="true">
<label>Lämna tomt <input name="zk_webbplats" tabindex="-1" autocomplete="off"></label>
</div>
<input type="hidden" name="zk_start">
<input type="hidden" name="zk_tack" value="https://min-sajt.se/tack"> <!-- valfri, måste vara vitlistad -->
<button>Skicka</button>
</form>
<script>
fetch("https://din-pb.se/api/zk/formular/kontakt").then(r => r.json())
.then(f => { document.querySelector("#kontakt [name=zk_start]").value = f.start })
</script>
Efter inlämning skickas besökaren (303) till zk_tack eller formulärets tack_url. Finns ingen visas en enkel tack-sida. Fel visas som en enkel felsida.
Både application/x-www-form-urlencoded och multipart/form-data fungerar. Värden sparas exakt som de skrevs (0701… behåller nollan).
#Captcha
Välj per formulär. Nyckeln sparas i ett dolt fält i zk_formular_installningar eller som miljövariabel (miljön vinner):
| Leverantör | Inställning | Miljövariabel | Widgetens fält |
|---|---|---|---|
| Friendly Captcha (Tyskland, EU — förstahandsval) | friendly_api_nyckel (dold), friendly_sitekey, friendly_eu | ZK_FORMULAR_FRIENDLY_NYCKEL | frc-captcha-response |
| Cloudflare Turnstile | turnstile_hemlig (dold) | ZK_FORMULAR_TURNSTILE_HEMLIG | cf-turnstile-response |
| hCaptcha | hcaptcha_hemlig (dold), hcaptcha_sitekey | ZK_FORMULAR_HCAPTCHA_HEMLIG | h-captcha-response |
Widgeten lägger själv in sitt fält i formuläret. Med fetch kan du också skicka token som zk_captcha.
- Friendly Captcha: pluginet använder v2-API:t (
/api/v2/captcha/siteverifymedX-API-Key).friendly_eu= använd EU-endpointeneu.frcapi.com— kräver att ditt Friendly-konto har EU-endpointen (en betalplan hos dem). Annarsglobal.frcapi.com. - Fail closed: svarar leverantören inte inom 10 sekunder, svarar med fel, eller saknas nyckeln, nekas inlämningen (503 "försök igen") — den släpps aldrig igenom okontrollerad. Det betyder att ett avbrott hos leverantören stoppar ditt formulär. (Friendly Captcha rekommenderar själva motsatsen för att inte tappa leads; vi valde säkerheten. Vill du ändra det, ändra i
verifieraCaptcha.) - Besökarens IP skickas inte till leverantören.
#Säkerhet
- Honungsfält (
zk_webbplats) och minsta ifyllnadstid är alltid på. Fastnar en robot i dem får den ett vanligt "tack" (200) men ingenting sparas eller mejlas — så att den inte lär sig vad som avslöjade den. Starttiden är signerad med serverns hemlighet och bunden till formuläret; en förfalskad eller saknadzk_startger 400 (det är oftast ett integrationsfel, och det ska synas), en äldre än 24 timmar ger "ladda om sidan". - Rate limit per IP, formulär och timme. Alla försök räknas, även robotarnas.
- IP-adresser sparas aldrig. Räknaren lagrar bara en hash av hemlighet + formulär + IP + aktuell timme, och raderas efter en till två timmar. Eftersom timmen ingår går det inte ens med databasen i handen att följa en IP mellan timmar. Observera att IP ändå behandlas (en IP-adress är en personuppgift) — nämn det i din integritetspolicy.
- Mejlinjektion: enradiga fält får inga radbrytningar (de blir mellanslag), e-postfält måste vara en enda vanlig adress (inga kommatecken,
<>eller radbrytningar). Mottagaren kommer alltid från formulärdefinitionen. Mejl-pluginet HTML-skyddar alla värden. - Kvittot innehåller inte vad avsändaren skrev — annars kan vem som helst använda ditt formulär för att skicka valfri text till valfri adress, med din avsändare.
- Öppen redirect:
zk_tackochtack_urlmåste ligga på appens egen adress (Settings → Application URL) eller itillatna_adresser. En otillåtenzk_tacknekar hela inlämningen (400). - Formulär, inlämningar, inställningar och räknare är bara läsbara för superusers.
GET /api/zk/formular/{namn}visar bara fält, captcha-typ och publik sitekey — aldrig mottagaren eller nycklar.
#Begränsningar
- Mejlet till dig har
Reply-Tosatt till avsändarens adress (om formuläret har ett e-postfält), så du kan svara direkt. Adressen är validerad som en enda adress. - En mottagare per formulär.
- Inga filuppladdningar.
- E-postadresser med å/ä/ö i adressen (internationaliserade adresser) godtas inte; domäner skrivna i punycode fungerar.
- Starttiden är inte engångs: en robot som hämtar den och väntar de tre sekunderna kommer förbi tidskontrollen. Det är rate limit och captcha som tar de robotarna.
- Mejl skickas direkt, utan kö. Är SMTP nere sparas inlämningen ändå (
mejl = felizk_formular_svar), men mejlet försöks inte igen. - Turnstiles
hostname/actionoch Friendly Captchassitekeyi svaret kontrolleras inte — vi litar på leverantörenssuccess.
#Testa
./bank.py formular
21 tester. Captcha testas mot en låtsas-leverantör (ZK_FORMULAR_FRIENDLY_URL, ZK_FORMULAR_TURNSTILE_URL, ZK_FORMULAR_HCAPTCHA_URL pekar om verify-adressen; ZK_FORMULAR_CAPTCHA_TIMEOUT sänker timeouten) — inget når internet. Godkänd, underkänd, timeout och serverfel testas för Friendly Captcha och Turnstile, godkänd/underkänd för hCaptcha. Mot de riktiga leverantörerna är pluginet inte provkört. Säkerhetstesterna är mutantverifierade: varje skydd har brutits med flit och fångats av ett test.