Documentation
¶
Overview ¶
Package forms captures HTML form posts for static sites: parsing, spam classification, storage, notification and export.
Index ¶
- Constants
- Variables
- func Classify(fields map[string]string, honeypot string, maxLinks int) (clean map[string]string, spam bool, reason string)
- func HashIP(salt, ip string) string
- func NewID() (string, error)
- func NewSalt() (string, error)
- func NoticeText(f store.Form, s store.FormSubmission, dashboardURL string) string
- func ParseRequest(r *http.Request, cfg Config) (map[string]string, error)
- func SampleValues(s Schema) map[string]string
- func Snippet(endpoint string, f store.Form) string
- func SnippetFetch(endpoint string, f store.Form) string
- func SnippetMinimal(endpoint string, f store.Form) string
- func SortedKeys(fields map[string]string) []string
- func ValidateHoneypot(field string) error
- func ValidateName(name string) error
- func ValidateRedirect(raw string) error
- func ValidateSchema(s Schema, honeypot string) error
- func ValidateSubmission(s Schema, honeypot string, in map[string]string) (map[string]string, map[string]string)
- func WriteCSV(w io.Writer, subs []store.FormSubmission) error
- type Config
- type Field
- type Notifier
- type Schema
- type Service
- type Store
Constants ¶
const ( EnvMaxBodyBytes = "FORMS_MAX_BODY_BYTES" EnvMaxFields = "FORMS_MAX_FIELDS" EnvMaxFieldBytes = "FORMS_MAX_FIELD_BYTES" EnvRatePerIPPerMinute = "FORMS_RATE_PER_IP_PER_MINUTE" EnvRatePerFormPerMin = "FORMS_RATE_PER_FORM_PER_MINUTE" EnvRetentionDays = "FORMS_RETENTION_DAYS" EnvMaxStoredPerForm = "FORMS_MAX_STORED_PER_FORM" EnvMaxFormsPerApp = "FORMS_MAX_PER_APP" EnvSpamMaxLinks = "FORMS_SPAM_MAX_LINKS" )
Environment variables that bound the public endpoint.
const ( TypeText = "text" TypeEmail = "email" TypeTextarea = "textarea" TypeSelect = "select" TypeCheckbox = "checkbox" TypeNumber = "number" TypeURL = "url" TypeHidden = "hidden" )
Field input types a form can declare.
const ( SpamHoneypot = "honeypot" SpamLinks = "too_many_links" SpamEmpty = "empty" SpamManual = "manual" )
Spam reasons stored with a flagged submission.
const DefaultHoneypot = "bot-field"
DefaultHoneypot is the hidden field name used when a form sets none.
Variables ¶
var ( ErrTooLarge = errors.New("forms: body too large") ErrUnsupportedType = errors.New("forms: unsupported content type") ErrTooManyFields = errors.New("forms: too many fields") ErrFieldTooLong = errors.New("forms: field too long") ErrEmpty = errors.New("forms: no fields") ErrMalformed = errors.New("forms: malformed body") )
Parse failures; the API maps each to a distinct status.
Functions ¶
func Classify ¶
func Classify(fields map[string]string, honeypot string, maxLinks int) (clean map[string]string, spam bool, reason string)
Classify reports whether fields look like spam and returns them without the honeypot field. maxLinks <= 0 disables the link heuristic.
func NoticeText ¶
NoticeText is the plain-text body sent to channels. Values are flattened and truncated so a submission cannot inject markup-length payloads or mentions into a chat channel.
func ParseRequest ¶
ParseRequest reads urlencoded, multipart or JSON bodies into string fields. Multipart files are never stored; only the file name is kept as the value.
func SampleValues ¶
SampleValues builds a plausible submission for the test button.
func SnippetFetch ¶
SnippetFetch returns an HTML form plus a script that posts it as JSON without leaving the page.
func SnippetMinimal ¶
SnippetMinimal returns bare markup: inputs with placeholders, no labels or styling hooks.
func SortedKeys ¶
SortedKeys returns the field names in a stable order for display and CSV columns.
func ValidateHoneypot ¶
ValidateHoneypot accepts an empty value (the default) or a plain field name.
func ValidateName ¶
ValidateName accepts lowercase letters, digits, dash and underscore, up to 63 characters.
func ValidateRedirect ¶
ValidateRedirect accepts an empty value or an absolute http(s) URL without credentials.
func ValidateSchema ¶
ValidateSchema checks the design itself, not a submission.
func ValidateSubmission ¶
func ValidateSubmission(s Schema, honeypot string, in map[string]string) (map[string]string, map[string]string)
ValidateSubmission checks fields against the schema. It returns the cleaned map (declared names plus the honeypot key, unknown names dropped) and a message per invalid field. A schema with no fields accepts everything unchanged.
Types ¶
type Config ¶
type Config struct {
MaxBodyBytes int64
MaxFields int
MaxFieldBytes int
RatePerIPPerMinute int
RatePerFormPerMin int
Retention time.Duration
MaxStoredPerForm int
MaxFormsPerApp int
SpamMaxLinks int
}
Config holds the limits read from the environment; zero disables a limit.
type Field ¶
type Field struct {
Name string `json:"name"`
Type string `json:"type"`
Label string `json:"label,omitempty"`
Required bool `json:"required,omitempty"`
Placeholder string `json:"placeholder,omitempty"`
MinLength int `json:"min_length,omitempty"`
MaxLength int `json:"max_length,omitempty"`
Options []string `json:"options,omitempty"`
// Default is the value a hidden field carries in the snippet.
Default string `json:"default,omitempty"`
}
Field is one input of a form.
func DefaultFields ¶
func DefaultFields() []Field
DefaultFields is the layout used by forms that never set a schema.
type Schema ¶
type Schema struct {
Fields []Field `json:"fields"`
SuccessMessage string `json:"success_message,omitempty"`
AllowedOrigins []string `json:"allowed_origins,omitempty"`
}
Schema is a form's editable design. No fields means the default three (DefaultFields). With fields set, submitted names that are not declared are dropped, not stored.
func ParseSchema ¶
ParseSchema decodes the stored JSON; empty or "{}" yields the zero Schema.
func (Schema) EffectiveFields ¶
EffectiveFields returns the declared fields, or the default three when none are set.
type Service ¶
type Service struct {
// Dispatch runs notification work; tests replace it to run inline.
Dispatch func(func())
Now func() time.Time
// contains filtered or unexported fields
}
Service stores submissions and fires notifications.
func NewService ¶
NewService builds a Service; notifier may be nil to disable notifications.
func (*Service) Submit ¶
func (s *Service) Submit(ctx context.Context, f store.Form, fields map[string]string, clientIP string) (store.FormSubmission, error)
Submit classifies and stores one post. A failed notification never fails the submission: the row is already saved, so it is logged and the visitor still gets success.