feat: initialize nuntius project with full server implementation

This commit is contained in:
2026-06-20 20:48:09 +02:00
commit 1b700f7975
33 changed files with 4313 additions and 0 deletions
+111
View File
@@ -0,0 +1,111 @@
//go:build linux || freebsd
package contactform
import (
"net/mail"
"strings"
"unicode/utf8"
)
// validServices is the allow-list for contact-type forms.
var validServices = map[string]bool{
"": true,
"architecture": true,
"ai": true,
"infrastructure": true,
"software": true,
"unix": true,
"other": true,
}
// MinNameRunes is the minimum allowed length of a name.
const MinNameRunes = 2
// MaxNameRunes is the maximum allowed length of a name.
const MaxNameRunes = 100
// MinMessageRunes is the minimum allowed length of a message body.
const MinMessageRunes = 10
// MaxMessageRunes is the maximum allowed length of a message body.
const MaxMessageRunes = 5000
// Validate dispatches to the correct type-specific validator.
// Supported types: contact, feedback, newsletter, generic. Default is "contact".
func Validate(r *Request, formType string) []FieldError {
switch formType {
case "newsletter":
return validateNewsletter(r)
case "feedback", "generic":
return validateGeneric(r)
default:
return validateContact(r)
}
}
func validateContact(r *Request) []FieldError {
r.Name = strings.TrimSpace(r.Name)
r.Email = strings.TrimSpace(r.Email)
r.Service = strings.TrimSpace(r.Service)
r.Message = strings.TrimSpace(r.Message)
var errs []FieldError
if n := utf8.RuneCountInString(r.Name); n < MinNameRunes {
errs = append(errs, FieldError{Field: "name", Message: "name must be at least 2 characters"})
} else if n > MaxNameRunes {
errs = append(errs, FieldError{Field: "name", Message: "name must be at most 100 characters"})
}
if _, err := mail.ParseAddress(r.Email); err != nil {
errs = append(errs, FieldError{Field: "email", Message: "email is invalid"})
}
if !validServices[r.Service] {
errs = append(errs, FieldError{Field: "service", Message: "service is not a recognised value"})
}
if n := utf8.RuneCountInString(r.Message); n < MinMessageRunes {
errs = append(errs, FieldError{Field: "message", Message: "message must be at least 10 characters"})
} else if n > MaxMessageRunes {
errs = append(errs, FieldError{Field: "message", Message: "message must be at most 5000 characters"})
}
return errs
}
func validateNewsletter(r *Request) []FieldError {
r.Email = strings.TrimSpace(r.Email)
if _, err := mail.ParseAddress(r.Email); err != nil {
return []FieldError{{Field: "email", Message: "email is invalid"}}
}
return nil
}
func validateGeneric(r *Request) []FieldError {
r.Name = strings.TrimSpace(r.Name)
r.Email = strings.TrimSpace(r.Email)
r.Message = strings.TrimSpace(r.Message)
var errs []FieldError
if n := utf8.RuneCountInString(r.Name); n < MinNameRunes {
errs = append(errs, FieldError{Field: "name", Message: "name must be at least 2 characters"})
} else if n > MaxNameRunes {
errs = append(errs, FieldError{Field: "name", Message: "name must be at most 100 characters"})
}
if _, err := mail.ParseAddress(r.Email); err != nil {
errs = append(errs, FieldError{Field: "email", Message: "email is invalid"})
}
if n := utf8.RuneCountInString(r.Message); n < MinMessageRunes {
errs = append(errs, FieldError{Field: "message", Message: "message must be at least 10 characters"})
} else if n > MaxMessageRunes {
errs = append(errs, FieldError{Field: "message", Message: "message must be at most 5000 characters"})
}
return errs
}