add pocketbase

This commit is contained in:
2025-05-29 11:48:39 +02:00
parent 7ed70d58d2
commit 5f8f8b3dd3
25 changed files with 1484 additions and 48 deletions

View File

@ -0,0 +1,82 @@
export class TurnStilleCaptcha {
sitekey = "{{.SiteKey}}";
/**
* Initializes the TurnStilleCaptcha instance.
* Creates a container for the captcha and appends it to the target element.
* If the Cloudflare Turnstile script is already loaded, it runs the captcha.
* Otherwise, it loads the script and initializes the captcha with the given properties.
* @param {HTMLElement} target - The element to attach the captcha container to.
* @param {Object} [props={}] - Optional properties for captcha initialization, such as theme.
*/
constructor(target, props = {}) {
// create holder
this.holder = document.createElement("div");
this.holder.id = "turnstile-container";
this.theme = props.theme || "auto";
target.appendChild(this.holder);
// execute code
if (window.turnstile) {
this.runCaptcha();
} else {
this.loadCloudflareScript();
}
}
runCaptcha() {
setTimeout(() => {
if (globalThis.turnstileInstance) {
window.turnstile.remove(globalThis.turnstileInstance);
}
globalThis.turnstileInstance = window.turnstile.render(this.holder, {
sitekey: this.sitekey,
theme: this.theme,
callback: (token) => {
if (token) {
const event = new CustomEvent("token", {
detail: token,
bubbles: true,
});
this.holder.dispatchEvent(event);
}
},
error: (error) => {
const event = new CustomEvent("failure", {
detail: error,
bubbles: true,
});
this.holder.dispatchEvent(event);
window.turnstile.reset(globalThis.turnstileInstance);
},
});
}, 1000);
}
loadCloudflareScript() {
const script = document.createElement("script");
script.id = "turnstile-script";
script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js";
// script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
script.async = true;
script.defer = true;
script.onload = () => {
const event = new CustomEvent("loaded", {
detail: "Turnstile script loaded",
bubbles: true,
});
this.holder.dispatchEvent(event);
this.runCaptcha();
};
script.onerror = () => {
const event = new CustomEvent("failure", {
detail: "Failed to load Turnstile script",
bubbles: true,
});
this.holder.dispatchEvent(event);
};
document.head.appendChild(script);
}
}

View File

@ -0,0 +1,95 @@
package cloudflare
import (
"bytes"
"encoding/json"
"errors"
"html/template"
"io"
"net/http"
"strings"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/core"
"github.com/pocketbase/pocketbase/tools/router"
)
var VerifyUrl = "https://challenges.cloudflare.com/turnstile/v0/siteverify"
type TurnstileResponse struct {
Success bool `json:"success"`
ErrorCodes []string `json:"error-codes,omitempty"`
ChallengeTS string `json:"challenge_ts,omitempty"`
Hostname string `json:"hostname,omitempty"`
}
type TurnStilleCaptcha struct {
SiteKey string
SecretKey string
}
func ServeTurnstilleCaptchaJS(app *pocketbase.PocketBase, se *core.ServeEvent) *router.Route[*core.RequestEvent] {
return se.Router.GET("/api/email/script.js", func(e *core.RequestEvent) error {
// www.abrasive.ma-al.pl
// siteKey: "0x4AAAAAABdgeAdu4Pxxovj3"
// secretKey: "0x4AAAAAABdgeHJDjMwmeX5aXaXGh6HWZbw"
settings, err := GetSettings(app)
if err != nil {
return err
}
file, err := JS.ReadFile("TurnStilleCaptcha.js")
if err != nil {
return err
}
templ, err := template.New("test").Parse(string(file))
buf := bytes.Buffer{}
templ.Execute(&buf, map[string]interface{}{
"SiteKey": settings.SiteKey,
})
if err != nil {
return err
}
e.Response.Header().Set("Content-Type", "application/javascript")
return e.String(http.StatusOK, buf.String())
})
}
func GetSettings(app *pocketbase.PocketBase) (*TurnStilleCaptcha, error) {
record, err := app.FindFirstRecordByFilter("settings", "key='turnstile'", nil)
settings := TurnStilleCaptcha{}
json.Unmarshal([]byte(record.GetString("value")), &settings)
if err != nil {
return nil, err
}
return &settings, nil
}
func VerifyTurnstile(app *pocketbase.PocketBase, token, ip string) error {
conf, err := GetSettings(app)
if err != nil {
return err
}
data := map[string]string{"secret": conf.SecretKey, "response": token, "remoteip": ip}
jsonData, _ := json.Marshal(data)
resp, err := http.Post(VerifyUrl, "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return err
}
defer resp.Body.Close()
var turnstileResp TurnstileResponse
body, _ := io.ReadAll(resp.Body)
json.Unmarshal(body, &turnstileResp)
if !turnstileResp.Success {
return errors.New(turnstileResp.ChallengeTS + ": " + strings.Join(turnstileResp.ErrorCodes, " "))
}
return nil
}

View File

@ -0,0 +1,8 @@
package cloudflare
import (
"embed"
)
//go:embed TurnStilleCaptcha.js
var JS embed.FS