homepage/server/antispam/antispam.go
2026-01-13 21:31:43 +01:00

56 lines
1.7 KiB
Go

// Package antispam defines HTML templates and the corresponding server-side
// logic for integrating captcha providers.
package antispam
import (
"context"
"html/template"
"log/slog"
"net/http"
"os"
)
type Antispam interface {
// Script contains HTML `<script>` tag(s) that need to be injected into the
// form content or document head.
Script() template.HTML
// FormField is the template that needs to be injected into the `<form>`.
FormField() template.HTML
// CheckRequest uses the captcha provider to determine if request should be
// allowed to proceed.
//
// Implementations using HTTP forms may require r.ParseForm() to have been
// called.
CheckRequest(r *http.Request) (bool, error)
}
type antispamKey struct{}
// GetAntispam returns the active antispam implementation from request context.
func GetAntispam(r *http.Request) Antispam {
if antispam, ok := r.Context().Value(antispamKey{}).(Antispam); ok {
return antispam
}
return NewNoop()
}
// WithAntispam chooses a suitable antispam implementation sets up request
// context accordingly.
func WithAntispam(next http.Handler) http.HandlerFunc {
hCaptchaSiteKey := os.Getenv("HCAPTCHA_SITE_KEY")
hCaptchaSecretKey := os.Getenv("HCAPTCHA_SECRET_KEY")
if hCaptchaSiteKey != "" && hCaptchaSecretKey != "" {
slog.Info("antispam: using hCaptcha implementation")
return withAntispam(next, NewHcaptcha(hCaptchaSiteKey, hCaptchaSecretKey))
}
slog.Info("antispam: falling back to noop implementation")
return next.ServeHTTP
}
func withAntispam(next http.Handler, impl Antispam) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := context.WithValue(r.Context(), antispamKey{}, impl)
next.ServeHTTP(w, r.WithContext(ctx))
}
}